fuzzily 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7405e2a5492b3d3ebc3a3af29130fe2b105d9d00
4
- data.tar.gz: cff5f7936cdd377ba57975341457e1a9c3941038
3
+ metadata.gz: c6b37d6f8b23347b04d0efb6d069c41654def4e5
4
+ data.tar.gz: 30c2b6d4362466e5cb63a9ae14496bd92e26ac54
5
5
  SHA512:
6
- metadata.gz: b42bc03dbc0e11415ba757ee9e5dd56e99055def2a368d82968ec7fcb30a3eab90c9f0065b4b0ad5e10bbc0176b31c4998a0dcd976e10809a45bb2e4e0e069cd
7
- data.tar.gz: bc411000aedc0213f1b884080406fb8d611bb24169d10a852d04f29015051e150230e49277be2885cf0248211e95eda7ec8afeffe9db52a17636b49b3c80741a
6
+ metadata.gz: b65ff29d86fb3822ee426f1f7731d4f254f5cc180c5b4b0cf8f5489d2c492d02341ca3747a349f60291bb035783d617d333a72c55c7e80e794b5198860423348
7
+ data.tar.gz: f2993001f8f9189101644ff8ce52d243832e2479f6cc1b567632d6f2d7d2ffe4c6be5cef87a9082eaf62bd50fe482907065dec22a24e561244a8a118e989a4d6
@@ -10,14 +10,20 @@ gemfile:
10
10
  - gemfiles/rails32.gemfile
11
11
  - gemfiles/rails32_pg.gemfile
12
12
  - gemfiles/rails32_mysql.gemfile
13
+ - gemfiles/rails40.gemfile
13
14
  before_install: bundle install
14
15
  bundler_args:
15
16
  matrix:
16
17
  exclude:
17
18
  - rvm: 2.0.0
18
19
  gemfile: gemfiles/rails23.gemfile
19
- env: TRAVIS=TRUE
20
+ - rvm: 1.8.7
21
+ gemfile: gemfiles/rails40.gemfile
22
+ - rvm: 1.9.2
23
+ gemfile: gemfiles/rails40.gemfile
20
24
  before_script:
21
25
  - psql -c 'create database fuzzily_test;' -U postgres
22
26
  - mysql -e 'create database fuzzily_test;'
23
- env: TRAVIS=TRUE
27
+ env:
28
+ global:
29
+ - TRAVIS=TRUE
data/Appraisals CHANGED
@@ -21,3 +21,7 @@ end
21
21
  appraise "rails32-mysql" do
22
22
  gem "activerecord", "~> 3.2.0"
23
23
  end
24
+
25
+ appraise "rails40" do
26
+ gem "activerecord", "~> 4.0.0"
27
+ end
data/Gemfile CHANGED
@@ -1,4 +1,7 @@
1
1
  source ENV.fetch('GEM_SOURCE','https://rubygems.org')
2
2
 
3
+ gem "activerecord", "~> 3.2.1"
4
+
3
5
  # Specify your gem's dependencies in fuzzily.gemspec
4
6
  gemspec
7
+
@@ -1,11 +1,11 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fuzzily (0.2.4)
4
+ fuzzily (0.3.0)
5
5
  activerecord (>= 2.3.17)
6
6
 
7
7
  GEM
8
- remote: http://yarp.dev/
8
+ remote: https://rubygems.org/
9
9
  specs:
10
10
  activemodel (3.2.14)
11
11
  activesupport (= 3.2.14)
@@ -33,12 +33,12 @@ GEM
33
33
  thor
34
34
  diff-lcs (1.2.4)
35
35
  i18n (0.6.5)
36
- method_source (0.8.1)
37
- mime-types (1.23)
36
+ method_source (0.8.2)
37
+ mime-types (1.25)
38
38
  multi_json (1.7.9)
39
- mysql2 (0.3.11)
40
- pg (0.15.1)
41
- pry (0.9.12.1)
39
+ mysql2 (0.3.13)
40
+ pg (0.16.0)
41
+ pry (0.9.12.2)
42
42
  coderay (~> 1.0.5)
