git-svn-id: svn://projects.jkraemer.net/acts_as_ferret/trunk/plugin/acts_as_ferret...
[acts_as_ferret.git] / lib / instance_methods.rb
blob6e85aff9b826b55c54ca972aa9a9fb42e017b89a
1 module ActsAsFerret #:nodoc:
3   module InstanceMethods
4     include ResultAttributes
6     # Returns an array of strings with the matches highlighted. The +query+ can
7     # either be a String or a Ferret::Search::Query object.
8     # 
9     # === Options
10     #
11     # field::            field to take the content from. This field has 
12     #                    to have it's content stored in the index 
13     #                    (:store => :yes in your call to aaf). If not
14     #                    given, all stored fields are searched, and the
15     #                    highlighted content found in all of them is returned.
16     #                    set :highlight => :no in the field options to
17     #                    avoid highlighting of contents from a :stored field.
18     # excerpt_length::   Default: 150. Length of excerpt to show. Highlighted
19     #                    terms will be in the centre of the excerpt.
20     # num_excerpts::     Default: 2. Number of excerpts to return.
21     # pre_tag::          Default: "<em>". Tag to place to the left of the
22     #                    match.  
23     # post_tag::         Default: "</em>". This tag should close the
24     #                    +:pre_tag+.
25     # ellipsis::         Default: "...". This is the string that is appended
26     #                    at the beginning and end of excerpts (unless the
27     #                    excerpt hits the start or end of the field. You'll
28     #                    probably want to change this to a Unicode elipsis
29     #                    character.
30     def highlight(query, options = {})
31       self.class.aaf_index.highlight(id, self.class.name, query, options)
32     end
33     
34     # re-eneable ferret indexing for this instance after a call to #disable_ferret
35     def enable_ferret  
36       @ferret_disabled = nil 
37     end
38     alias ferret_enable enable_ferret  # compatibility
39     
40     # returns true if ferret indexing is enabled for this record.
41     #
42     # The optional is_bulk_index parameter will be true if the method is called
43     # by rebuild_index or bulk_index, and false otherwise.
44     #
45     # If is_bulk_index is true, the class level ferret_enabled state will be
46     # ignored by this method (per-instance ferret_enabled checks however will 
47     # take place, so if you override this method to forbid indexing of certain 
48     # records you're still safe).
49     def ferret_enabled?(is_bulk_index = false)
50       @ferret_disabled.nil? && (is_bulk_index || self.class.ferret_enabled?)
51     end
53     # Disable Ferret for this record for a specified amount of time. ::once will 
54     # disable Ferret for the next call to #save (this is the default), ::always 
55     # will do so for all subsequent calls. 
56     #
57     # Note that this will turn off only the create and update hooks, but not the 
58     # destroy hook. I think that's reasonable, if you think the opposite, please 
59     # tell me.
60     #
61     # To manually trigger reindexing of a record after you're finished modifying 
62     # it, you can call #ferret_update directly instead of #save (remember to
63     # enable ferret again before).
64     #
65     # When given a block, this will be executed without any ferret indexing of 
66     # this object taking place. The optional argument in this case can be used 
67     # to indicate if the object should be indexed after executing the block
68     # (::index_when_finished). Automatic Ferret indexing of this object will be 
69     # turned on after the block has been executed. If passed ::index_when_true, 
70     # the index will only be updated if the block evaluated not to false or nil.
71     #
72     def disable_ferret(option = :once)
73       if block_given?
74         @ferret_disabled = :always
75         result = yield
76         ferret_enable
77         ferret_update if option == :index_when_finished || (option == :index_when_true && result)
78         result
79       elsif [:once, :always].include?(option)
80         @ferret_disabled = option
81       else
82         raise ArgumentError.new("Invalid Argument #{option}")
83       end
84     end
86     # add to index
87     def ferret_create
88       if ferret_enabled?
89         logger.debug "ferret_create/update: #{self.class.name} : #{self.id}"
90         self.class.aaf_index << self
91       else
92         ferret_enable if @ferret_disabled == :once
93       end
94       true # signal success to AR
95     end
96     alias :ferret_update :ferret_create
97     
99     # remove from index
100     def ferret_destroy
101       logger.debug "ferret_destroy: #{self.class.name} : #{self.id}"
102       begin
103         self.class.aaf_index.remove self.id, self.class.name
104       rescue
105         logger.warn("Could not find indexed value for this object: #{$!}\n#{$!.backtrace}")
106       end
107       true # signal success to AR
108     end
109     
110     # turn this instance into a ferret document (which basically is a hash of
111     # fieldname => value pairs)
112     def to_doc
113       logger.debug "creating doc for class: #{self.class.name}, id: #{self.id}"
114       returning Ferret::Document.new do |doc|
115         # store the id of each item
116         doc[:id] = self.id
118         # store the class name if configured to do so
119         doc[:class_name] = self.class.name if aaf_configuration[:store_class_name]
120       
121         # iterate through the fields and add them to the document
122         aaf_configuration[:ferret_fields].each_pair do |field, config|
123           doc[field] = self.send("#{field}_to_ferret") unless config[:ignore]
124         end
125         if aaf_configuration[:boost]
126           if self.respond_to?(aaf_configuration[:boost])
127             boost = self.send aaf_configuration[:boost]
128             doc.boost = boost.to_i if boost
129           else
130             logger.error "boost option should point to an instance method: #{aaf_configuration[:boost]}"
131           end
132         end
133       end
134     end
136     def document_number
137       self.class.aaf_index.document_number(id, self.class.name)
138     end
140     def query_for_record
141       self.class.aaf_index.query_for_record(id, self.class.name)
142     end
144     def content_for_field_name(field, dynamic_boost = nil)
145       field_data = self[field] || self.instance_variable_get("@#{field.to_s}".to_sym) || self.send(field.to_sym)
146       if (dynamic_boost && boost_value = self.send(dynamic_boost))
147         field_data = Ferret::Field.new(field_data)
148         field_data.boost = boost_value.to_i
149       end
150       field_data
151     end
154   end