scoped_search 4.1.7 → 4.1.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 4d5d5ddd90a25434d39644b46bf7ff531a4c3b9f
4
- data.tar.gz: c6f84e3fa52f86b25d03b0ec6a0cb61fb0be79e9
2
+ SHA256:
3
+ metadata.gz: 76592b4cfe81be4cef968ea35cd9659957af349738fb799ce5e34608247b320a
4
+ data.tar.gz: 43e889676986012a6827d6904c616e66e73eecd7a4381ed4e4eb573da270f3f1
5
5
  SHA512:
6
- metadata.gz: e798cdc07468120499ae613122e4a96d6e300998cf3868af27e4df65204af23a28aed861cb3144a7b7bdf44097b5024684b91d0db1a9becdb0397c73d0e39cc6
7
- data.tar.gz: 0da17cd812a2ca5f2802e3cff5835317117426530707d6af6dc1d1d093a9efac13378d1d781b8a1512fd86ca6ab969ef6fe10be6ac6e30356ec988a040cd49d4
6
+ metadata.gz: 6d0d6000ce3a827d33c24591548fcbc6c477c0a9d878dfa39c4a92c5eaaa9bdfe619a5df6c410b2469ebfff4c88a41aef7669afeb591e5d5d6e9a47704bf873c
7
+ data.tar.gz: d9a20afd3c5328f2195b694a3c305a4f00e4a104680fcf8292afb2b1e7cd838da86ce07c03648312d670d010ec7cda38684bc5be24e9841ffbc445dfc5e9e6d8
@@ -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.0"
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
@@ -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 = klass.respond_to?(:completer_scope) ? klass.completer_scope(@options) : klass
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]) 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(find_options[:order]) if find_options[:order]
333
- search_scope = search_scope.references(find_options[:include]) if 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 nil unless timestamp
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
- # Yield the timestamp and return the SQL test
173
- yield(:parameter, timestamp)
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
- yield(:parameter, set_value)
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 [:like, :unlike].include?(operator)
226
- yield(:parameter, (value !~ /^\%|\*/ && value !~ /\%|\*$/) ? "%#{value}%" : value.tr_s('%*', '%'))
227
- return "#{field.to_sql(operator, &block)} #{self.sql_operator(operator, field)} ?"
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
- elsif [:in, :notin].include?(operator)
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
- join = has_many_through_join(field)
248
- return "#{primary_key} IN (SELECT #{primary_key} FROM #{join} WHERE #{field.to_sql(operator, &block)} #{self.sql_operator(operator, field)} ? )"
249
- else
250
- foreign_key = connection.quote_column_name(field.reflection_keys(definition.reflection_by_name(field.definition.klass, field.relation))[1])
251
- return "#{primary_key} IN (SELECT #{foreign_key} FROM #{connection.quote_table_name(field.klass.table_name)} WHERE #{field.to_sql(operator, &block)} #{self.sql_operator(operator, field)} ? )"
252
- end
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)
@@ -1,3 +1,3 @@
1
1
  module ScopedSearch
2
- VERSION = "4.1.7"
2
+ VERSION = "4.1.8"
3
3
  end
@@ -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.7
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: 2019-05-07 00:00:00.000000000 Z
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
- rubyforge_project:
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