pg_search 2.2.0 → 2.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +1 -0
  3. data/.editorconfig +10 -0
  4. data/.github/dependabot.yml +11 -0
  5. data/.rubocop.yml +88 -7
  6. data/.travis.yml +11 -19
  7. data/CHANGELOG.md +40 -15
  8. data/Gemfile +1 -1
  9. data/LICENSE +1 -1
  10. data/README.md +46 -45
  11. data/lib/pg_search.rb +13 -56
  12. data/lib/pg_search/document.rb +2 -2
  13. data/lib/pg_search/features/dmetaphone.rb +4 -6
  14. data/lib/pg_search/features/tsearch.rb +13 -12
  15. data/lib/pg_search/migration/templates/add_pg_search_dmetaphone_support_functions.rb.erb +6 -6
  16. data/lib/pg_search/migration/templates/create_pg_search_documents.rb.erb +2 -2
  17. data/lib/pg_search/model.rb +57 -0
  18. data/lib/pg_search/multisearch.rb +10 -1
  19. data/lib/pg_search/multisearch/rebuilder.rb +7 -3
  20. data/lib/pg_search/scope_options.rb +2 -2
  21. data/lib/pg_search/tasks.rb +2 -1
  22. data/lib/pg_search/version.rb +1 -1
  23. data/pg_search.gemspec +9 -5
  24. data/spec/.rubocop.yml +2 -2
  25. data/spec/integration/.rubocop.yml +11 -0
  26. data/spec/integration/associations_spec.rb +27 -66
  27. data/spec/integration/deprecation_spec.rb +33 -0
  28. data/spec/integration/pagination_spec.rb +1 -1
  29. data/spec/integration/pg_search_spec.rb +70 -59
  30. data/spec/integration/single_table_inheritance_spec.rb +2 -2
  31. data/spec/lib/pg_search/configuration/association_spec.rb +10 -8
  32. data/spec/lib/pg_search/configuration/foreign_column_spec.rb +1 -1
  33. data/spec/lib/pg_search/features/dmetaphone_spec.rb +2 -2
  34. data/spec/lib/pg_search/features/trigram_spec.rb +15 -11
  35. data/spec/lib/pg_search/features/tsearch_spec.rb +16 -10
  36. data/spec/lib/pg_search/multisearch/rebuilder_spec.rb +124 -76
  37. data/spec/lib/pg_search/multisearch_spec.rb +49 -30
  38. data/spec/lib/pg_search/multisearchable_spec.rb +155 -101
  39. data/spec/lib/pg_search/normalizer_spec.rb +12 -10
  40. data/spec/lib/pg_search_spec.rb +59 -46
  41. data/spec/spec_helper.rb +13 -4
  42. data/spec/support/database.rb +1 -1
  43. metadata +76 -13
@@ -11,7 +11,7 @@ describe "a pg_search_scope on an STI subclass" do
11
11
  end
12
12
 
13
13
  model do
14
- include PgSearch
14
+ include PgSearch::Model
15
15
  pg_search_scope :search_content, against: :content
16
16
  end
17
17
  end
@@ -51,7 +51,7 @@ describe "a pg_search_scope on an STI subclass" do
51
51
  end
52
52
 
53
53
  model do
54
- include PgSearch
54
+ include PgSearch::Model
55
55
  self.inheritance_column = 'custom_type'
56
56
  pg_search_scope :search_content, against: :content
57
57
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "spec_helper"
4
4
 
5
+ # rubocop:disable RSpec/NestedGroups
5
6
  describe PgSearch::Configuration::Association do
6
7
  with_model :Avatar do
7
8
  table do |t|
@@ -17,7 +18,7 @@ describe PgSearch::Configuration::Association do
17
18
  end
18
19
 
19
20
  model do
20
- include PgSearch
21
+ include PgSearch::Model
21
22
  has_one :avatar, class_name: "Avatar"
22
23
  belongs_to :site
23
24
 
@@ -32,14 +33,14 @@ describe PgSearch::Configuration::Association do
32
33
  end
33
34
 
34
35
  model do
35
- include PgSearch
36
+ include PgSearch::Model
36
37
  has_many :users, class_name: "User"
37
38
 
38
39
  pg_search_scope :with_users, associated_against: { users: :name }
39
40
  end
40
41
  end
41
42
 
42
- context "has_one" do
43
+ context "with has_one" do
43
44
  let(:association) { described_class.new(User, :avatar, :url) }
44
45
 
45
46
  describe "#table_name" do
@@ -50,7 +51,7 @@ describe PgSearch::Configuration::Association do
50
51
 