43
43
  method_source (~> 0.8)
44
44
  slop (~> 3.4)
@@ -47,20 +47,20 @@ GEM
47
47
  rake (10.1.0)
48
48
  rest-client (1.6.7)
49
49
  mime-types (>= 1.16)
50
- rspec (2.13.0)
51
- rspec-core (~> 2.13.0)
52
- rspec-expectations (~> 2.13.0)
53
- rspec-mocks (~> 2.13.0)
54
- rspec-core (2.13.1)
55
- rspec-expectations (2.13.0)
50
+ rspec (2.14.1)
51
+ rspec-core (~> 2.14.0)
52
+ rspec-expectations (~> 2.14.0)
53
+ rspec-mocks (~> 2.14.0)
54
+ rspec-core (2.14.5)
55
+ rspec-expectations (2.14.2)
56
56
  diff-lcs (>= 1.1.3, < 2.0)
57
- rspec-mocks (2.13.1)
57
+ rspec-mocks (2.14.3)
58
58
  simplecov (0.7.1)
59
59
  multi_json (~> 1.0)
60
60
  simplecov-html (~> 0.7.1)
61
61
  simplecov-html (0.7.1)
62
- slop (3.4.4)
63
- sqlite3 (1.3.7)
62
+ slop (3.4.6)
63
+ sqlite3 (1.3.8)
64
64
  thor (0.18.1)
65
65
  tzinfo (0.3.37)
66
66
 
@@ -68,6 +68,7 @@ PLATFORMS
68
68
  ruby
69
69
 
70
70
  DEPENDENCIES
71
+ activerecord (~> 3.2.1)
71
72
  appraisal
72
73
  coveralls
73
74
  fuzzily!
data/README.md CHANGED
@@ -15,7 +15,7 @@ Blurrily finds misspelled, prefix, or partial needles in a haystack of
15
15
  strings. It's a fast, [trigram](http://en.wikipedia.org/wiki/N-gram)-based, database-backed [fuzzy](http://en.wikipedia.org/wiki/Approximate_string_matching) string search/match engine for Rails.
