scoped_search 4.1.7 → 4.1.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +26 -2
- data/CHANGELOG.rdoc +5 -0
- data/Gemfile.activerecord60 +17 -0
- data/Gemfile.activerecord60_with_activesupport60 +18 -0
- data/lib/scoped_search/auto_complete_builder.rb +2 -2
- data/lib/scoped_search/definition.rb +5 -5
- data/lib/scoped_search/query_builder.rb +40 -40
- data/lib/scoped_search/version.rb +1 -1
- data/spec/integration/sti_querying_spec.rb +12 -2
- data/spec/unit/query_builder_spec.rb +2 -0
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 76592b4cfe81be4cef968ea35cd9659957af349738fb799ce5e34608247b320a
|
4
|
+
data.tar.gz: 43e889676986012a6827d6904c616e66e73eecd7a4381ed4e4eb573da270f3f1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d0d6000ce3a827d33c24591548fcbc6c477c0a9d878dfa39c4a92c5eaaa9bdfe619a5df6c410b2469ebfff4c88a41aef7669afeb591e5d5d6e9a47704bf873c
|
7
|
+
data.tar.gz: d9a20afd3c5328f2195b694a3c305a4f00e4a104680fcf8292afb2b1e7cd838da86ce07c03648312d670d010ec7cda38684bc5be24e9841ffbc445dfc5e9e6d8
|
data/.travis.yml
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
language: ruby
|
2
2
|
cache: bundler
|
3
3
|
sudo: false
|
4
|
+
services:
|
5
|
+
- postgresql
|
6
|
+
- mysql
|
4
7
|
|
5
8
|
install:
|
6
9
|
- bundle install
|
@@ -16,7 +19,7 @@ rvm:
|
|
16
19
|
- "2.2.2"
|
17
20
|
- "2.3.7"
|
18
21
|
- "2.4.0"
|
19
|
-
- "2.5.
|
22
|
+
- "2.5.1"
|
20
23
|
- "2.6.0"
|
21
24
|
- ruby-head
|
22
25
|
- jruby-19mode
|
@@ -28,6 +31,8 @@ gemfile:
|
|
28
31
|
- Gemfile.activerecord51
|
29
32
|
- Gemfile.activerecord52
|
30
33
|
- Gemfile.activerecord52_with_activesupport52
|
34
|
+
- Gemfile.activerecord60
|
35
|
+
- Gemfile.activerecord60_with_activesupport60
|
31
36
|
|
32
37
|
matrix:
|
33
38
|
allow_failures:
|
@@ -51,4 +56,23 @@ matrix:
|
|
51
56
|
gemfile: Gemfile.activerecord52_with_activesupport52
|
52
57
|
- rvm: "2.1"
|
53
58
|
gemfile: Gemfile.activerecord52_with_activesupport52
|
54
|
-
|
59
|
+
- rvm: "2.0"
|
60
|
+
gemfile: Gemfile.activerecord60
|
61
|
+
- rvm: "2.1"
|
62
|
+
gemfile: Gemfile.activerecord60
|
63
|
+
- rvm: "2.2.2"
|
64
|
+
gemfile: Gemfile.activerecord60
|
65
|
+
- rvm: "2.3.7"
|
66
|
+
gemfile: Gemfile.activerecord60
|
67
|
+
- rvm: "2.4.0"
|
68
|
+
gemfile: Gemfile.activerecord60
|
69
|
+
- rvm: "2.0"
|
70
|
+
gemfile: Gemfile.activerecord60_with_activesupport60
|
71
|
+
- rvm: "2.1"
|
72
|
+
gemfile: Gemfile.activerecord60_with_activesupport60
|
73
|
+
- rvm: "2.2.2"
|
74
|
+
gemfile: Gemfile.activerecord60_with_activesupport60
|
75
|
+
- rvm: "2.3.7"
|
76
|
+
gemfile: Gemfile.activerecord60_with_activesupport60
|
77
|
+
- rvm: "2.4.0"
|
78
|
+
gemfile: Gemfile.activerecord60_with_activesupport60
|
data/CHANGELOG.rdoc
CHANGED
@@ -8,6 +8,11 @@ Please add an entry to the "Unreleased changes" section in your pull requests.
|
|
8
8
|
|
9
9
|
- Nothing yet
|
10
10
|
|
11
|
+
=== Version 4.1.8
|
12
|
+
|
13
|
+
- Fix querying in associations by set, datetime or IN searches
|
14
|
+
- Add support for ActiveRecord 6.0 and Rails 6.1
|
15
|
+
|
11
16
|
=== Version 4.1.7
|
12
17
|
|
13
18
|
- When Active Support is available, we parse time respecting current time zone
|
@@ -0,0 +1,17 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
gemspec
|
3
|
+
|
4
|
+
gem 'actionview', '~> 6.0.0'
|
5
|
+
gem 'activerecord', '~> 6.0.0'
|
6
|
+
|
7
|
+
platforms :jruby do
|
8
|
+
gem 'activerecord-jdbcsqlite3-adapter'
|
9
|
+
gem 'activerecord-jdbcmysql-adapter'
|
10
|
+
gem 'activerecord-jdbcpostgresql-adapter'
|
11
|
+
end
|
12
|
+
|
13
|
+
platforms :ruby do
|
14
|
+
gem 'sqlite3', '~> 1.4'
|
15
|
+
gem 'mysql2', '> 0.5'
|
16
|
+
gem 'pg', '>= 0.18', '< 2.0'
|
17
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
gemspec
|
3
|
+
|
4
|
+
gem 'actionview', '~> 6.0.0'
|
5
|
+
gem 'activerecord', '~> 6.0.0'
|
6
|
+
gem 'activesupport', '~> 6.0.0'
|
7
|
+
|
8
|
+
platforms :jruby do
|
9
|
+
gem 'activerecord-jdbcsqlite3-adapter'
|
10
|
+
gem 'activerecord-jdbcmysql-adapter'
|
11
|
+
gem 'activerecord-jdbcpostgresql-adapter'
|
12
|
+
end
|
13
|
+
|
14
|
+
platforms :ruby do
|
15
|
+
gem 'sqlite3', '~> 1.4'
|
16
|
+
gem 'mysql2', '> 0.5'
|
17
|
+
gem 'pg', '>= 0.18', '< 2.0'
|
18
|
+
end
|
@@ -220,8 +220,8 @@ module ScopedSearch
|
|
220
220
|
|
221
221
|
def completer_scope(field)
|
222
222
|
klass = field.klass
|
223
|
-
scope =
|
224
|
-
scope.respond_to?(:reorder) ? scope.reorder(field.quoted_field) : scope.scoped(:order => field.quoted_field)
|
223
|
+
scope = klass.respond_to?(:completer_scope) ? klass.completer_scope(@options) : klass
|
224
|
+
scope.respond_to?(:reorder) ? scope.reorder(Arel.sql(field.quoted_field)) : scope.scoped(:order => field.quoted_field)
|
225
225
|
end
|
226
226
|
|
227
227
|
# set value completer
|
@@ -326,11 +326,11 @@ module ScopedSearch
|
|
326
326
|
|
327
327
|
search_scope = klass.all
|
328
328
|
find_options = ScopedSearch::QueryBuilder.build_query(definition, query || '', options)
|
329
|
-
search_scope = search_scope.where(find_options[:conditions])
|
330
|
-
search_scope = search_scope.includes(find_options[:include])
|
331
|
-
search_scope = search_scope.joins(find_options[:joins])
|
332
|
-
search_scope = search_scope.reorder(find_options[:order])
|
333
|
-
search_scope = search_scope.references(find_options[:include])
|
329
|
+
search_scope = search_scope.where(find_options[:conditions]) if find_options[:conditions]
|
330
|
+
search_scope = search_scope.includes(find_options[:include]) if find_options[:include]
|
331
|
+
search_scope = search_scope.joins(find_options[:joins]) if find_options[:joins]
|
332
|
+
search_scope = search_scope.reorder(Arel.sql(find_options[:order])) if find_options[:order]
|
333
|
+
search_scope = search_scope.references(find_options[:include]) if find_options[:include]
|
334
334
|
|
335
335
|
search_scope
|
336
336
|
end
|
@@ -137,7 +137,7 @@ module ScopedSearch
|
|
137
137
|
|
138
138
|
# Parse the value as a date/time and ignore invalid timestamps
|
139
139
|
timestamp = definition.parse_temporal(value)
|
140
|
-
return
|
140
|
+
return [] unless timestamp
|
141
141
|
|
142
142
|
timestamp = timestamp.to_date if field.date?
|
143
143
|
# Check for the case that a date-only value is given as search keyword,
|
@@ -149,11 +149,9 @@ module ScopedSearch
|
|
149
149
|
if [:eq, :ne].include?(operator)
|
150
150
|
# Instead of looking for an exact (non-)match, look for dates that
|
151
151
|
# fall inside/outside the range of timestamps of that day.
|
152
|
-
yield(:parameter, timestamp)
|
153
|
-
yield(:parameter, timestamp + span)
|
154
152
|
negate = (operator == :ne) ? 'NOT ' : ''
|
155
153
|
field_sql = field.to_sql(operator, &block)
|
156
|
-
return "#{negate}(#{field_sql} >= ? AND #{field_sql} < ?)"
|
154
|
+
return ["#{negate}(#{field_sql} >= ? AND #{field_sql} < ?)", timestamp, timestamp + span]
|
157
155
|
|
158
156
|
elsif operator == :gt
|
159
157
|
# Make sure timestamps on the given date are not included in the results
|
@@ -169,9 +167,8 @@ module ScopedSearch
|
|
169
167
|
end
|
170
168
|
end
|
171
169
|
|
172
|
-
#
|
173
|
-
|
174
|
-
"#{field.to_sql(operator, &block)} #{sql_operator(operator, field)} ?"
|
170
|
+
# return the SQL test
|
171
|
+
["#{field.to_sql(operator, &block)} #{sql_operator(operator, field)} ?", timestamp]
|
175
172
|
end
|
176
173
|
|
177
174
|
# Validate the key name is in the set and translate the value to the set value.
|
@@ -205,8 +202,7 @@ module ScopedSearch
|
|
205
202
|
set_value = false
|
206
203
|
end
|
207
204
|
end
|
208
|
-
|
209
|
-
return "#{negate}(#{field.to_sql(operator, &block)} #{self.sql_operator(operator, field)} ?)"
|
205
|
+
["#{negate}(#{field.to_sql(operator, &block)} #{self.sql_operator(operator, field)} ?)", set_value]
|
210
206
|
end
|
211
207
|
|
212
208
|
# Generates a simple SQL test expression, for a field and value using an operator.
|
@@ -222,41 +218,45 @@ module ScopedSearch
|
|
222
218
|
|
223
219
|
yield(:keyparameter, lhs.sub(/^.*\./,'')) if field.key_field
|
224
220
|
|
225
|
-
if
|
226
|
-
|
227
|
-
|
221
|
+
condition, *values = if field.temporal?
|
222
|
+
datetime_test(field, operator, value, &block)
|
223
|
+
elsif field.set?
|
224
|
+
set_test(field, operator, value, &block)
|
225
|
+
else
|
226
|
+
["#{field.to_sql(operator, &block)} #{self.sql_operator(operator, field)} #{value_placeholders(operator, value)}", value]
|
227
|
+
end
|
228
|
+
values.each { |value| preprocess_parameters(field, operator, value, &block) }
|
228
229
|
|
229
|
-
|
230
|
-
value.split(',').collect { |v| yield(:parameter, map_value(field, field.set? ? translate_value(field, v) : v.strip)) }
|
231
|
-
value = value.split(',').collect { "?" }.join(",")
|
232
|
-
return "#{field.to_sql(operator, &block)} #{self.sql_operator(operator, field)} (#{value})"
|
233
|
-
|
234
|
-
elsif field.temporal?
|
235
|
-
return datetime_test(field, operator, value, &block)
|
236
|
-
|
237
|
-
elsif field.set?
|
238
|
-
return set_test(field, operator, value, &block)
|
239
|
-
|
240
|
-
elsif field.relation && definition.reflection_by_name(field.definition.klass, field.relation).macro == :has_many
|
241
|
-
value = value.to_i if field.offset
|
242
|
-
value = map_value(field, value)
|
243
|
-
yield(:parameter, value)
|
230
|
+
if field.relation && definition.reflection_by_name(field.definition.klass, field.relation).macro == :has_many
|
244
231
|
connection = field.definition.klass.connection
|
245
232
|
primary_key = "#{connection.quote_table_name(field.definition.klass.table_name)}.#{connection.quote_column_name(field.definition.klass.primary_key)}"
|
246
|
-
if definition.reflection_by_name(field.definition.klass, field.relation).options.has_key?(:through)
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
else
|
255
|
-
value = value.to_i if field.offset
|
256
|
-
value = map_value(field, value)
|
257
|
-
yield(:parameter, value)
|
258
|
-
return "#{field.to_sql(operator, &block)} #{self.sql_operator(operator, field)} ?"
|
233
|
+
key, join_table = if definition.reflection_by_name(field.definition.klass, field.relation).options.has_key?(:through)
|
234
|
+
[primary_key, has_many_through_join(field)]
|
235
|
+
else
|
236
|
+
[connection.quote_column_name(field.reflection_keys(definition.reflection_by_name(field.definition.klass, field.relation))[1]),
|
237
|
+
connection.quote_table_name(field.klass.table_name)]
|
238
|
+
end
|
239
|
+
|
240
|
+
condition = "#{primary_key} IN (SELECT #{key} FROM #{join_table} WHERE #{condition} )"
|
259
241
|
end
|
242
|
+
condition
|
243
|
+
end
|
244
|
+
|
245
|
+
def preprocess_parameters(field, operator, value, &block)
|
246
|
+
values = if [:in, :notin].include?(operator)
|
247
|
+
value.split(',').map { |v| map_value(field, field.set? ? translate_value(field, v) : v.strip) }
|
248
|
+
elsif [:like, :unlike].include?(operator)
|
249
|
+
[(value !~ /^\%|\*/ && value !~ /\%|\*$/) ? "%#{value}%" : value.tr_s('%*', '%')]
|
250
|
+
else
|
251
|
+
[map_value(field, field.offset ? value.to_i : value)]
|
252
|
+
end
|
253
|
+
values.each { |value| yield(:parameter, value) }
|
254
|
+
end
|
255
|
+
|
256
|
+
def value_placeholders(operator, value)
|
257
|
+
return '?' unless [:in, :notin].include?(operator)
|
258
|
+
|
259
|
+
'(' + value.split(',').map { '?' }.join(',') + ')'
|
260
260
|
end
|
261
261
|
|
262
262
|
def find_has_many_through_association(field, through)
|
@@ -13,15 +13,18 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
|
|
13
13
|
|
14
14
|
@parent_class = ScopedSearch::RSpec::Database.create_model(int: :integer, type: :string, related_id: :integer) do |klass|
|
15
15
|
klass.scoped_search on: :int
|
16
|
+
klass.belongs_to @related_class.table_name.to_sym, foreign_key: :related_id
|
16
17
|
end
|
17
18
|
@subclass1 = ScopedSearch::RSpec::Database.create_sti_model(@parent_class)
|
18
19
|
@subclass2 = ScopedSearch::RSpec::Database.create_sti_model(@parent_class) do |klass|
|
19
|
-
klass.belongs_to @related_class.table_name.to_sym, foreign_key: :related_id
|
20
20
|
klass.scoped_search on: :int, rename: :other_int
|
21
21
|
klass.scoped_search relation: @related_class.table_name, on: :int, rename: :related_int
|
22
22
|
end
|
23
23
|
|
24
|
-
@related_class.has_many @subclass1.table_name.to_sym
|
24
|
+
@related_class.has_many @subclass1.table_name.to_sym, :foreign_key => :related_id
|
25
|
+
@related_class.has_many @subclass2.table_name.to_sym, :foreign_key => :related_id
|
26
|
+
@related_class.scoped_search :relation => @subclass1.table_name.to_sym, :on => :int, :rename => 'subclass1.id'
|
27
|
+
@related_class.scoped_search :relation => @subclass2.table_name.to_sym, :on => :int, :rename => 'subclass2.id'
|
25
28
|
|
26
29
|
@record1 = @subclass1.create!(int: 7)
|
27
30
|
@related_record1 = @related_class.create!(int: 42)
|
@@ -79,5 +82,12 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
|
|
79
82
|
@subclass2.search_for('related_int = 42').should eq([@record2])
|
80
83
|
end
|
81
84
|
end
|
85
|
+
|
86
|
+
context 'querying related records' do
|
87
|
+
it 'shuld find only relevant instances of STI subclasses' do
|
88
|
+
@related_class.search_for("subclass1.id ^ (#{@record1.int})").should eq([])
|
89
|
+
@related_class.search_for("subclass2.id ^ (#{@record1.int}, #{@record2.int})").should eq([@related_record1])
|
90
|
+
end
|
91
|
+
end
|
82
92
|
end
|
83
93
|
end
|
@@ -52,6 +52,8 @@ describe ScopedSearch::QueryBuilder do
|
|
52
52
|
it "should validate value if validator selected" do
|
53
53
|
field = double('field')
|
54
54
|
field.stub(:virtual?).and_return(false)
|
55
|
+
field.stub(:temporal?).and_return(false)
|
56
|
+
field.stub(:relation).and_return(nil)
|
55
57
|
field.stub(:only_explicit).and_return(true)
|
56
58
|
field.stub(:field).and_return(:test_field)
|
57
59
|
field.stub(:ext_method).and_return(nil)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scoped_search
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.1.
|
4
|
+
version: 4.1.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Amos Benari
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2020-04-15 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
@@ -87,6 +87,8 @@ files:
|
|
87
87
|
- Gemfile.activerecord51
|
88
88
|
- Gemfile.activerecord52
|
89
89
|
- Gemfile.activerecord52_with_activesupport52
|
90
|
+
- Gemfile.activerecord60
|
91
|
+
- Gemfile.activerecord60_with_activesupport60
|
90
92
|
- LICENSE
|
91
93
|
- README.rdoc
|
92
94
|
- Rakefile
|
@@ -159,8 +161,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
159
161
|
- !ruby/object:Gem::Version
|
160
162
|
version: '0'
|
161
163
|
requirements: []
|
162
|
-
|
163
|
-
rubygems_version: 2.6.8
|
164
|
+
rubygems_version: 3.0.3
|
164
165
|
signing_key:
|
165
166
|
specification_version: 4
|
166
167
|
summary: Easily search you ActiveRecord models with a simple query language using
|