51
52
  describe "#join" do
52
53
  let(:expected_sql) do
53
- <<-SQL.gsub(/\s+/, ' ').strip
54
+ <<~SQL.squish
54
55
  LEFT OUTER JOIN
55
56
  (SELECT model_id AS id,
56
57
  #{column_select} AS #{association.columns.first.alias}
@@ -70,7 +71,7 @@ describe PgSearch::Configuration::Association do
70
71
  end
71
72
  end
72
73
 
73
- context "belongs_to" do
74
+ context "with belongs_to" do
74
75
  let(:association) { described_class.new(User, :site, :title) }
75
76
 
76
77
  describe "#table_name" do
@@ -81,7 +82,7 @@ describe PgSearch::Configuration::Association do
81
82
 
82
83
  describe "#join" do
83
84
  let(:expected_sql) do
84
- <<-SQL.gsub(/\s+/, ' ').strip
85
+ <<~SQL.squish
85
86
  LEFT OUTER JOIN
86
87
  (SELECT model_id AS id,
87
88
  #{column_select} AS #{association.columns.first.alias}
@@ -101,7 +102,7 @@ describe PgSearch::Configuration::Association do
101
102
  end
102
103
  end
103
104
 
104
- context "has_many" do
105
+ context "with has_many" do
105
106
  let(:association) { described_class.new(Site, :users, :name) }
106
107
 
107
108
  describe "#table_name" do
@@ -112,7 +113,7 @@ describe PgSearch::Configuration::Association do
112
113
 
113
114
  describe "#join" do
114
115
  let(:expected_sql) do
115
- <<-SQL.gsub(/\s+/, ' ').strip
116
+ <<~SQL.squish
116
117
  LEFT OUTER JOIN
117
118
  (SELECT model_id AS id,
118
119
  string_agg(\"#{association.table_name}\".\"name\"::text, ' ') AS #{association.columns.first.alias}
@@ -138,3 +139,4 @@ describe PgSearch::Configuration::Association do
138
139
  end
139
140
  end
140
141
  end
142
+ # rubocop:enable RSpec/NestedGroups
@@ -17,7 +17,7 @@ describe PgSearch::Configuration::ForeignColumn do
17
17
  end
18
18
 
19
19
  model do
20
- include PgSearch
20
+ include PgSearch::Model
21
21
  belongs_to :another_model, class_name: 'AssociatedModel'
22
22
 
23
23
  pg_search_scope :with_another, associated_against: { another_model: :title }
@@ -18,7 +18,7 @@ describe PgSearch::Features::DMetaphone do
18
18
  PgSearch::Configuration::Column.new(:content, nil, Model)
19
19
  ]
20
20
  options = {}
21
- config = double(:config, ignore: [])
21
+ config = instance_double("PgSearch::Configuration", :config, ignore: [])
22
22
  normalizer = PgSearch::Normalizer.new(config)
23
23
 
24
24
  feature = described_class.new(query, options, columns, Model, normalizer)
@@ -43,7 +43,7 @@ describe PgSearch::Features::DMetaphone do
43
43
  PgSearch::Configuration::Column.new(:content, nil, Model)
44
44
  ]
45
45
  options = {}
46
- config = double(:config, ignore: [])
46
+ config = instance_double("PgSearch::Configuration", :config, ignore: [])
47
47
  normalizer = PgSearch::Normalizer.new(config)
48
48
 
49
49
  feature = described_class.new(query, options, columns, Model, normalizer)
@@ -3,8 +3,10 @@
3
3
  require 'spec_helper'
4
4
  require 'ostruct'
5
5
 
6
+ # rubocop:disable RSpec/MultipleMemoizedHelpers, RSpec/NestedGroups
6
7
  describe PgSearch::Features::Trigram do
7
8
  subject(:feature) { described_class.new(query, options, columns, Model, normalizer) }
9
+
8
10
  let(:query) { 'lolwut' }
9
11
  let(:options) { {} }
10
12
  let(:columns) {
@@ -17,8 +19,10 @@ describe PgSearch::Features::Trigram do
17
19
  let(:config) { OpenStruct.new(ignore: []) }
18
20
 
19
21
  let(:coalesced_columns) do
20
- <<-SQL.strip_heredoc.chomp
21
- coalesce(#{Model.quoted_table_name}."name"::text, '') || ' ' || coalesce(#{Model.quoted_table_name}."content"::text, '')
22
+ <<~SQL.squish
23
+ coalesce(#{Model.quoted_table_name}."name"::text, '')
24
+ || ' '
25
+ || coalesce(#{Model.quoted_table_name}."content"::text, '')
22
26
  SQL
23
27
  end
24
28
 
@@ -35,7 +39,7 @@ describe PgSearch::Features::Trigram do
35
39
  expect(feature.conditions.to_sql).to eq("('#{query}' % (#{coalesced_columns}))")
36
40
  end
37
41
 
38
- context 'searching by word_similarity' do
42
+ context 'when searching by word_similarity' do
39
43
  let(:options) do
40
44
  { word_similarity: true }
41
45
  end
@@ -46,7 +50,7 @@ describe PgSearch::Features::Trigram do
46
50
  end
47
51
  end
48
52
 
49
- context 'ignoring accents' do
53
+ context 'when ignoring accents' do
50
54
  it 'escapes the search document and query, but not the accent function' do
51
55
  config.ignore = [:accents]
52
56
  expect(feature.conditions.to_sql).to eq("(unaccent('#{query}') % (unaccent(#{coalesced_columns})))")
@@ -54,7 +58,7 @@ describe PgSearch::Features::Trigram do
54
58
  end
55
59
 
56
60
  context 'when a threshold is specified' do
57
- context 'searching by similarity' do
61
+ context 'when searching by similarity' do
58
62
  let(:options) do
59
63
  { threshold: 0.5 }
60
64
  end
@@ -66,7 +70,7 @@ describe PgSearch::Features::Trigram do
66
70
  end
67
71
  end
68
72
 
69
- context 'searching by word_similarity' do
73
+ context 'when searching by word_similarity' do
70
74
  let(:options) do
71
75
  { threshold: 0.5, word_similarity: true }
72
76
  end
@@ -79,21 +83,20 @@ describe PgSearch::Features::Trigram do
79
83
  end
80
84
  end
81
85
 
82
- context 'only certain columns are selected' do
83
- context 'one column' do
86
+ context 'when only certain columns are selected' do
87
+ context 'with one column' do
84
88
  let(:options) { { only: :name } }
85
89
 
86
90
  it 'only searches against the select column' do
87
- options = { only: :name }
88
91
  coalesced_column = "coalesce(#{Model.quoted_table_name}.\"name\"::text, '')"
89
92
  expect(feature.conditions.to_sql).to eq("('#{query}' % (#{coalesced_column}))")
90
93
  end
91
94
  end
92
- context 'multiple columns' do
95
+
96
+ context 'with multiple columns' do
93
97
  let(:options) { { only: %i[name content] } }
94
98
 
95
99
  it 'concatenates when multiples columns are selected' do
96
- options = { only: %i[name content] }
97
100
  expect(feature.conditions.to_sql).to eq("('#{query}' % (#{coalesced_columns}))")
98
101
  end
99
102
  end
@@ -106,3 +109,4 @@ describe PgSearch::Features::Trigram do
106
109
  end
107
110
  end
108
111
  end
112
+ # rubocop:enable RSpec/MultipleMemoizedHelpers, RSpec/NestedGroups
@@ -19,7 +19,7 @@ describe PgSearch::Features::TSearch do
19
19
  PgSearch::Configuration::Column.new(:content, nil, Model)
20
20
  ]
21
21
  options = {}
22
- config = double(:config, ignore: [])
22
+ config = instance_double("PgSearch::Configuration", :config, ignore: [])
23
23
  normalizer = PgSearch::Normalizer.new(config)
24
24
 
25
25
  feature = described_class.new(query, options, columns, Model, normalizer)
@@ -44,7 +44,7 @@ describe PgSearch::Features::TSearch do
44
44
  PgSearch::Configuration::Column.new(:content, nil, Model)
45
45
  ]
46
46
  options = {}
47
- config = double(:config, ignore: [])
47
+ config = instance_double("PgSearch::Configuration", :config, ignore: [])
48
48
  normalizer = PgSearch::Normalizer.new(config)
49
49
 
50
50
  feature = described_class.new(query, options, columns, Model, normalizer)
@@ -61,7 +61,7 @@ describe PgSearch::Features::TSearch do
61
61
  PgSearch::Configuration::Column.new(:content, nil, Model)
62
62
  ]
63
63
  options = { negation: true }
64
- config = double(:config, ignore: [])
64
+ config = instance_double("PgSearch::Configuration", :config, ignore: [])
65
65
  normalizer = PgSearch::Normalizer.new(config)
66
66
 
67
67
  feature = described_class.new(query, options, columns, Model, normalizer)
@@ -79,7 +79,7 @@ describe PgSearch::Features::TSearch do
79
79
  PgSearch::Configuration::Column.new(:content, nil, Model)
80
80
  ]
81
81
  options = { negation: false }
82
- config = double(:config, ignore: [])
82
+ config = instance_double("PgSearch::Configuration", :config, ignore: [])
83
83
  normalizer = PgSearch::Normalizer.new(config)
84
84
 
85
85
  feature = described_class.new(query, options, columns, Model, normalizer)
@@ -97,7 +97,7 @@ describe PgSearch::Features::TSearch do
97
97
  PgSearch::Configuration::Column.new(:content, nil, Model)
98
98
  ]
99
99
  options = { tsvector_column: "my_tsvector" }
100
- config = double(:config, ignore: [])
100
+ config = instance_double("PgSearch::Configuration", :config, ignore: [])
101
101
  normalizer = PgSearch::Normalizer.new(config)
102
102
 
103
103
  feature = described_class.new(query, options, columns, Model, normalizer)
@@ -115,7 +115,7 @@ describe PgSearch::Features::TSearch do
115
115
  PgSearch::Configuration::Column.new(:content, nil, Model)
116
116
  ]
