lib/fuzzily/searchable.rb in fuzzily-0.2.4 vs lib/fuzzily/searchable.rb in fuzzily-0.3.0
- old
+ new
@@ -1,63 +1,64 @@
require 'fuzzily/trigram'
+require 'ostruct'
module Fuzzily
module Searchable
- # fuzzily_searchable <field> [, <field>...] [, <options>]
- def fuzzily_searchable(*fields)
- options = fields.last.kind_of?(Hash) ? fields.pop : {}
- fields.each do |field|
- make_field_fuzzily_searchable(field, options)
+ def self.included(by)
+ case ActiveRecord::VERSION::MAJOR
+ when 2 then by.extend Rails2ClassMethods
+ when 3 then by.extend Rails3ClassMethods
+ when 4 then by.extend Rails4ClassMethods
end
end
private
- def make_field_fuzzily_searchable(field, options={})
- class_variable_defined?(:"@@fuzzily_searchable_#{field}") and return
+ def _update_fuzzy!(_o)
+ self.send(_o.trigram_association).delete_all
+ String.new(self.send(_o.field)).scored_trigrams.each do |trigram, score|
+ self.send(_o.trigram_association).create!(:score => score, :trigram => trigram, :owner_type => self.class.name)
+ end
+ end
- trigram_class_name = options.fetch(:class_name, 'Trigram')
- trigram_association = "trigrams_for_#{field}".to_sym
- update_trigrams_method = "update_fuzzy_#{field}!".to_sym
- supports_bulk_inserts =
- connection.class.name !~ /sqlite/i ||
- connection.send(:sqlite_version) >= '3.7.11'
+ module ClassMethods
+ # fuzzily_searchable <field> [, <field>...] [, <options>]
+ def fuzzily_searchable(*fields)
+ options = fields.last.kind_of?(Hash) ? fields.pop : {}
- has_many trigram_association,
- :class_name => trigram_class_name,
- :as => :owner,
- :conditions => { :fuzzy_field => field.to_s },
- :dependent => :destroy,
- :autosave => true
-
- singleton_class.send(:define_method,"find_by_fuzzy_#{field}".to_sym) do |*args|
- case args.size
- when 1 then pattern = args.first ; options = {}
- when 2 then pattern, options = args
- else raise 'Wrong # of arguments'
+ fields.each do |field|
+ make_field_fuzzily_searchable(field, options)
end
+ end
+ private
+
+ def _find_by_fuzzy(_o, pattern, options={})
options[:limit] ||= 10
- trigram_class_name.constantize.
- scoped(options).
+ _o.trigram_class_name.constantize.
+ limit(options[:limit]).
for_model(self.name).
- for_field(field.to_s).
+ for_field(_o.field.to_s).
matches_for(pattern)
end
- singleton_class.send(:define_method,"bulk_update_fuzzy_#{field}".to_sym) do
- trigram_class = trigram_class_name.constantize
+ def _bulk_update_fuzzy(_o)
+ trigram_class = _o.trigram_class_name.constantize
- self.scoped(:include => trigram_association).find_in_batches(:batch_size => 100) do |batch|
+ supports_bulk_inserts =
+ connection.class.name !~ /sqlite/i ||
+ connection.send(:sqlite_version) >= '3.7.11'
+
+ _with_included_trigrams(_o).find_in_batches(:batch_size => 100) do |batch|
inserts = []
batch.each do |record|
- data = Fuzzily::String.new(record.send(field))
+ data = Fuzzily::String.new(record.send(_o.field))
data.scored_trigrams.each do |trigram, score|
- inserts << sanitize_sql_array(['(?,?,?,?,?)', self.name, record.id, field.to_s, score, trigram])
+ inserts << sanitize_sql_array(['(?,?,?,?,?)', self.name, record.id, _o.field.to_s, score, trigram])
end
end
# take care of quoting
c = trigram_class.connection
@@ -72,11 +73,11 @@
c.quote_column_name('score'),
c.quote_column_name('trigram')
]
trigram_class.transaction do
- batch.each { |record| record.send(trigram_association).delete_all }
+ batch.each { |record| record.send(_o.trigram_association).delete_all }
break if inserts.empty?
if supports_bulk_inserts
trigram_class.connection.insert(insert_sql + inserts.join(", "))
else
@@ -86,22 +87,81 @@
end
end
end
end
- define_method update_trigrams_method do
- self.send(trigram_association).delete_all
- String.new(self.send(field)).scored_trigrams.each do |trigram, score|
- self.send(trigram_association).create!(:score => score, :trigram => trigram, :owner_type => self.class.name)
+ def make_field_fuzzily_searchable(field, options={})
+ class_variable_defined?(:"@@fuzzily_searchable_#{field}") and return
+
+ _o = OpenStruct.new(
+ :field => field,
+ :trigram_class_name => options.fetch(:class_name, 'Trigram'),
+ :trigram_association => "trigrams_for_#{field}".to_sym,
+ :update_trigrams_method => "update_fuzzy_#{field}!".to_sym
+ )
+
+ _add_trigram_association(_o)
+
+ singleton_class.send(:define_method,"find_by_fuzzy_#{field}".to_sym) do |*args|
+ _find_by_fuzzy(_o, *args)
end
+
+ singleton_class.send(:define_method,"bulk_update_fuzzy_#{field}".to_sym) do
+ _bulk_update_fuzzy(_o)
+ end
+
+ define_method _o.update_trigrams_method do
+ _update_fuzzy!(_o)
+ end
+
+ after_save do |record|
+ next unless record.send("#{field}_changed?".to_sym)
+ record.send(_o.update_trigrams_method)
+ end
+
+ class_variable_set(:"@@fuzzily_searchable_#{field}", true)
+ self
end
+ end
- after_save do |record|
- next unless record.send("#{field}_changed?".to_sym)
- record.send(update_trigrams_method)
+ module Rails2ClassMethods
+ include ClassMethods
+
+ private
+
+ def _add_trigram_association(_o)
+ has_many _o.trigram_association,
+ :class_name => _o.trigram_class_name,
+ :as => :owner,
+ :conditions => { :fuzzy_field => _o.field.to_s },
+ :dependent => :destroy,
+ :autosave => true
end
- class_variable_set(:"@@fuzzily_searchable_#{field}", true)
- self
+ def _with_included_trigrams(_o)
+ self.scoped(:include => _o.trigram_association)
+ end
end
+
+ Rails3ClassMethods = Rails2ClassMethods
+
+ module Rails4ClassMethods
+ include ClassMethods
+
+ private
+
+ def _add_trigram_association(_o)
+ has_many _o.trigram_association,
+ lambda { where(:fuzzy_field => _o.field.to_s) },
+ :class_name => _o.trigram_class_name,
+ :as => :owner,
+ :dependent => :delete_all,
+ :autosave => true
+ end
+
+ def _with_included_trigrams(_o)
+ self.includes(_o.trigram_association)
+ end
+ end
+
end
-end
+end
\ No newline at end of file