16
16
  Loosely inspired from an [old blog post](http://unirec.blogspot.co.uk/2007/12/live-fuzzy-search-using-n-grams-in.html).
17
17
 
18
- Tested with ActiveRecord (2.3, 3.0, 3.1, 3.2) on various Rubies (1.8.7, 1.9.2, 1.9.3, 2.0.0) and the most common adapters (SQLite3, MySQL, and PostgreSQL).
18
+ Tested with ActiveRecord (2.3, 3.0, 3.1, 3.2, 4.0) on various Rubies (1.8.7, 1.9.2, 1.9.3, 2.0.0) and the most common adapters (SQLite3, MySQL, and PostgreSQL).
19
19
 
20
20
  If your dateset is big, if you need yet more speed, or do not use ActiveRecord,
21
21
  check out [blurrily](http://github.com/mezis/blurrily), another gem (backed with a C extension)
@@ -71,7 +71,9 @@ Search!
71
71
  MyStuff.find_by_fuzzy_name('Some Name', :limit => 10)
72
72
  # => records
73
73
 
74
+ You can force an update on a specific record with
74
75
 
76
+ MyStuff.find(123).update_fuzzy_name!
75
77
 
76
78
  ## Indexing more than one field
77
79
 
@@ -131,4 +133,5 @@ Copyright (c) 2013 HouseTrip Ltd.
131
133
  5. Create a new Pull Request
132
134
 
133
135
 
134
- Thanks to @bclennox and @fdegiuli for helping fix compatibility issues.
136
+ Thanks to @bclennox, @fdegiuli, @nickbender, @Shanison for pointing out
137
+ and/or helping on various issues.
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /Users/mezis/Dropbox/Development/fuzzily
3
3
  specs:
4
- fuzzily (0.2.4)
4
+ fuzzily (0.3.0)
5
5
  activerecord (>= 2.3.17)
6
6
 
7
7
  GEM
@@ -48,8 +48,8 @@ GEM
48
48
  multi_json (~> 1.0)
49
49
  simplecov-html (~> 0.7.1)
50
50
  simplecov-html (0.7.1)
51
- slop (3.4.4)
52
- sqlite3 (1.3.7)
51
+ slop (3.4.6)
52
+ sqlite3 (1.3.8)
53
53
  thor (0.18.1)
54
54
 
55
55
  PLATFORMS
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /Users/mezis/Dropbox/Development/fuzzily
3
3
  specs:
4
- fuzzily (0.2.4)
4
+ fuzzily (0.3.0)
5
5
  activerecord (>= 2.3.17)
6
6
 
7
7
  GEM
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /Users/mezis/Dropbox/Development/fuzzily
3
3
  specs:
4
- fuzzily (0.2.4)
4
+ fuzzily (0.3.0)
5
5
  activerecord (>= 2.3.17)
6
6
 
7
7
  GEM
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /Users/mezis/Dropbox/Development/fuzzily
3
3
  specs:
4
- fuzzily (0.2.4)
4
+ fuzzily (0.3.0)
5
5
  activerecord (>= 2.3.17)
6
6
 
7
7
  GEM
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /Users/mezis/Dropbox/Development/fuzzily
3
3
  specs:
4
- fuzzily (0.2.4)
4
+ fuzzily (0.3.0)
5
5
  activerecord (>= 2.3.17)
6
6
 
7
7
  GEM
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /Users/mezis/Dropbox/Development/fuzzily
3
3
  specs:
4
- fuzzily (0.2.4)
4
+ fuzzily (0.3.0)
5
5
  activerecord (>= 2.3.17)
6
6
 
7
7
  GEM
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 4.0.0"
6
+
7
+ gemspec :path=>"../"
@@ -0,0 +1,89 @@
1
+ PATH
2
+ remote: /Users/mezis/Dropbox/Development/fuzzily
3
+ specs:
4
+ fuzzily (0.3.0)
5
+ activerecord (>= 2.3.17)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activemodel (4.0.0)
11
+ activesupport (= 4.0.0)
12
+ builder (~> 3.1.0)
13
+ activerecord (4.0.0)
14
+ activemodel (= 4.0.0)
15
+ activerecord-deprecated_finders (~> 1.0.2)
16
+ activesupport (= 4.0.0)
17
+ arel (~> 4.0.0)
18
+ activerecord-deprecated_finders (1.0.3)
19
+ activesupport (4.0.0)
20
+ i18n (~> 0.6, >= 0.6.4)
21
+ minitest (~> 4.2)
22
+ multi_json (~> 1.3)
23
+ thread_safe (~> 0.1)
24
+ tzinfo (~> 0.3.37)
25
+ appraisal (0.5.2)
26
+ bundler
27
+ rake
28
+ arel (4.0.0)
29
+ atomic (1.1.13)
30
+ builder (3.1.4)
31
+ coderay (1.0.9)
32
+ colorize (0.5.8)
33
+ coveralls (0.6.7)
34
+ colorize
35
+ multi_json (~> 1.3)
36
+ rest-client
37
+ simplecov (>= 0.7)
38
+ thor
39
+ diff-lcs (1.2.4)
40
+ i18n (0.6.5)
41
+ method_source (0.8.2)
42
+ mime-types (1.25)
43
+ minitest (4.7.5)
44
+ multi_json (1.7.9)
45
+ mysql2 (0.3.13)
46
+ pg (0.16.0)
47
+ pry (0.9.12.2)
48
+ coderay (~> 1.0.5)
49
+ method_source (~> 0.8)
50
+ slop (~> 3.4)
51
+ pry-nav (0.2.3)
52
+ pry (~> 0.9.10)
53
+ rake (10.1.0)
54
+ rest-client (1.6.7)
55
+ mime-types (>= 1.16)
56
+ rspec (2.14.1)
57
+ rspec-core (~> 2.14.0)
58
+ rspec-expectations (~> 2.14.0)
59
+ rspec-mocks (~> 2.14.0)
60
+ rspec-core (2.14.5)
61
+ rspec-expectations (2.14.2)
62
+ diff-lcs (>= 1.1.3, < 2.0)
63
+ rspec-mocks (2.14.3)
64
+ simplecov (0.7.1)
65
+ multi_json (~> 1.0)
66
+ simplecov-html (~> 0.7.1)
67
+ simplecov-html (0.7.1)
68
+ slop (3.4.6)
69
+ sqlite3 (1.3.8)
70
+ thor (0.18.1)
71
+ thread_safe (0.1.2)
72
+ atomic
73
+ tzinfo (0.3.37)
74
+
75
+ PLATFORMS
76
+ ruby
77
+
78
+ DEPENDENCIES
79
+ activerecord (~> 4.0.0)
80
+ appraisal
81
+ coveralls
82
+ fuzzily!
83
+ mysql2
84
+ pg
85
+ pry
86
+ pry-nav
87
+ rake
88
+ rspec
89
+ sqlite3
@@ -4,4 +4,4 @@ require "fuzzily/migration"
4
4
  require "fuzzily/model"
5
5
  require "active_record"
6
6
 
7
- ActiveRecord::Base.extend(Fuzzily::Searchable)
7
+ ActiveRecord::Base.send :include, Fuzzily::Searchable
@@ -5,8 +5,7 @@ module Fuzzily
5
5
 
6
6
  def self.included(by)
7
7
  by.ancestors.include?(ActiveRecord::Base) or raise 'Not included in an ActiveRecord subclass'
8
-
9
- scope_method = ActiveRecord::VERSION::MAJOR == 2 ? :named_scope : :scope
8
+ by.extend(ClassMethods)
10
9
 
11
10
  by.class_eval do
12
11
  return if class_variable_defined?(:@@fuzzily_trigram_model)
@@ -18,31 +17,65 @@ module Fuzzily
18
17
  validates_presence_of :score
19
18
  validates_presence_of :fuzzy_field
20
19
 
21
- send scope_method, :for_model, lambda { |model| {
22
- :conditions => { :owner_type => model.kind_of?(Class) ? model.name : model }
23
- }}
24
- send scope_method, :for_field, lambda { |field_name| {
25
- :conditions => { :fuzzy_field => field_name }
26
- }}
27
- send scope_method, :with_trigram, lambda { |trigrams| {
28
- :conditions => { :trigram => trigrams }
29
- }}
30
-
20
+ _add_fuzzy_scopes
31
21
  class_variable_set(:@@fuzzily_trigram_model, true)
32
22
  end
33
-
34
- by.extend(ClassMethods)
35
23
  end
36
24
 
37
25
  module ClassMethods
38
26
  def matches_for(text)
39
- trigrams = Fuzzily::String.new(text).trigrams
40
- self.
41
- scoped(:select => 'owner_id, owner_type, count(*) AS matches, MAX(score) AS score').
42
- scoped(:group => 'owner_id, owner_type').
43
- scoped(:order => 'matches DESC, score ASC').
44
- with_trigram(trigrams).
45
- map(&:owner)
27
+ _matches_for_trigrams Fuzzily::String.new(text).trigrams
28
+ end
29
+
30
+ private
31
+
32
+ module Rails2
33
+ def _matches_for_trigrams(trigrams)
34
+ self.
35
+ scoped(:select => 'owner_id, owner_type, count(*) AS matches, MAX(score) AS score').
36
+ scoped(:group => 'owner_id, owner_type').
37
+ scoped(:order => 'matches DESC, score ASC').
38
+ with_trigram(trigrams).
39
+ map(&:owner)
40
+ end
41
+
42
+ def _add_fuzzy_scopes
43
+ named_scope :for_model, lambda { |model| {
44
+ :conditions => { :owner_type => model.kind_of?(Class) ? model.name : model }
45
+ }}
46
+ named_scope :for_field, lambda { |field_name| {
47
+ :conditions => { :fuzzy_field => field_name }
48
+ }}
49
+ named_scope :with_trigram, lambda { |trigrams| {
50
+ :conditions => { :trigram => trigrams }
51
+ }}
52
+ named_scope :limit, lambda { |count| { :limit => count }}
53
+ end
54
+ end
55
+
56
+ module Rails3
57
+ def _matches_for_trigrams(trigrams)
58
+ self.
59
+ select('owner_id, owner_type, count(*) AS matches, MAX(score) AS score').
60
+ group('owner_id, owner_type').
61
+ order('matches DESC, score ASC').
62
+ with_trigram(trigrams).
63
+ map(&:owner)
64
+ end
65
+
66
+ def _add_fuzzy_scopes
67
+ scope :for_model, lambda { |model|
68
+ where(:owner_type => model.kind_of?(Class) ? model.name : model)
69
+ }
70
+ scope :for_field, lambda { |field_name| where(:fuzzy_field => field_name) }
71
+ scope :with_trigram, lambda { |trigrams| where(:trigram => trigrams) }
72
+ end
73
+ end
74
+
75
+ if ActiveRecord::VERSION::MAJOR == 2
76
+ include Rails2
77
+ else
78
+ include Rails3
46
79
  end
47
80
  end
48
81
  end
@@ -1,61 +1,62 @@
1
1
  require 'fuzzily/trigram'
2
+ require 'ostruct'
2
3
 
3
4
  module Fuzzily
4
5
  module Searchable
5
- # fuzzily_searchable <field> [, <field>...] [, <options>]
6
- def fuzzily_searchable(*fields)
7
- options = fields.last.kind_of?(Hash) ? fields.pop : {}
8
6
 
9
- fields.each do |field|
10
- make_field_fuzzily_searchable(field, options)
7
+ def self.included(by)
8
+ case ActiveRecord::VERSION::MAJOR
9
+ when 2 then by.extend Rails2ClassMethods
10
+ when 3 then by.extend Rails3ClassMethods
11
+ when 4 then by.extend Rails4ClassMethods
11
12
  end
12
13
  end
13
14
 
14
15
  private
15
16
 
16
- def make_field_fuzzily_searchable(field, options={})
17
- class_variable_defined?(:"@@fuzzily_searchable_#{field}") and return
18
-
19
- trigram_class_name = options.fetch(:class_name, 'Trigram')
20
- trigram_association = "trigrams_for_#{field}".to_sym
21
- update_trigrams_method = "update_fuzzy_#{field}!".to_sym
22
-
23
- supports_bulk_inserts =
24
- connection.class.name !~ /sqlite/i ||
25
- connection.send(:sqlite_version) >= '3.7.11'
26
-
27
- has_many trigram_association,
28
- :class_name => trigram_class_name,
29
- :as => :owner,
30
- :conditions => { :fuzzy_field => field.to_s },
31
- :dependent => :destroy,
32
- :autosave => true
33
-
34
- singleton_class.send(:define_method,"find_by_fuzzy_#{field}".to_sym) do |*args|
35
- case args.size
36
- when 1 then pattern = args.first ; options = {}
37
- when 2 then pattern, options = args
38
- else raise 'Wrong # of arguments'
17
+ def _update_fuzzy!(_o)
18
+ self.send(_o.trigram_association).delete_all
19
+ String.new(self.send(_o.field)).scored_trigrams.each do |trigram, score|
20
+ self.send(_o.trigram_association).create!(:score => score, :trigram => trigram, :owner_type => self.class.name)
21
+ end
22
+ end
23
+
24
+
25
+ module ClassMethods
26
+ # fuzzily_searchable <field> [, <field>...] [, <options>]
27
+ def fuzzily_searchable(*fields)
28
+ options = fields.last.kind_of?(Hash) ? fields.pop : {}
29
+
30
+ fields.each do |field|
31
+ make_field_fuzzily_searchable(field, options)
39
32
  end
33
+ end
40
34
 
35
+ private
36
+
37
+ def _find_by_fuzzy(_o, pattern, options={})
41
38
  options[:limit] ||= 10
42
39
 
43
- trigram_class_name.constantize.
44
- scoped(options).
40
+ _o.trigram_class_name.constantize.
41
+ limit(options[:limit]).
45
42
  for_model(self.name).
46
- for_field(field.to_s).
43
+ for_field(_o.field.to_s).
47
44
  matches_for(pattern)
48
45
  end
49
46
 
50
- singleton_class.send(:define_method,"bulk_update_fuzzy_#{field}".to_sym) do
51
- trigram_class = trigram_class_name.constantize
47
+ def _bulk_update_fuzzy(_o)
48
+ trigram_class = _o.trigram_class_name.constantize
49
+
50
+ supports_bulk_inserts =
51
+ connection.class.name !~ /sqlite/i ||
52
+ connection.send(:sqlite_version) >= '3.7.11'
52
53
 
53
- self.scoped(:include => trigram_association).find_in_batches(:batch_size => 100) do |batch|
54
+ _with_included_trigrams(_o).find_in_batches(:batch_size => 100) do |batch|
54
55
  inserts = []
55
56
  batch.each do |record|
56
- data = Fuzzily::String.new(record.send(field))
57
+ data = Fuzzily::String.new(record.send(_o.field))
57
58
  data.scored_trigrams.each do |trigram, score|
58
- inserts << sanitize_sql_array(['(?,?,?,?,?)', self.name, record.id, field.to_s, score, trigram])
59
+ inserts << sanitize_sql_array(['(?,?,?,?,?)', self.name, record.id, _o.field.to_s, score, trigram])
59
60
  end
60
61
  end
61
62
 
@@ -74,7 +75,7 @@ module Fuzzily
74
75
  ]
75
76
 
76
77
  trigram_class.transaction do
77
- batch.each { |record| record.send(trigram_association).delete_all }
78
+ batch.each { |record| record.send(_o.trigram_association).delete_all }
78
79
  break if inserts.empty?
79
80
 
80
81
  if supports_bulk_inserts
@@ -88,20 +89,79 @@ module Fuzzily
88
89
  end
89
90
  end
90
91
 
91
- define_method update_trigrams_method do
92
- self.send(trigram_association).delete_all
93
- String.new(self.send(field)).scored_trigrams.each do |trigram, score|
94
- self.send(trigram_association).create!(:score => score, :trigram => trigram, :owner_type => self.class.name)
92
+ def make_field_fuzzily_searchable(field, options={})
93
+ class_variable_defined?(:"@@fuzzily_searchable_#{field}") and return
94
+
95
+ _o = OpenStruct.new(
96
+ :field => field,
97
+ :trigram_class_name => options.fetch(:class_name, 'Trigram'),
98
+ :trigram_association => "trigrams_for_#{field}".to_sym,
99
+ :update_trigrams_method => "update_fuzzy_#{field}!".to_sym
100
+ )
101
+
102
+ _add_trigram_association(_o)
103
+
104
+ singleton_class.send(:define_method,"find_by_fuzzy_#{field}".to_sym) do |*args|
105
+ _find_by_fuzzy(_o, *args)
106
+ end
107
+
108
+ singleton_class.send(:define_method,"bulk_update_fuzzy_#{field}".to_sym) do
109
+ _bulk_update_fuzzy(_o)
110
+ end
111
+
112
+ define_method _o.update_trigrams_method do
113
+ _update_fuzzy!(_o)
95
114
  end
115
+
116
+ after_save do |record|
117
+ next unless record.send("#{field}_changed?".to_sym)
118
+ record.send(_o.update_trigrams_method)
119
+ end
120
+
121
+ class_variable_set(:"@@fuzzily_searchable_#{field}", true)
122
+ self
96
123
  end
124
+ end
125
+
126
+ module Rails2ClassMethods
127
+ include ClassMethods
97
128
 
98
- after_save do |record|
99
- next unless record.send("#{field}_changed?".to_sym)
100
- record.send(update_trigrams_method)
129
+ private
130
+
131
+ def _add_trigram_association(_o)
132
+ has_many _o.trigram_association,
133
+ :class_name => _o.trigram_class_name,
134
+ :as => :owner,
135
+ :conditions => { :fuzzy_field => _o.field.to_s },
136
+ :dependent => :destroy,
137
+ :autosave => true
101
138
  end
102
139
 
103
- class_variable_set(:"@@fuzzily_searchable_#{field}", true)
104
- self
140
+ def _with_included_trigrams(_o)
141
+ self.scoped(:include => _o.trigram_association)
142
+ end
105
143
  end
144
+
145
+ Rails3ClassMethods = Rails2ClassMethods
146
+
147
+ module Rails4ClassMethods
148
+ include ClassMethods
149
+
150
+ private
151
+
152
+ def _add_trigram_association(_o)
153
+ has_many _o.trigram_association,
154
+ lambda { where(:fuzzy_field => _o.field.to_s) },
155
+ :class_name => _o.trigram_class_name,
156
+ :as => :owner,
157
+ :dependent => :delete_all,
158
+ :autosave => true
159
+ end
160
+
161
+ def _with_included_trigrams(_o)
162
+ self.includes(_o.trigram_association)
163
+ end
164
+ end
165
+
106
166
  end
107
- end
167
+ end
@@ -1,3 +1,3 @@
1
1
  module Fuzzily
2
- VERSION = "0.2.4"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -43,22 +43,27 @@ describe Fuzzily::Searchable do
43
43
  before { subject.fuzzily_searchable :name }
44
44
 
45
45
  it 'generates trigram records on creation' do
46
- subject.create(:name => 'Paris')
46
+ subject.create!(:name => 'Paris')
47
47
  subject.last.trigrams_for_name.should_not be_empty
48
48
  end
49
49
 
50
50
  it 'generates the correct trigrams' do
51
- record = subject.create(:name => 'FOO')
51
+ record = subject.create!(:name => 'FOO')
52
52
  Trigram.first.trigram.should == '**f'
53
53
  Trigram.first.owner_id.should == record.id
54
54
  Trigram.first.owner_type.should == 'Stuff'
55
55
  end
56
56
 
57
57
  it 'updates all trigram records on save' do
58
- subject.create(:name => 'Paris')
58
+ subject.create!(:name => 'Paris')
59
59
  subject.first.update_attribute :name, 'Rome'
60
60
  Trigram.all.map(&:trigram).should =~ %w(**r *ro rom ome me*)
61
61
  end
62
+
63
+ it 'deletes all trigrams on destroy' do
64
+ subject.create!(:name => 'Paris').destroy
65
+ Trigram.all.should be_empty
66
+ end
62
67
  end
63
68
 
64
69
  describe '#update_fuzzy_<field>!' do
@@ -67,14 +72,14 @@ describe Fuzzily::Searchable do
67
72
  end
68
73
 
69
74
  it 're-creates trigrams' do
70
- subject.create(:name => 'Paris')
75
+ subject.create!(:name => 'Paris')
71
76
  old_ids = Trigram.all.map(&:id)
72
77
  subject.last.update_fuzzy_name!
73
78
  (old_ids & Trigram.all.map(&:id)).should be_empty
74
79
  end
75
80
 
76
81
  it 'ignores nil values' do
77
- subject.create(:name => nil)
82
+ subject.create!(:name => nil)
78
83
  subject.last.update_fuzzy_name!
79
84
  Trigram.all.should be_empty
80
85
  end
@@ -84,14 +89,14 @@ describe Fuzzily::Searchable do
84
89
  before { subject.fuzzily_searchable :name }
85
90
 
86
91
  it 'creates all trigrams' do
87
- subject.create(:name => 'Paris')
92
+ subject.create!(:name => 'Paris')
88
93
  Trigram.delete_all
89
94
  subject.bulk_update_fuzzy_name
90
95
  Trigram.all.should_not be_empty
91
96
  end
92
97
 
93
98
  it 'ignores nil values' do
94
- subject.create(:name => nil)
99
+ subject.create!(:name => nil)
95
100
  Trigram.delete_all
96
101
  subject.bulk_update_fuzzy_name
97
102
  Trigram.all.should be_empty
@@ -102,9 +107,9 @@ describe Fuzzily::Searchable do
102
107
  describe '#find_by_fuzzy_<field>' do
103
108
  it 'returns records' do
104
109
  subject.fuzzily_searchable :name
105
- @paris = subject.create(:name => 'Paris')
106
- @palma = subject.create(:name => 'Palma de Majorca')
107
- @palmyre = subject.create(:name => 'La Palmyre')
110
+ @paris = subject.create!(:name => 'Paris')
111
+ @palma = subject.create!(:name => 'Palma de Majorca')
112
+ @palmyre = subject.create!(:name => 'La Palmyre')
108
113
 
109
114
  subject.find_by_fuzzy_name('Piris').should_not be_empty
110
115
  subject.find_by_fuzzy_name('Piris').should =~ [@paris, @palma]
@@ -113,10 +118,10 @@ describe Fuzzily::Searchable do
113
118
 
114
119
  it 'favours exact matches' do
115
120
  subject.fuzzily_searchable :name
116
- @new_york = subject.create(:name => 'New York')
117
- @yorkshire = subject.create(:name => 'Yorkshire')
118
- @york = subject.create(:name => 'York')
119
- @yorkisthan = subject.create(:name => 'Yorkisthan')
121
+ @new_york = subject.create!(:name => 'New York')
122
+ @yorkshire = subject.create!(:name => 'Yorkshire')
123
+ @york = subject.create!(:name => 'York')
124
+ @yorkisthan = subject.create!(:name => 'Yorkisthan')
120
125
 
121
126
  subject.find_by_fuzzy_name('York').should == [@york, @new_york, @yorkshire, @yorkisthan]
122
127
  subject.find_by_fuzzy_name('Yorkshire').should == [@yorkshire, @york, @yorkisthan, @new_york]
@@ -124,11 +129,17 @@ describe Fuzzily::Searchable do
124
129
 
125
130
  it 'does not favour short words' do
126
131
  subject.fuzzily_searchable :name
127
- @lo = subject.create(:name => 'Lo') # **l *lo lo*
128
- @london = subject.create(:name => 'London') # **l *lo lon ond ndo don on*
132
+ @lo = subject.create!(:name => 'Lo') # **l *lo lo*
133
+ @london = subject.create!(:name => 'London') # **l *lo lon ond ndo don on*
129
134
  # **l *lo lon
130
135
  subject.find_by_fuzzy_name('Lon').should == [@london, @lo]
131
136
  end
137
+
138
+ it 'honours limie option' do
139
+ subject.fuzzily_searchable :name
140
+ 3.times { subject.create!(:name => 'Paris') }
141
+ subject.find_by_fuzzy_name('Paris', :limit => 2).length.should == 2
142
+ end
132
143
  end
133
144
  end
134
145
 
@@ -27,7 +27,7 @@ def get_connection_hash
27
27
  :adapter => 'mysql2',
28
28
  :database => 'fuzzily_test',
29
29
  :host => 'localhost',
30
- :user => ENV['FUZZILY_DB_USER']
30
+ :username => ENV['FUZZILY_DB_USER']
31
31
  }
32
32
  when 'sqlite3'
33
33
  {
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fuzzily
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julien Letessier
@@ -180,6 +180,8 @@ files:
180
180
  - gemfiles/rails32_mysql.gemfile.lock
181
181
  - gemfiles/rails32_pg.gemfile
182
182
  - gemfiles/rails32_pg.gemfile.lock
183
+ - gemfiles/rails40.gemfile
184
+ - gemfiles/rails40.gemfile.lock
183
185
  - lib/fuzzily.rb
184
186
  - lib/fuzzily/migration.rb
185
187
  - lib/fuzzily/model.rb