117
117
  options = { tsvector_column: ["tsvector1", "tsvector2"] }
118
- config = double(:config, ignore: [])
118
+ config = instance_double("PgSearch::Configuration", :config, ignore: [])
119
119
  normalizer = PgSearch::Normalizer.new(config)
120
120
 
121
121
  feature = described_class.new(query, options, columns, Model, normalizer)
@@ -141,7 +141,7 @@ describe PgSearch::Features::TSearch do
141
141
  ]
142
142
  options = {}
143
143
 
144
- config = double(:config, ignore: [])
144
+ config = instance_double("PgSearch::Configuration", :config, ignore: [])
145
145
  normalizer = PgSearch::Normalizer.new(config)
146
146
 
147
147
  feature = described_class.new(query, options, columns, Model, normalizer)
@@ -151,6 +151,7 @@ describe PgSearch::Features::TSearch do
151
151
  end
152
152
 
153
153
  context "when options[:dictionary] is passed" do
154
+ # rubocop:disable RSpec/ExampleLength
154
155
  it 'uses the provided dictionary' do
155
156
  query = "query"
156
157
  columns = [
@@ -165,7 +166,7 @@ describe PgSearch::Features::TSearch do
165
166
  }
166
167
  }
167
168
 
168
- config = double(:config, ignore: [])
169
+ config = instance_double("PgSearch::Configuration", :config, ignore: [])
169
170
  normalizer = PgSearch::Normalizer.new(config)
