ransack 4.3.0 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +4 -2
- data/lib/polyamorous/polyamorous.rb +1 -1
- data/lib/ransack/adapters/active_record/context.rb +30 -3
- data/lib/ransack/context.rb +3 -0
- data/lib/ransack/helpers/form_builder.rb +6 -7
- data/lib/ransack/helpers/form_helper.rb +86 -20
- data/lib/ransack/locale/ja.yml +51 -51
- data/lib/ransack/locale/ko.yml +6 -6
- data/lib/ransack/locale/uk.yml +72 -0
- data/lib/ransack/nodes/condition.rb +36 -6
- data/lib/ransack/nodes/grouping.rb +1 -1
- data/lib/ransack/nodes/sort.rb +1 -1
- data/lib/ransack/nodes/value.rb +9 -1
- data/lib/ransack/search.rb +1 -1
- data/lib/ransack/version.rb +1 -1
- data/lib/ransack.rb +8 -0
- data/spec/polyamorous/join_association_spec.rb +0 -1
- data/spec/polyamorous/join_dependency_spec.rb +0 -1
- data/spec/ransack/adapters/active_record/base_spec.rb +101 -2
- data/spec/ransack/adapters/active_record/context_spec.rb +72 -0
- data/spec/ransack/helpers/form_builder_spec.rb +0 -2
- data/spec/ransack/helpers/form_helper_spec.rb +219 -5
- data/spec/ransack/nodes/condition_spec.rb +229 -0
- data/spec/ransack/nodes/grouping_spec.rb +2 -2
- data/spec/ransack/nodes/value_spec.rb +12 -1
- data/spec/ransack/predicate_spec.rb +0 -1
- data/spec/ransack/search_spec.rb +107 -1
- data/spec/ransack/translate_spec.rb +0 -1
- data/spec/spec_helper.rb +2 -3
- data/spec/support/schema.rb +30 -0
- metadata +41 -87
- data/.github/FUNDING.yml +0 -3
- data/.github/SECURITY.md +0 -12
- data/.github/workflows/codeql.yml +0 -72
- data/.github/workflows/cronjob.yml +0 -141
- data/.github/workflows/deploy.yml +0 -35
- data/.github/workflows/rubocop.yml +0 -20
- data/.github/workflows/test-deploy.yml +0 -29
- data/.github/workflows/test.yml +0 -183
- data/.gitignore +0 -7
- data/.nojekyll +0 -0
- data/.rubocop.yml +0 -50
- data/CHANGELOG.md +0 -1193
- data/CONTRIBUTING.md +0 -171
- data/Gemfile +0 -58
- data/Rakefile +0 -24
- data/bug_report_templates/test-ransack-scope-and-column-same-name.rb +0 -78
- data/bug_report_templates/test-ransacker-arel-present-predicate.rb +0 -75
- data/docs/.gitignore +0 -19
- data/docs/.nojekyll +0 -0
- data/docs/babel.config.js +0 -3
- data/docs/blog/2022-03-27-ransack-3.0.0.md +0 -20
- data/docs/docs/getting-started/_category_.json +0 -4
- data/docs/docs/getting-started/advanced-mode.md +0 -46
- data/docs/docs/getting-started/configuration.md +0 -47
- data/docs/docs/getting-started/search-matches.md +0 -67
- data/docs/docs/getting-started/simple-mode.md +0 -289
- data/docs/docs/getting-started/sorting.md +0 -71
- data/docs/docs/getting-started/using-predicates.md +0 -282
- data/docs/docs/going-further/_category_.json +0 -4
- data/docs/docs/going-further/acts-as-taggable-on.md +0 -114
- data/docs/docs/going-further/associations.md +0 -70
- data/docs/docs/going-further/custom-predicates.md +0 -52
- data/docs/docs/going-further/documentation.md +0 -43
- data/docs/docs/going-further/exporting-to-csv.md +0 -49
- data/docs/docs/going-further/external-guides.md +0 -57
- data/docs/docs/going-further/form-customisation.md +0 -63
- data/docs/docs/going-further/i18n.md +0 -53
- data/docs/docs/going-further/img/create_release.png +0 -0
- data/docs/docs/going-further/merging-searches.md +0 -41
- data/docs/docs/going-further/other-notes.md +0 -425
- data/docs/docs/going-further/polymorphic-search.md +0 -46
- data/docs/docs/going-further/ransackers.md +0 -331
- data/docs/docs/going-further/release_process.md +0 -36
- data/docs/docs/going-further/saving-queries.md +0 -82
- data/docs/docs/going-further/searching-postgres.md +0 -57
- data/docs/docs/going-further/wiki-contributors.md +0 -82
- data/docs/docs/intro.md +0 -99
- data/docs/docusaurus.config.js +0 -120
- data/docs/package.json +0 -42
- data/docs/sidebars.js +0 -31
- data/docs/src/components/HomepageFeatures/index.js +0 -64
- data/docs/src/components/HomepageFeatures/styles.module.css +0 -11
- data/docs/src/css/custom.css +0 -39
- data/docs/src/pages/index.module.css +0 -23
- data/docs/src/pages/markdown-page.md +0 -7
- data/docs/static/.nojekyll +0 -0
- data/docs/static/img/docusaurus.png +0 -0
- data/docs/static/img/favicon.ico +0 -0
- data/docs/static/img/logo.svg +0 -1
- data/docs/static/img/tutorial/docsVersionDropdown.png +0 -0
- data/docs/static/img/tutorial/localeDropdown.png +0 -0
- data/docs/static/img/undraw_docusaurus_mountain.svg +0 -171
- data/docs/static/img/undraw_docusaurus_react.svg +0 -170
- data/docs/static/img/undraw_docusaurus_tree.svg +0 -40
- data/docs/static/logo/ransack-h.png +0 -0
- data/docs/static/logo/ransack-h.svg +0 -34
- data/docs/static/logo/ransack-v.png +0 -0
- data/docs/static/logo/ransack-v.svg +0 -34
- data/docs/static/logo/ransack.png +0 -0
- data/docs/static/logo/ransack.svg +0 -21
- data/docs/yarn.lock +0 -8884
- data/ransack.gemspec +0 -26
@@ -99,6 +99,235 @@ module Ransack
|
|
99
99
|
specify { expect(subject).to eq Condition.extract(Context.for(Person), 'full_name_eq', Person.first.name) }
|
100
100
|
end
|
101
101
|
end
|
102
|
+
|
103
|
+
context 'with wildcard string values' do
|
104
|
+
it 'properly quotes values with wildcards for LIKE predicates' do
|
105
|
+
ransack_hash = { name_cont: 'test%' }
|
106
|
+
sql = Person.ransack(ransack_hash).result.to_sql
|
107
|
+
|
108
|
+
# The % should be properly quoted in the SQL
|
109
|
+
case ActiveRecord::Base.connection.adapter_name
|
110
|
+
when "Mysql2"
|
111
|
+
expect(sql).to include("LIKE '%test\\\\%%'")
|
112
|
+
expect(sql).not_to include("NOT LIKE '%test\\\\%%'")
|
113
|
+
when "PostGIS", "PostgreSQL"
|
114
|
+
expect(sql).to include("ILIKE '%test\\%%'")
|
115
|
+
expect(sql).not_to include("NOT ILIKE '%test\\%%'")
|
116
|
+
else
|
117
|
+
expect(sql).to include("LIKE '%test%%'")
|
118
|
+
expect(sql).not_to include("NOT LIKE '%test%%'")
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'properly quotes values with wildcards for NOT LIKE predicates' do
|
123
|
+
ransack_hash = { name_not_cont: 'test%' }
|
124
|
+
sql = Person.ransack(ransack_hash).result.to_sql
|
125
|
+
|
126
|
+
# The % should be properly quoted in the SQL
|
127
|
+
case ActiveRecord::Base.connection.adapter_name
|
128
|
+
when "Mysql2"
|
129
|
+
expect(sql).to include("NOT LIKE '%test\\\\%%'")
|
130
|
+
when "PostGIS", "PostgreSQL"
|
131
|
+
expect(sql).to include("NOT ILIKE '%test\\%%'")
|
132
|
+
else
|
133
|
+
expect(sql).to include("NOT LIKE '%test%%'")
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context 'with negative conditions on associations' do
|
139
|
+
it 'handles not_null predicate with true value correctly' do
|
140
|
+
ransack_hash = { comments_id_not_null: true }
|
141
|
+
sql = Person.ransack(ransack_hash).result.to_sql
|
142
|
+
|
143
|
+
# Should generate an IN query with IS NOT NULL condition
|
144
|
+
expect(sql).to include('IN (')
|
145
|
+
expect(sql).to include('IS NOT NULL')
|
146
|
+
expect(sql).not_to include('IS NULL')
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'handles not_null predicate with false value correctly' do
|
150
|
+
ransack_hash = { comments_id_not_null: false }
|
151
|
+
sql = Person.ransack(ransack_hash).result.to_sql
|
152
|
+
|
153
|
+
# Should generate a NOT IN query with IS NULL condition
|
154
|
+
expect(sql).to include('NOT IN (')
|
155
|
+
expect(sql).to include('IS NULL')
|
156
|
+
expect(sql).not_to include('IS NOT NULL')
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'handles not_cont predicate correctly' do
|
160
|
+
ransack_hash = { comments_body_not_cont: 'test' }
|
161
|
+
sql = Person.ransack(ransack_hash).result.to_sql
|
162
|
+
|
163
|
+
# Should generate a NOT IN query with LIKE condition (not NOT LIKE)
|
164
|
+
expect(sql).to include('NOT IN (')
|
165
|
+
expect(sql).to include("LIKE '%test%'")
|
166
|
+
expect(sql).not_to include("NOT LIKE '%test%'")
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
context 'with nested conditions' do
|
171
|
+
it 'correctly identifies non-nested conditions' do
|
172
|
+
condition = Condition.extract(
|
173
|
+
Context.for(Person), 'name_eq', 'Test'
|
174
|
+
)
|
175
|
+
|
176
|
+
# Create a mock parent table
|
177
|
+
parent_table = Person.arel_table
|
178
|
+
|
179
|
+
# Get the attribute name and make sure it starts with the table name
|
180
|
+
attribute = condition.attributes.first
|
181
|
+
expect(attribute.name).to eq('name')
|
182
|
+
expect(parent_table.name).to eq('people')
|
183
|
+
|
184
|
+
# The method should return false because 'name' doesn't start with 'people'
|
185
|
+
result = condition.send(:not_nested_condition, attribute, parent_table)
|
186
|
+
expect(result).to be false
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'correctly identifies truly non-nested conditions when attribute name starts with table name' do
|
190
|
+
# Create a condition with an attribute that starts with the table name
|
191
|
+
condition = Condition.extract(
|
192
|
+
Context.for(Person), 'name_eq', 'Test'
|
193
|
+
)
|
194
|
+
|
195
|
+
# Modify the attribute name to start with the table name for testing purposes
|
196
|
+
attribute = condition.attributes.first
|
197
|
+
allow(attribute).to receive(:name).and_return('people_name')
|
198
|
+
|
199
|
+
# Create a parent table
|
200
|
+
parent_table = Person.arel_table
|
201
|
+
|
202
|
+
# Now the method should return true because 'people_name' starts with 'people'
|
203
|
+
result = condition.send(:not_nested_condition, attribute, parent_table)
|
204
|
+
expect(result).to be true
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'correctly identifies nested conditions' do
|
208
|
+
condition = Condition.extract(
|
209
|
+
Context.for(Person), 'articles_title_eq', 'Test'
|
210
|
+
)
|
211
|
+
|
212
|
+
# Create a mock table alias
|
213
|
+
parent_table = Arel::Nodes::TableAlias.new(
|
214
|
+
Article.arel_table,
|
215
|
+
Article.arel_table
|
216
|
+
)
|
217
|
+
|
218
|
+
# Access the private method using send
|
219
|
+
result = condition.send(:not_nested_condition, condition.attributes.first, parent_table)
|
220
|
+
|
221
|
+
# Should return false for nested condition
|
222
|
+
expect(result).to be false
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
context 'with polymorphic associations and not_in predicate' do
|
227
|
+
before do
|
228
|
+
# Define test models for polymorphic associations
|
229
|
+
class ::TestTask < ActiveRecord::Base
|
230
|
+
self.table_name = 'tasks'
|
231
|
+
has_many :follows, primary_key: :uid, inverse_of: :followed, foreign_key: :followed_uid, class_name: 'TestFollow'
|
232
|
+
has_many :users, through: :follows, source: :follower, source_type: 'TestUser'
|
233
|
+
|
234
|
+
# Add ransackable_attributes method
|
235
|
+
def self.ransackable_attributes(auth_object = nil)
|
236
|
+
["created_at", "id", "name", "uid", "updated_at"]
|
237
|
+
end
|
238
|
+
|
239
|
+
# Add ransackable_associations method
|
240
|
+
def self.ransackable_associations(auth_object = nil)
|
241
|
+
["follows", "users"]
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
class ::TestFollow < ActiveRecord::Base
|
246
|
+
self.table_name = 'follows'
|
247
|
+
belongs_to :follower, polymorphic: true, foreign_key: :follower_uid, primary_key: :uid
|
248
|
+
belongs_to :followed, polymorphic: true, foreign_key: :followed_uid, primary_key: :uid
|
249
|
+
|
250
|
+
# Add ransackable_attributes method
|
251
|
+
def self.ransackable_attributes(auth_object = nil)
|
252
|
+
["created_at", "followed_type", "followed_uid", "follower_type", "follower_uid", "id", "updated_at"]
|
253
|
+
end
|
254
|
+
|
255
|
+
# Add ransackable_associations method
|
256
|
+
def self.ransackable_associations(auth_object = nil)
|
257
|
+
["followed", "follower"]
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
class ::TestUser < ActiveRecord::Base
|
262
|
+
self.table_name = 'users'
|
263
|
+
has_many :follows, primary_key: :uid, inverse_of: :follower, foreign_key: :follower_uid, class_name: 'TestFollow'
|
264
|
+
has_many :tasks, through: :follows, source: :followed, source_type: 'TestTask'
|
265
|
+
|
266
|
+
# Add ransackable_attributes method
|
267
|
+
def self.ransackable_attributes(auth_object = nil)
|
268
|
+
["created_at", "id", "name", "uid", "updated_at"]
|
269
|
+
end
|
270
|
+
|
271
|
+
# Add ransackable_associations method
|
272
|
+
def self.ransackable_associations(auth_object = nil)
|
273
|
+
["follows", "tasks"]
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
# Create tables if they don't exist
|
278
|
+
ActiveRecord::Base.connection.create_table(:tasks, force: true) do |t|
|
279
|
+
t.string :uid
|
280
|
+
t.string :name
|
281
|
+
t.timestamps null: false
|
282
|
+
end
|
283
|
+
|
284
|
+
ActiveRecord::Base.connection.create_table(:follows, force: true) do |t|
|
285
|
+
t.string :followed_uid, null: false
|
286
|
+
t.string :followed_type, null: false
|
287
|
+
t.string :follower_uid, null: false
|
288
|
+
t.string :follower_type, null: false
|
289
|
+
t.timestamps null: false
|
290
|
+
t.index [:followed_uid, :followed_type]
|
291
|
+
t.index [:follower_uid, :follower_type]
|
292
|
+
end
|
293
|
+
|
294
|
+
ActiveRecord::Base.connection.create_table(:users, force: true) do |t|
|
295
|
+
t.string :uid
|
296
|
+
t.string :name
|
297
|
+
t.timestamps null: false
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
after do
|
302
|
+
# Clean up test models and tables
|
303
|
+
Object.send(:remove_const, :TestTask)
|
304
|
+
Object.send(:remove_const, :TestFollow)
|
305
|
+
Object.send(:remove_const, :TestUser)
|
306
|
+
|
307
|
+
ActiveRecord::Base.connection.drop_table(:tasks, if_exists: true)
|
308
|
+
ActiveRecord::Base.connection.drop_table(:follows, if_exists: true)
|
309
|
+
ActiveRecord::Base.connection.drop_table(:users, if_exists: true)
|
310
|
+
end
|
311
|
+
|
312
|
+
it 'correctly handles not_in predicate with polymorphic associations' do
|
313
|
+
# Create the search
|
314
|
+
search = TestTask.ransack(users_uid_not_in: ['uid_example'])
|
315
|
+
sql = search.result.to_sql
|
316
|
+
|
317
|
+
# Verify the SQL contains the expected NOT IN clause
|
318
|
+
expect(sql).to include('NOT IN')
|
319
|
+
expect(sql).to include("follower_uid")
|
320
|
+
expect(sql).to include("followed_uid")
|
321
|
+
expect(sql).to include("'uid_example'")
|
322
|
+
|
323
|
+
# The SQL should include a reference to tasks.uid
|
324
|
+
expect(sql).to include("tasks")
|
325
|
+
expect(sql).to include("uid")
|
326
|
+
|
327
|
+
# The SQL should include a reference to follows table
|
328
|
+
expect(sql).to include("follows")
|
329
|
+
end
|
330
|
+
end
|
102
331
|
end
|
103
332
|
end
|
104
333
|
end
|
@@ -3,7 +3,6 @@ require 'spec_helper'
|
|
3
3
|
module Ransack
|
4
4
|
module Nodes
|
5
5
|
describe Grouping do
|
6
|
-
|
7
6
|
before do
|
8
7
|
@g = 1
|
9
8
|
end
|
@@ -66,6 +65,7 @@ module Ransack
|
|
66
65
|
}
|
67
66
|
}
|
68
67
|
end
|
68
|
+
|
69
69
|
before { subject.conditions = conditions }
|
70
70
|
|
71
71
|
it 'expect duplicates to be removed' do
|
@@ -98,6 +98,7 @@ module Ransack
|
|
98
98
|
}
|
99
99
|
}
|
100
100
|
end
|
101
|
+
|
101
102
|
before { subject.conditions = conditions }
|
102
103
|
|
103
104
|
it 'expect them to be parsed as different and not as duplicates' do
|
@@ -105,7 +106,6 @@ module Ransack
|
|
105
106
|
end
|
106
107
|
end
|
107
108
|
end
|
108
|
-
|
109
109
|
end
|
110
110
|
end
|
111
111
|
end
|
@@ -71,6 +71,18 @@ module Ransack
|
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
74
|
+
[[], ["12"], ["101.5"]].each do |value|
|
75
|
+
context "with an array value (#{value.inspect})" do
|
76
|
+
let(:raw_value) { value }
|
77
|
+
|
78
|
+
it "should cast to integer as nil" do
|
79
|
+
result = subject.cast(:integer)
|
80
|
+
|
81
|
+
expect(result).to be nil
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
74
86
|
["12", "101.5"].each do |value|
|
75
87
|
context "with a float value (#{value})" do
|
76
88
|
let(:raw_value) { value }
|
@@ -109,7 +121,6 @@ module Ransack
|
|
109
121
|
end
|
110
122
|
end
|
111
123
|
end
|
112
|
-
|
113
124
|
end
|
114
125
|
end
|
115
126
|
end
|
data/spec/ransack/search_spec.rb
CHANGED
@@ -178,7 +178,6 @@ module Ransack
|
|
178
178
|
# AND "articles"."title" = 'Test' AND "articles"."published" = 't' AND ('default_scope' = 'default_scope')
|
179
179
|
# ) ORDER BY "people"."id" DESC
|
180
180
|
|
181
|
-
pending("spec should pass, but I do not know how/where to fix lib code")
|
182
181
|
s = Search.new(Person, published_articles_title_not_eq: 'Test')
|
183
182
|
expect(s.result.to_sql).to include 'default_scope'
|
184
183
|
expect(s.result.to_sql).to include 'published'
|
@@ -337,18 +336,90 @@ module Ransack
|
|
337
336
|
end
|
338
337
|
end
|
339
338
|
|
339
|
+
context "ransackable_scope with array arguments" do
|
340
|
+
around(:each) do |example|
|
341
|
+
Person.define_singleton_method(:domestic) do |countries|
|
342
|
+
self.where(name: countries)
|
343
|
+
end
|
344
|
+
|
345
|
+
Person.define_singleton_method(:flexible_scope) do |*args|
|
346
|
+
self.where(id: args)
|
347
|
+
end
|
348
|
+
|
349
|
+
Person.define_singleton_method(:two_param_scope) do |param1, param2|
|
350
|
+
self.where(name: param1, id: param2)
|
351
|
+
end
|
352
|
+
|
353
|
+
begin
|
354
|
+
example.run
|
355
|
+
ensure
|
356
|
+
Person.singleton_class.undef_method :domestic
|
357
|
+
Person.singleton_class.undef_method :flexible_scope
|
358
|
+
Person.singleton_class.undef_method :two_param_scope
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
it "handles scopes that take arrays as single arguments (arity 1)" do
|
363
|
+
allow(Person).to receive(:ransackable_scopes)
|
364
|
+
.and_return(Person.ransackable_scopes + [:domestic])
|
365
|
+
|
366
|
+
# This should not raise ArgumentError
|
367
|
+
expect {
|
368
|
+
s = Search.new(Person, domestic: ['US', 'JP'])
|
369
|
+
s.result # This triggers the actual scope call
|
370
|
+
}.not_to raise_error
|
371
|
+
|
372
|
+
s = Search.new(Person, domestic: ['US', 'JP'])
|
373
|
+
expect(s.instance_variable_get(:@scope_args)["domestic"]).to eq("US")
|
374
|
+
end
|
375
|
+
|
376
|
+
it "handles scopes with flexible arity (negative arity)" do
|
377
|
+
allow(Person).to receive(:ransackable_scopes)
|
378
|
+
.and_return(Person.ransackable_scopes + [:flexible_scope])
|
379
|
+
|
380
|
+
expect {
|
381
|
+
s = Search.new(Person, flexible_scope: ['US', 'JP'])
|
382
|
+
s.result
|
383
|
+
}.not_to raise_error
|
384
|
+
end
|
385
|
+
|
386
|
+
it "handles scopes with arity > 1" do
|
387
|
+
allow(Person).to receive(:ransackable_scopes)
|
388
|
+
.and_return(Person.ransackable_scopes + [:two_param_scope])
|
389
|
+
|
390
|
+
expect {
|
391
|
+
s = Search.new(Person, two_param_scope: ['param1', 'param2'])
|
392
|
+
s.result
|
393
|
+
}.not_to raise_error
|
394
|
+
end
|
395
|
+
|
396
|
+
it "still supports the workaround with nested arrays" do
|
397
|
+
allow(Person).to receive(:ransackable_scopes)
|
398
|
+
.and_return(Person.ransackable_scopes + [:domestic])
|
399
|
+
|
400
|
+
# The workaround from the issue should still work
|
401
|
+
expect {
|
402
|
+
s = Search.new(Person, domestic: [['US', 'JP']])
|
403
|
+
s.result
|
404
|
+
}.not_to raise_error
|
405
|
+
end
|
406
|
+
end
|
340
407
|
end
|
341
408
|
|
342
409
|
describe '#result' do
|
343
410
|
let(:people_name_field) {
|
344
411
|
"#{quote_table_name("people")}.#{quote_column_name("name")}"
|
345
412
|
}
|
413
|
+
let(:people_temperament_field) {
|
414
|
+
"#{quote_table_name("people")}.#{quote_column_name("temperament")}"
|
415
|
+
}
|
346
416
|
let(:children_people_name_field) {
|
347
417
|
"#{quote_table_name("children_people")}.#{quote_column_name("name")}"
|
348
418
|
}
|
349
419
|
let(:notable_type_field) {
|
350
420
|
"#{quote_table_name("notes")}.#{quote_column_name("notable_type")}"
|
351
421
|
}
|
422
|
+
|
352
423
|
it 'evaluates conditions contextually' do
|
353
424
|
s = Search.new(Person, children_name_eq: 'Ernie')
|
354
425
|
expect(s.result).to be_an ActiveRecord::Relation
|
@@ -356,6 +427,36 @@ module Ransack
|
|
356
427
|
children_people_name_field} = 'Ernie'/
|
357
428
|
end
|
358
429
|
|
430
|
+
context 'when evaluating enums' do
|
431
|
+
before do
|
432
|
+
Person.take.update_attribute(:temperament, 'choleric')
|
433
|
+
end
|
434
|
+
|
435
|
+
it 'evaluates enum key correctly' do
|
436
|
+
s = Search.new(Person, temperament_eq: 'choleric')
|
437
|
+
|
438
|
+
expect(s.result.to_sql).not_to match /#{
|
439
|
+
people_temperament_field} = 0/
|
440
|
+
|
441
|
+
expect(s.result.to_sql).to match /#{
|
442
|
+
people_temperament_field} = #{Person.temperaments[:choleric]}/
|
443
|
+
|
444
|
+
expect(s.result).not_to be_empty
|
445
|
+
end
|
446
|
+
|
447
|
+
it 'evaluates enum value correctly' do
|
448
|
+
s = Search.new(Person, temperament_eq: Person.temperaments[:choleric])
|
449
|
+
|
450
|
+
expect(s.result.to_sql).not_to match /#{
|
451
|
+
people_temperament_field} = 0/
|
452
|
+
|
453
|
+
expect(s.result.to_sql).to match /#{
|
454
|
+
people_temperament_field} = #{Person.temperaments[:choleric]}/
|
455
|
+
|
456
|
+
expect(s.result).not_to be_empty
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
359
460
|
it 'use appropriate table alias' do
|
360
461
|
s = Search.new(Person, {
|
361
462
|
name_eq: "person_name_query",
|
@@ -479,6 +580,11 @@ module Ransack
|
|
479
580
|
@s = Search.new(Person)
|
480
581
|
end
|
481
582
|
|
583
|
+
it 'doesn\'t creates sorts' do
|
584
|
+
@s.sorts = ''
|
585
|
+
expect(@s.sorts.size).to eq(0)
|
586
|
+
end
|
587
|
+
|
482
588
|
it 'creates sorts based on a single attribute/direction' do
|
483
589
|
@s.sorts = 'id desc'
|
484
590
|
expect(@s.sorts.size).to eq(1)
|
data/spec/spec_helper.rb
CHANGED
@@ -1,13 +1,12 @@
|
|
1
|
-
require '
|
2
|
-
require 'polyamorous/polyamorous'
|
1
|
+
require 'ransack'
|
3
2
|
require 'sham'
|
4
3
|
require 'faker'
|
5
|
-
require 'ransack'
|
6
4
|
require 'action_controller'
|
7
5
|
require 'ransack/helpers'
|
8
6
|
require 'pry'
|
9
7
|
require 'simplecov'
|
10
8
|
require 'byebug'
|
9
|
+
require 'machinist/active_record'
|
11
10
|
|
12
11
|
SimpleCov.start
|
13
12
|
I18n.enforce_available_locales = false
|
data/spec/support/schema.rb
CHANGED
@@ -81,6 +81,8 @@ class Person < ApplicationRecord
|
|
81
81
|
scope :sort_by_reverse_name_asc, lambda { order(Arel.sql("REVERSE(name) ASC")) }
|
82
82
|
scope :sort_by_reverse_name_desc, lambda { order("REVERSE(name) DESC") }
|
83
83
|
|
84
|
+
enum :temperament, { sanguine: 1, choleric: 2, melancholic: 3, phlegmatic: 4 }
|
85
|
+
|
84
86
|
alias_attribute :full_name, :name
|
85
87
|
|
86
88
|
ransack_alias :term, :name_or_email
|
@@ -138,6 +140,17 @@ class Person < ApplicationRecord
|
|
138
140
|
Arel.sql(query)
|
139
141
|
end
|
140
142
|
|
143
|
+
ransacker :article_tags, formatter: proc { |id|
|
144
|
+
if Tag.exists?(id)
|
145
|
+
joins(articles: :tags)
|
146
|
+
.where(tags: { id: id })
|
147
|
+
.distinct
|
148
|
+
.select(:id).arel
|
149
|
+
end
|
150
|
+
} do |parent|
|
151
|
+
parent.table[:id]
|
152
|
+
end
|
153
|
+
|
141
154
|
def self.ransackable_attributes(auth_object = nil)
|
142
155
|
if auth_object == :admin
|
143
156
|
authorizable_ransackable_attributes - ['only_sort']
|
@@ -163,6 +176,7 @@ class Article < ApplicationRecord
|
|
163
176
|
has_many :comments
|
164
177
|
has_and_belongs_to_many :tags
|
165
178
|
has_many :notes, as: :notable
|
179
|
+
has_many :recent_notes, as: :notable
|
166
180
|
|
167
181
|
alias_attribute :content, :body
|
168
182
|
|
@@ -232,18 +246,28 @@ end
|
|
232
246
|
class Comment < ApplicationRecord
|
233
247
|
belongs_to :article
|
234
248
|
belongs_to :person
|
249
|
+
has_and_belongs_to_many :tags
|
235
250
|
|
236
251
|
default_scope { where(disabled: false) }
|
237
252
|
end
|
238
253
|
|
239
254
|
class Tag < ApplicationRecord
|
240
255
|
has_and_belongs_to_many :articles
|
256
|
+
has_and_belongs_to_many :comments
|
241
257
|
end
|
242
258
|
|
243
259
|
class Note < ApplicationRecord
|
244
260
|
belongs_to :notable, polymorphic: true
|
245
261
|
end
|
246
262
|
|
263
|
+
class RecentNote < ApplicationRecord
|
264
|
+
DEFAULT_NOTABLE_ID = 1
|
265
|
+
self.table_name = "notes"
|
266
|
+
default_scope { where(notable_id: DEFAULT_NOTABLE_ID) }
|
267
|
+
|
268
|
+
belongs_to :notable, polymorphic: true
|
269
|
+
end
|
270
|
+
|
247
271
|
class Account < ApplicationRecord
|
248
272
|
belongs_to :agent_account, class_name: "Account"
|
249
273
|
belongs_to :trade_account, class_name: "Account"
|
@@ -278,6 +302,7 @@ module Schema
|
|
278
302
|
t.string :new_start
|
279
303
|
t.string :stop_end
|
280
304
|
t.integer :salary
|
305
|
+
t.integer :temperament
|
281
306
|
t.date :life_start
|
282
307
|
t.boolean :awesome, default: false
|
283
308
|
t.boolean :terms_and_conditions, default: false
|
@@ -310,6 +335,11 @@ module Schema
|
|
310
335
|
t.integer :tag_id
|
311
336
|
end
|
312
337
|
|
338
|
+
create_table :comments_tags, force: true, id: false do |t|
|
339
|
+
t.integer :comment_id
|
340
|
+
t.integer :tag_id
|
341
|
+
end
|
342
|
+
|
313
343
|
create_table :notes, force: true do |t|
|
314
344
|
t.integer :notable_id
|
315
345
|
t.string :notable_type
|