170
171
 
171
172
  feature = described_class.new(query, options, columns, Model, normalizer)
@@ -174,9 +175,11 @@ describe PgSearch::Features::TSearch do
174
175
 
175
176
  expect(feature.highlight.to_sql).to eq(expected_sql)
176
177
  end
178
+ # rubocop:enable RSpec/ExampleLength
177
179
  end
178
180
 
179
181
  context "when options[:highlight] has options set" do
182
+ # rubocop:disable RSpec/ExampleLength
180
183
  it "passes the options to ts_headline" do
181
184
  query = "query"
182
185
  columns = [
@@ -195,7 +198,7 @@ describe PgSearch::Features::TSearch do
195
198
  }
196
199
  }
197
200
 
198
- config = double(:config, ignore: [])
201
+ config = instance_double("PgSearch::Configuration", :config, ignore: [])
199
202
  normalizer = PgSearch::Normalizer.new(config)
200
203
 
201
204
  feature = described_class.new(query, options, columns, Model, normalizer)
@@ -204,7 +207,9 @@ describe PgSearch::Features::TSearch do
204
207
 
205
208
  expect(feature.highlight.to_sql).to eq(expected_sql)
206
209
  end
210
+ # rubocop:enable RSpec/ExampleLength
207
211
 
212
+ # rubocop:disable RSpec/ExampleLength
208
213
  it "passes deprecated options to ts_headline" do
209
214
  query = "query"
210
215
  columns = [
@@ -223,7 +228,7 @@ describe PgSearch::Features::TSearch do
223
228
  }
224
229
  }
225
230
 
226
- config = double(:config, ignore: [])
231
+ config = instance_double("PgSearch::Configuration", :config, ignore: [])
227
232
  normalizer = PgSearch::Normalizer.new(config)
228
233
 
229
234
  feature = described_class.new(query, options, columns, Model, normalizer)
@@ -233,6 +238,7 @@ describe PgSearch::Features::TSearch do
233
238
 
234
239
  expect(highlight_sql).to eq(expected_sql)
235
240
  end
241
+ # rubocop:enable RSpec/ExampleLength
236
242
  end
237
243
  end
238
244
  end
@@ -2,20 +2,16 @@
2
2
 
3
3
  require "spec_helper"
4
4
 
5
- def has_microsecond_precision?
6
- (ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR >= 1) ||
7
- (ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR == 0 && ActiveRecord::VERSION::TINY >= 1)
8
- end
9
-
5
+ # rubocop:disable RSpec/NestedGroups
10
6
  describe PgSearch::Multisearch::Rebuilder do
11
- with_table "pg_search_documents", {}, &DOCUMENTS_SCHEMA
7
+ with_table "pg_search_documents", &DOCUMENTS_SCHEMA
12
8
 
13
9
  describe 'when initialized with a model that is not multisearchable' do
14
10
  with_model :not_multisearchable
15
11
 
16
12
  it 'raises an exception' do
17
13
  expect {
18
- PgSearch::Multisearch::Rebuilder.new(NotMultisearchable)
14
+ described_class.new(NotMultisearchable)
19
15
  }.to raise_exception(
20
16
  PgSearch::Multisearch::ModelNotMultisearchable,
21
17
  "NotMultisearchable is not multisearchable. See PgSearch::ClassMethods#multisearchable"
@@ -25,10 +21,10 @@ describe PgSearch::Multisearch::Rebuilder do
25
21
 
26
22
  describe "#rebuild" do
27
23
  context "when the model defines .rebuild_pg_search_documents" do
28
- context "and multisearchable is not conditional" do
24
+ context "when multisearchable is not conditional" do
29
25
  with_model :Model do
30
26
  model do
31
- include PgSearch
27
+ include PgSearch::Model
32
28
  multisearchable
33
29
 
34
30
  def rebuild_pg_search_documents
@@ -36,14 +32,18 @@ describe PgSearch::Multisearch::Rebuilder do
36
32
  end
37
33
  end
38
34
 
39
- it "should call .rebuild_pg_search_documents" do
40
- rebuilder = PgSearch::Multisearch::Rebuilder.new(Model)
41
- expect(Model).to receive(:rebuild_pg_search_documents)
42
- rebuilder.rebuild
35
+ it "calls .rebuild_pg_search_documents" do
36
+ rebuilder = described_class.new(Model)
37
+
38
+ without_partial_double_verification do
39
+ allow(Model).to receive(:rebuild_pg_search_documents)
40
+ rebuilder.rebuild
41
+ expect(Model).to have_received(:rebuild_pg_search_documents)
42
+ end
43
43
  end
44
44
  end
45
45
 
46
- context "and multisearchable is conditional" do
46
+ context "when multisearchable is conditional" do
47
47
  %i[if unless].each do |conditional_key|
48
48
  context "via :#{conditional_key}" do
49
49
  with_model :Model do
@@ -52,7 +52,7 @@ describe PgSearch::Multisearch::Rebuilder do
52
52
  end
53
53
 
54
54
  model do
55
- include PgSearch
55
+ include PgSearch::Model
56
56
  multisearchable conditional_key => :active?
57
57
 
58
58
  def rebuild_pg_search_documents
@@ -60,10 +60,14 @@ describe PgSearch::Multisearch::Rebuilder do
60
60
  end
61
61
  end
62
62
 
63
- it "should call .rebuild_pg_search_documents" do
64
- rebuilder = PgSearch::Multisearch::Rebuilder.new(Model)
65
- expect(Model).to receive(:rebuild_pg_search_documents)
66
- rebuilder.rebuild
63
+ it "calls .rebuild_pg_search_documents" do
64
+ rebuilder = described_class.new(Model)
65
+
66
+ without_partial_double_verification do
67
+ allow(Model).to receive(:rebuild_pg_search_documents)
68
+ rebuilder.rebuild
69
+ expect(Model).to have_received(:rebuild_pg_search_documents)
70
+ end
67
71
  end
68
72
  end
69
73
  end
@@ -71,7 +75,7 @@ describe PgSearch::Multisearch::Rebuilder do
71
75
  end
72
76
 
73
77
  context "when the model does not define .rebuild_pg_search_documents" do
74
- context "and multisearchable is not conditional" do
78
+ context "when multisearchable is not conditional" do
75
79
  context "when :against only includes columns" do
76
80
  with_model :Model do
77
81
  table do |t|
@@ -79,13 +83,13 @@ describe PgSearch::Multisearch::Rebuilder do
79
83
  end
80
84
 
81
85
  model do
82
- include PgSearch
86
+ include PgSearch::Model
83
87
  multisearchable against: :name
84
88
  end
85
89
  end
86
90
 
87
- it "should not call :rebuild_pg_search_documents" do
88
- rebuilder = PgSearch::Multisearch::Rebuilder.new(Model)
91
+ it "does not call :rebuild_pg_search_documents" do
92
+ rebuilder = described_class.new(Model)
89
93
 
90
94
  # stub respond_to? to return false since should_not_receive defines the method
91
95
  original_respond_to = Model.method(:respond_to?)
@@ -97,28 +101,28 @@ describe PgSearch::Multisearch::Rebuilder do
97
101
  end
98
102
  end
99
103
 
100
- expect(Model).not_to receive(:rebuild_pg_search_documents)
101
- rebuilder.rebuild
104
+ without_partial_double_verification do
105
+ allow(Model).to receive(:rebuild_pg_search_documents)
106
+ rebuilder.rebuild
107
+ expect(Model).not_to have_received(:rebuild_pg_search_documents)
108
+ end
102
109
  end
103
110
 
104
- it "should execute the default SQL" do
111
+ # rubocop:disable RSpec/ExampleLength
112
+ it "executes the default SQL" do
105
113
  time = Time.utc(2001, 1, 1, 0, 0, 0)
106
- rebuilder = PgSearch::Multisearch::Rebuilder.new(Model, -> { time })
107
-
108
- # Handle change in precision of Time objects in SQL in Active Record 4.0.1
109
- # https://github.com/rails/rails/commit/17f5d8e062909f1fcae25351834d8e89967b645e
110
- expected_timestamp = has_microsecond_precision? ? "2001-01-01 00:00:00.000000" : "2001-01-01 00:00:00"
111
-
112
- expected_sql = <<-SQL.strip_heredoc
113
- INSERT INTO "pg_search_documents" (searchable_type, searchable_id, content, created_at, updated_at)
114
- SELECT 'Model' AS searchable_type,
115
- #{Model.quoted_table_name}.#{Model.primary_key} AS searchable_id,
116
- (
117
- coalesce(#{Model.quoted_table_name}."name"::text, '')
118
- ) AS content,
119
- '#{expected_timestamp}' AS created_at,
120
- '#{expected_timestamp}' AS updated_at
121
- FROM #{Model.quoted_table_name}
114
+ rebuilder = described_class.new(Model, -> { time })
115
+
116
+ expected_sql = <<~SQL.squish
117
+ INSERT INTO "pg_search_documents" (searchable_type, searchable_id, content, created_at, updated_at)
118
+ SELECT 'Model' AS searchable_type,
119
+ #{Model.quoted_table_name}.#{Model.primary_key} AS searchable_id,
120
+ (
121
+ coalesce(#{Model.quoted_table_name}."name"::text, '')
122
+ ) AS content,
123
+ '2001-01-01 00:00:00' AS created_at,
124
+ '2001-01-01 00:00:00' AS updated_at
125
+ FROM #{Model.quoted_table_name}
122
126
  SQL
123
127
 
124
128
  executed_sql = []
@@ -133,56 +137,54 @@ describe PgSearch::Multisearch::Rebuilder do
133
137
  expect(executed_sql.length).to eq(1)
134
138
  expect(executed_sql.first.strip).to eq(expected_sql.strip)
135
139
  end
140
+ # rubocop:enable RSpec/ExampleLength
136
141
 
137
- context "for a model with a camel case column" do
142
+ context "with a model with a camel case column" do
138
143
  with_model :ModelWithCamelCaseColumn do
139
144
  table do |t|
140
145
  t.string :camelName
141
146
  end
142
147
 
143
148
  model do
144
- include PgSearch
149
+ include PgSearch::Model
145
150
  multisearchable against: :name
146
151
  end
147
152
  end
148
153
 
149
154
  it "creates search document without PG error" do
150
155
  time = Time.utc(2001, 1, 1, 0, 0, 0)
151
- rebuilder = PgSearch::Multisearch::Rebuilder.new(Model, -> { time })
156
+ rebuilder = described_class.new(Model, -> { time })
152
157
  rebuilder.rebuild
153
158
  end
154
159
  end
155
160
 
156
- context "for a model with a non-standard primary key" do
161
+ context "with a model with a non-standard primary key" do
157
162
  with_model :ModelWithNonStandardPrimaryKey do
158
163
  table primary_key: :non_standard_primary_key do |t|
159
164
  t.string :name
160
165
  end
161
166
 
162
167
  model do
163
- include PgSearch
168
+ include PgSearch::Model
164
169
  multisearchable against: :name
165
170
  end
166
171
  end
167
172
 
173
+ # rubocop:disable RSpec/ExampleLength
168
174
  it "generates SQL with the correct primary key" do
169
175
  time = Time.utc(2001, 1, 1, 0, 0, 0)
170
- rebuilder = PgSearch::Multisearch::Rebuilder.new(ModelWithNonStandardPrimaryKey, -> { time })
171
-
172
- # Handle change in precision of Time objects in SQL in Active Record 4.0.1
173
- # https://github.com/rails/rails/commit/17f5d8e062909f1fcae25351834d8e89967b645e
174
- expected_timestamp = has_microsecond_precision? ? "2001-01-01 00:00:00.000000" : "2001-01-01 00:00:00"
175
-
176
- expected_sql = <<-SQL.strip_heredoc
177
- INSERT INTO "pg_search_documents" (searchable_type, searchable_id, content, created_at, updated_at)
178
- SELECT 'ModelWithNonStandardPrimaryKey' AS searchable_type,
179
- #{ModelWithNonStandardPrimaryKey.quoted_table_name}.non_standard_primary_key AS searchable_id,
180
- (
181
- coalesce(#{ModelWithNonStandardPrimaryKey.quoted_table_name}."name"::text, '')
182
- ) AS content,
183
- '#{expected_timestamp}' AS created_at,
184
- '#{expected_timestamp}' AS updated_at
185
- FROM #{ModelWithNonStandardPrimaryKey.quoted_table_name}
176
+ rebuilder = described_class.new(ModelWithNonStandardPrimaryKey, -> { time })
177
+
178
+ expected_sql = <<~SQL.squish
179
+ INSERT INTO "pg_search_documents" (searchable_type, searchable_id, content, created_at, updated_at)
180
+ SELECT 'ModelWithNonStandardPrimaryKey' AS searchable_type,
181
+ #{ModelWithNonStandardPrimaryKey.quoted_table_name}.non_standard_primary_key AS searchable_id,
182
+ (
183
+ coalesce(#{ModelWithNonStandardPrimaryKey.quoted_table_name}."name"::text, '')
184
+ ) AS content,
185
+ '2001-01-01 00:00:00' AS created_at,
186
+ '2001-01-01 00:00:00' AS updated_at
187
+ FROM #{ModelWithNonStandardPrimaryKey.quoted_table_name}
186
188
  SQL
187
189
 
188
190
  executed_sql = []
@@ -197,6 +199,7 @@ describe PgSearch::Multisearch::Rebuilder do
197
199
  expect(executed_sql.length).to eq(1)
198
200
  expect(executed_sql.first.strip).to eq(expected_sql.strip)
199
201
  end
202
+ # rubocop:enable RSpec/ExampleLength
200
203
  end
201
204
  end
202
205
 
@@ -206,7 +209,7 @@ describe PgSearch::Multisearch::Rebuilder do
206
209
  end
207
210
 
208
211
  model do
209
- include PgSearch
212
+ include PgSearch::Model
210
213
  multisearchable against: [:foo]
211
214
 
212
215
  def foo
@@ -215,10 +218,11 @@ describe PgSearch::Multisearch::Rebuilder do
215
218
  end
216
219
  end
217
220
 
221
+ # rubocop:disable RSpec/ExampleLength
218
222
  it "calls update_pg_search_document on each record" do
219
223
  record = Model.create!
220
224
 
221
- rebuilder = PgSearch::Multisearch::Rebuilder.new(Model)
225
+ rebuilder = described_class.new(Model)
222
226
 
223
227
  # stub respond_to? to return false since should_not_receive defines the method
224
228
  original_respond_to = Model.method(:respond_to?)
@@ -229,16 +233,49 @@ describe PgSearch::Multisearch::Rebuilder do
229
233
  original_respond_to.call(method_name, *args)
230
234
  end
231
235
  end
232
- expect(Model).not_to receive(:rebuild_pg_search_documents)
233
236
 
234
- rebuilder.rebuild
237
+ without_partial_double_verification do
238
+ allow(Model).to receive(:rebuild_pg_search_documents)
239
+
240
+ rebuilder.rebuild
241
+
242
+ expect(Model).not_to have_received(:rebuild_pg_search_documents)
243
+ end
235
244
 
236
245
  expect(record.pg_search_document).to be_present
237
246
  end
247
+ # rubocop:enable RSpec/ExampleLength
248
+ end
249
+
250
+ context "when only additional_attributes is set" do
251
+ with_model :Model do
252
+ table do |t|
253
+ t.string :name
254
+ end
255
+
256
+ model do
257
+ include PgSearch::Model
258
+ multisearchable against: :name,
259
+ additional_attributes: ->(obj) { { additional_attribute_column: "#{obj.class}::#{obj.id}" } }
260
+ end
261
+ end
262
+
263
+ it "calls update_pg_search_document on each record" do
264
+ record_1 = Model.create!(name: "record_1", id: 1)
265
+ record_2 = Model.create!(name: "record_2", id: 2)
266
+
267
+ PgSearch::Document.delete_all
268
+
269
+ rebuilder = described_class.new(Model)
270
+ rebuilder.rebuild
271
+
272
+ expect(record_1.reload.pg_search_document.additional_attribute_column).to eq("Model::1")
273
+ expect(record_2.reload.pg_search_document.additional_attribute_column).to eq("Model::2")
274
+ end
238
275
  end
239
276
  end
240
277
 
241
- context "and multisearchable is conditional" do
278
+ context "when multisearchable is conditional" do
242
279
  context "via :if" do
243
280
  with_model :Model do
244
281
  table do |t|
@@ -246,16 +283,17 @@ describe PgSearch::Multisearch::Rebuilder do
246
283
  end
247
284
 
248
285
  model do
249
- include PgSearch
286
+ include PgSearch::Model
250
287
  multisearchable if: :active?
251
288
  end
252
289
  end
253
290
 
291
+ # rubocop:disable RSpec/ExampleLength
254
292
  it "calls update_pg_search_document on each record" do
255
293
  record_1 = Model.create!(active: true)
256
294
  record_2 = Model.create!(active: false)
257
295
 
258
- rebuilder = PgSearch::Multisearch::Rebuilder.new(Model)
296
+ rebuilder = described_class.new(Model)
259
297
 
260
298
  # stub respond_to? to return false since should_not_receive defines the method
261
299
  original_respond_to = Model.method(:respond_to?)
@@ -266,13 +304,17 @@ describe PgSearch::Multisearch::Rebuilder do
266
304
  original_respond_to.call(method_name, *args)
267
305
  end
268
306
  end
269
- expect(Model).not_to receive(:rebuild_pg_search_documents)
270
307
 
271
- rebuilder.rebuild
308
+ without_partial_double_verification do
309
+ allow(Model).to receive(:rebuild_pg_search_documents)
310
+ rebuilder.rebuild
311
+ expect(Model).not_to have_received(:rebuild_pg_search_documents)
312
+ end
272
313
 
273
314
  expect(record_1.pg_search_document).to be_present
274
315
  expect(record_2.pg_search_document).not_to be_present
275
316
  end
317
+ # rubocop:enable RSpec/ExampleLength
276
318
  end
277
319
 
278
320
  context "via :unless" do
@@ -282,16 +324,17 @@ describe PgSearch::Multisearch::Rebuilder do
282
324
  end
283
325
 
284
326
  model do
285
- include PgSearch
327
+ include PgSearch::Model
286
328
  multisearchable unless: :inactive?
287
329
  end
288
330
  end
289
331
 
332
+ # rubocop:disable RSpec/ExampleLength
290
333
  it "calls update_pg_search_document on each record" do
291
334
  record_1 = Model.create!(inactive: true)
292
335
  record_2 = Model.create!(inactive: false)
293
336
 
294
- rebuilder = PgSearch::Multisearch::Rebuilder.new(Model)
337
+ rebuilder = described_class.new(Model)
295
338
 
296
339
  # stub respond_to? to return false since should_not_receive defines the method
297
340
  original_respond_to = Model.method(:respond_to?)
@@ -302,15 +345,20 @@ describe PgSearch::Multisearch::Rebuilder do
302
345
  original_respond_to.call(method_name, *args)
303
346
  end
304
347
  end
305
- expect(Model).not_to receive(:rebuild_pg_search_documents)
306
348
 
307
- rebuilder.rebuild
349
+ without_partial_double_verification do
350
+ allow(Model).to receive(:rebuild_pg_search_documents)
351
+ rebuilder.rebuild
352
+ expect(Model).not_to have_received(:rebuild_pg_search_documents)
353
+ end
308
354
 
309
355
  expect(record_1.pg_search_document).not_to be_present
310
356
  expect(record_2.pg_search_document).to be_present
311
357
  end
358
+ # rubocop:enable RSpec/ExampleLength
312
359
  end
313
360
  end
314
361
  end
315
362
  end
316
363
  end
364
+ # rubocop:enable RSpec/NestedGroups