scoped_search 3.3.0 → 4.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8d62827c492fb99894adbd0b01e0a1341c6744a5
4
- data.tar.gz: 2ee3ede28bcd6b96e12ca1a7629b7cbef38aee4d
3
+ metadata.gz: df5ac5265b984d6208df025b20dd336950d9bdbb
4
+ data.tar.gz: 3643c8835b43e6333e24d44473b1b29c3ec7ac91
5
5
  SHA512:
6
- metadata.gz: 9e26efdc791ad2fde5e975f44c8512a5089c4774f41f299e68f2c03fab3657323be4668e0668deb86f448dce2b02fe92b95d8d180cfff76b6f7dacd6e4bb01df
7
- data.tar.gz: 206daf327aa65a9de4b2ac8ed049620b4df5c4b71330b846413c9140d0f83aed95f612db37d9154d624d2f8c3a54a8d9fd785e41a76fa9e223b0b4e9b81a7497
6
+ metadata.gz: 7d760759a261eda13744cad755263a056213315859bc925e5ea814cd0e214e19941dc6b3e07572f80b0bf10058359d6895f663a9249b894fa53297c9f81a7efa
7
+ data.tar.gz: 4b9580f1b55aeaf6f74b64847318361d58f1e3a7477376b1db4921861ec95db3ba8594f7a6f5915f097fdd9313b2fb9bd30b26afc53c2199d3f367d8fa54c336
@@ -11,7 +11,6 @@ script:
11
11
  - bundle exec rake
12
12
 
13
13
  rvm:
14
- - "1.9"
15
14
  - "2.0"
16
15
  - "2.1"
17
16
  - "2.2.2"
@@ -21,9 +20,6 @@ rvm:
21
20
  - jruby-head
22
21
 
23
22
  gemfile:
24
- - Gemfile.activerecord32
25
- - Gemfile.activerecord40
26
- - Gemfile.activerecord41
27
23
  - Gemfile.activerecord42
28
24
  - Gemfile.activerecord50
29
25
 
@@ -33,11 +29,7 @@ matrix:
33
29
  - rvm: jruby-head
34
30
  - rvm: jruby-19mode
35
31
  exclude:
36
- - rvm: "1.9"
37
- gemfile: Gemfile.activerecord50
38
32
  - rvm: "2.0"
39
33
  gemfile: Gemfile.activerecord50
40
34
  - rvm: "2.1"
41
35
  gemfile: Gemfile.activerecord50
42
- - rvm: "2.3.1"
43
- gemfile: Gemfile.activerecord32
@@ -8,6 +8,26 @@ Please add an entry to the "Unreleased changes" section in your pull requests.
8
8
 
9
9
  *Nothing yet*
10
10
 
11
+ === Version 4.0.0
12
+
13
+ - Drop support for Ruby 1.9
14
+ - Drop support for ActiveRecord 3, 4.0, and 4.1
15
+ - `scoped_search` registration deprecates `:in` in favor of `:relation`, and
16
+ `:alias` in favor of `aliases: [..]` to avoid clash with Ruby reserved words.
17
+ - `sort` helper: any HTML options must now be given as a keyword argument,
18
+ instead of the third argument, i.e. `html_options: {..}`
19
+ - `sort` helper now takes hash of `url_options`, defaulting to an unfiltered
20
+ `params`. Call `params.permit(..)` and pass in the result to safely generate
21
+ URLs and prevent non-sanitized errors under Rails 5.
22
+ - Auto completion: remove autocomplete_* JavaScript helpers in favor of jQuery
23
+ method provided via asset pipeline. Call `$(..).scopedSearch` on autocomplete
24
+ text boxes to activate.
25
+ - Auto completion: escape quotes in listed values (#138)
26
+ - Add :validator option to definitions to validate user's search inputs
27
+ - Fix incorrect association conditions being used on through relations
28
+ - Use `#distinct` on Rails 4+
29
+ - Test Rails 5.0.x releases, test latest AR adapters
30
+
11
31
  === Version 3.3.0
12
32
 
13
33
  - Support for ActiveRecord 5.0
@@ -10,7 +10,7 @@ platforms :jruby do
10
10
  end
11
11
 
12
12
  platforms :ruby do
13
- gem 'sqlite3'
14
- gem 'mysql2', '~> 0.3.11'
15
- gem 'pg'
13
+ gem 'sqlite3', '~> 1.3.6'
14
+ gem 'mysql2', '>= 0.3.13', '< 0.5'
15
+ gem 'pg', '~> 0.15'
16
16
  end
@@ -1,7 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
  gemspec
3
3
 
4
- gem 'activerecord', '5.0.0.rc1'
4
+ gem 'activerecord', '~> 5.0.0'
5
5
 
6
6
  platforms :jruby do
7
7
  gem 'activerecord-jdbcsqlite3-adapter'
@@ -10,7 +10,7 @@ platforms :jruby do
10
10
  end
11
11
 
12
12
  platforms :ruby do
13
- gem 'sqlite3'
14
- gem 'mysql2', '~> 0.3.11'
15
- gem 'pg'
13
+ gem 'sqlite3', '~> 1.3.6'
14
+ gem 'mysql2', '>= 0.3.18', '< 0.5'
15
+ gem 'pg', '~> 0.18'
16
16
  end
@@ -22,6 +22,9 @@ Add the following line in your Gemfile, and run <tt>bundle install</tt>:
22
22
 
23
23
  gem "scoped_search"
24
24
 
25
+ Scoped search 4.x supports Rails 4.2 to 5.0, with Ruby 2.0.0 or higher. Use
26
+ previous versions, e.g. 3.x to support older versions of Rails or Ruby.
27
+
25
28
  == Usage
26
29
 
27
30
  Scoped search requires you to define the fields you want to search in:
@@ -65,6 +65,7 @@ require 'scoped_search/definition'
65
65
  require 'scoped_search/query_language'
66
66
  require 'scoped_search/query_builder'
67
67
  require 'scoped_search/auto_complete_builder'
68
+ require 'scoped_search/validators'
68
69
 
69
70
  # Import the search_on method in the ActiveReocrd::Base class
70
71
  ActiveRecord::Base.send(:extend, ScopedSearch::ClassMethods)
@@ -178,7 +178,7 @@ module ScopedSearch
178
178
  .where(value_conditions(field_name, val))
179
179
  .select(field_name)
180
180
  .limit(20)
181
- .uniq
181
+ .distinct
182
182
  .map(&field.key_field)
183
183
  .compact
184
184
  .map { |f| "#{name}.#{f} " }
@@ -205,10 +205,10 @@ module ScopedSearch
205
205
  .where(value_conditions(field.quoted_field, val))
206
206
  .select(field.quoted_field)
207
207
  .limit(20)
208
- .uniq
208
+ .distinct
209
209
  .map(&field.field)
210
210
  .compact
211
- .map { |v| v.to_s =~ /\s/ ? "\"#{v}\"" : v }
211
+ .map { |v| v.to_s =~ /\s/ ? "\"#{v.gsub('"', '\"')}\"" : v }
212
212
  end
213
213
 
214
214
  def completer_scope(field)
@@ -16,44 +16,76 @@ module ScopedSearch
16
16
  class Field
17
17
 
18
18
  attr_reader :definition, :field, :only_explicit, :relation, :key_relation, :full_text_search,
19
- :key_field, :complete_value, :complete_enabled, :offset, :word_size, :ext_method, :operators
19
+ :key_field, :complete_value, :complete_enabled, :offset, :word_size, :ext_method, :operators,
20
+ :validator
20
21
 
21
22
  # Initializes a Field instance given the definition passed to the
22
23
  # scoped_search call on the ActiveRecord-based model class.
23
- def initialize(definition, options = {})
24
- @definition = definition
25
- @definition.profile = options[:profile] if options[:profile]
26
- @definition.default_order ||= default_order(options)
27
-
28
- case options
29
- when Symbol, String
30
- @field = field.to_sym
31
- when Hash
32
- @field = options.delete(:on)
33
-
34
- # Set attributes from options hash
35
- @complete_value = options[:complete_value]
36
- @relation = options[:in]
37
- @key_relation = options[:in_key]
38
- @key_field = options[:on_key]
39
- @offset = options[:offset]
40
- @word_size = options[:word_size] || 1
41
- @ext_method = options[:ext_method]
42
- @operators = options[:operators]
43
- @only_explicit = !!options[:only_explicit]
44
- @full_text_search = options[:full_text_search]
45
- @default_operator = options[:default_operator] if options.has_key?(:default_operator)
46
- @complete_enabled = options[:complete_enabled].nil? ? true : options[:complete_enabled]
24
+ #
25
+ # Field name may be given in positional 'field' argument or 'on' named
26
+ # argument.
27
+ def initialize(definition,
28
+ field = nil,
29
+ aliases: [],
30
+ complete_enabled: true,
31
+ complete_value: nil,
32
+ default_operator: nil,
33
+ default_order: nil,
34
+ ext_method: nil,
35
+ full_text_search: nil,
36
+ in_key: nil,
37
+ offset: nil,
38
+ on: field,
39
+ on_key: nil,
40
+ only_explicit: nil,
41
+ operators: nil,
42
+ profile: nil,
43
+ relation: nil,
44
+ rename: nil,
45
+ validator: nil,
46
+ word_size: 1,
47
+ **kwargs)
48
+
49
+ # Prefer 'on' kw arg if given, defaults to the 'field' positional to allow either syntax
50
+ raise ArgumentError, "Missing field or 'on' keyword argument" if on.nil?
51
+ @field = on.to_sym
52
+
53
+ # Reserved Ruby keywords so access via kwargs instead, but deprecate them for future versions
54
+ if kwargs.key?(:in)
55
+ relation = kwargs.delete(:in)
56
+ ActiveSupport::Deprecation.warn("'in' argument deprecated, prefer 'relation' since scoped_search 4.0.0", caller(6))
47
57
  end
58
+ if kwargs.key?(:alias)
59
+ aliases += [kwargs.delete(:alias)]
60
+ ActiveSupport::Deprecation.warn("'alias' argument deprecated, prefer aliases: [..] since scoped_search 4.0.0", caller(6))
61
+ end
62
+ raise ArgumentError, "Unknown arguments to scoped_search: #{kwargs.keys.join(', ')}" unless kwargs.empty?
48
63
 
49
- # Store this field is the field array
50
- definition.fields[@field] ||= self unless options[:rename]
51
- definition.fields[options[:rename].to_sym] ||= self if options[:rename]
52
- definition.unique_fields << self
53
-
54
- # Store definition for alias / aliases as well
55
- definition.fields[options[:alias].to_sym] ||= self if options[:alias]
56
- options[:aliases].each { |al| definition.fields[al.to_sym] ||= self } if options[:aliases]
64
+ @definition = definition
65
+ @definition.profile = profile if profile
66
+ @definition.default_order ||= generate_default_order(default_order, rename || @field) if default_order
67
+
68
+ # Set attributes from keyword arguments
69
+ @complete_enabled = complete_enabled
70
+ @complete_value = complete_value
71
+ @default_operator = default_operator
72
+ @ext_method = ext_method
73
+ @full_text_search = full_text_search
74
+ @key_field = on_key
75
+ @key_relation = in_key
76
+ @offset = offset
77
+ @only_explicit = !!only_explicit
78
+ @operators = operators
79
+ @relation = relation
80
+ @validator = validator
81
+ @word_size = word_size
82
+
83
+ # Store this field in the field array
84
+ definition.fields[rename ? rename.to_sym : @field] ||= self
85
+ definition.unique_fields << self
86
+
87
+ # Store definition for aliases as well
88
+ aliases.each { |al| definition.fields[al.to_sym] ||= self }
57
89
  end
58
90
 
59
91
  # The ActiveRecord-based class that belongs to this field.
@@ -82,11 +114,7 @@ module ScopedSearch
82
114
  if klass.columns_hash.has_key?(field.to_s)
83
115
  klass.columns_hash[field.to_s]
84
116
  else
85
- if "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}".to_f < 4.1
86
- raise ActiveRecord::UnknownAttributeError, "#{klass.inspect} doesn't have column #{field.inspect}."
87
- else
88
- raise ActiveRecord::UnknownAttributeError.new(klass, field)
89
- end
117
+ raise ActiveRecord::UnknownAttributeError.new(klass, field)
90
118
  end
91
119
  end
92
120
  end
@@ -135,11 +163,9 @@ module ScopedSearch
135
163
  end
136
164
  end
137
165
 
138
- def default_order(options)
139
- return nil if options[:default_order].nil?
140
- field_name = options[:rename].nil? ? options[:on] : options[:rename]
141
- order = (options[:default_order].to_s.downcase.include?('desc')) ? "DESC" : "ASC"
142
- return "#{field_name} #{order}"
166
+ def generate_default_order(default_order, field)
167
+ order = (default_order.to_s.downcase.include?('desc')) ? "DESC" : "ASC"
168
+ return "#{field} #{order}"
143
169
  end
144
170
 
145
171
  # Return 'table'.'column' with the correct database quotes
@@ -234,8 +260,8 @@ module ScopedSearch
234
260
  end
235
261
 
236
262
  # Defines a new search field for this search definition.
237
- def define(options)
238
- Field.new(self, options)
263
+ def define(*args)
264
+ Field.new(self, *args)
239
265
  end
240
266
 
241
267
  # Returns a reflection for a given klass and name
@@ -252,24 +278,13 @@ module ScopedSearch
252
278
  @klass.scope(:search_for, proc { |query, options|
253
279
  klass = definition.klass
254
280
 
255
- search_scope = case ActiveRecord::VERSION::MAJOR
256
- when 3
257
- klass.scoped
258
- when 4
259
- (ActiveRecord::VERSION::MINOR < 1) ? klass.where(nil) : klass.all
260
- when 5
261
- klass.all
262
- else
263
- raise ScopedSearch::DefinitionError, 'version ' \
264
- "#{ActiveRecord::VERSION::MAJOR} of activerecord is not supported"
265
- end
266
-
281
+ search_scope = klass.all
267
282
  find_options = ScopedSearch::QueryBuilder.build_query(definition, query || '', options || {})
268
283
  search_scope = search_scope.where(find_options[:conditions]) if find_options[:conditions]
269
284
  search_scope = search_scope.includes(find_options[:include]) if find_options[:include]
270
285
  search_scope = search_scope.joins(find_options[:joins]) if find_options[:joins]
271
286
  search_scope = search_scope.reorder(find_options[:order]) if find_options[:order]
272
- search_scope = search_scope.references(find_options[:include]) if find_options[:include] && ActiveRecord::VERSION::MAJOR >= 4
287
+ search_scope = search_scope.references(find_options[:include]) if find_options[:include]
273
288
 
274
289
  search_scope
275
290
  })
@@ -251,9 +251,9 @@ module ScopedSearch
251
251
 
252
252
  def find_has_many_through_association(field, through)
253
253
  middle_table_association = nil
254
- field.klass.reflect_on_all_associations(:has_many).each do |reflection|
254
+ field.klass.reflect_on_all_associations(:has_many).each do |reflection|
255
255
  class_name = reflection.options[:class_name].constantize.table_name if reflection.options[:class_name]
256
- middle_table_association = reflection.name if class_name == through.to_s
256
+ middle_table_association = reflection.name if class_name == through.to_s
257
257
  middle_table_association = reflection.plural_name if reflection.plural_name == through.to_s
258
258
  end
259
259
  middle_table_association
@@ -271,7 +271,7 @@ module ScopedSearch
271
271
 
272
272
  # primary and foreign keys + optional condition for the many to middle join
273
273
  pk1, fk1 = field.reflection_keys(definition.reflection_by_name(many_class, through))
274
- condition1 = field.reflection_conditions(definition.reflection_by_name(field.klass, many_table_name))
274
+ condition1 = field.reflection_conditions(definition.reflection_by_name(field.klass, middle_table_name))
275
275
 
276
276
  # primary and foreign keys + optional condition for the endpoint to middle join
277
277
  middle_table_association = find_has_many_through_association(field, through) || middle_table_name
@@ -468,6 +468,10 @@ module ScopedSearch
468
468
  # Search only on the given field.
469
469
  field = definition.field_by_name(lhs.value)
470
470
  raise ScopedSearch::QueryNotSupported, "Field '#{lhs.value}' not recognized for searching!" unless field
471
+
472
+ # see if the value passes user defined validation
473
+ validate_value(field, rhs.value)
474
+
471
475
  builder.sql_test(field, operator, rhs.value,lhs.value, &block)
472
476
  end
473
477
 
@@ -485,6 +489,16 @@ module ScopedSearch
485
489
  raise ScopedSearch::QueryNotSupported, "Don't know how to handle this operator node: #{operator.inspect} with #{children.inspect}!"
486
490
  end
487
491
  end
492
+
493
+ private
494
+
495
+ def validate_value(field, value)
496
+ validator = field.validator
497
+ if validator
498
+ valid = validator.call(value)
499
+ raise ScopedSearch::QueryNotSupported, "Value '#{value}' is not valid for field '#{field.field}'" unless valid
500
+ end
501
+ end
488
502
  end
489
503
 
490
504
  # Defines the to_sql method for AST AND/OR operators
@@ -4,20 +4,31 @@ module ScopedSearch
4
4
  #
5
5
  # Examples:
6
6
  #
7
- # sort @search, :by => :username
8
- # sort @search, :by => :created_at, :as => "Created"
9
- # sort @search, :by => :created_at, :default => "DESC"
7
+ # sort :username
8
+ # sort :created_at, as: "Created"
9
+ # sort :created_at, default: "DESC"
10
+ #
11
+ # * <tt>field</tt> - the name of the named scope. This helper will prepend this value with "ascend_by_" and "descend_by_"
10
12
  #
11
13
  # This helper accepts the following options:
12
14
  #
13
- # * <tt>:by</tt> - the name of the named scope. This helper will prepend this value with "ascend_by_" and "descend_by_"
14
- # * <tt>:as</tt> - the text used in the link, defaults to whatever is passed to :by
15
+ # * <tt>:as</tt> - the text used in the link, defaults to whatever is passed to `field`
15
16
  # * <tt>:default</tt> - default sorting order, DESC or ASC
16
- def sort(field, options = {}, html_options = {})
17
+ # * <tt>:html_options</tt> - is a hash of HTML options for the anchor tag
18
+ # * <tt>:url_options</tt> - is a hash of URL parameters, defaulting to `params`, to preserve the current URL
19
+ # parameters.
20
+ #
21
+ # On Rails 5 or higher, parameter whitelisting prevents any parameter being used in a link by
22
+ # default, so `params.permit(..)` should be passed for `url_options` for all known and
23
+ # permitted URL parameters, e.g.
24
+ #
25
+ # sort :username, url_options: params.permit(:search)
26
+ #
27
+ def sort(field, as: nil, default: "ASC", html_options: {}, url_options: params)
17
28
 
18
- unless options[:as]
19
- id = field.to_s.downcase == "id"
20
- options[:as] = id ? field.to_s.upcase : field.to_s.humanize
29
+ unless as
30
+ id = field.to_s.downcase == "id"
31
+ as = id ? field.to_s.upcase : field.to_s.humanize
21
32
  end
22
33
 
23
34
  ascend = "#{field} ASC"
@@ -30,151 +41,27 @@ module ScopedSearch
30
41
  when descend
31
42
  new_sort = ascend
32
43
  else
33
- new_sort = ["ASC", "DESC"].include?(options[:default]) ? "#{field} #{options[:default]}" : ascend
44
+ new_sort = ["ASC", "DESC"].include?(default) ? "#{field} #{default}" : ascend
34
45
  end
35
46
 
36
47
  unless selected_sort.nil?
37
48
  css_classes = html_options[:class] ? html_options[:class].split(" ") : []
38
49
  if selected_sort == ascend
39
- options[:as] = "&#9650;&nbsp;#{options[:as]}"
50
+ as = "&#9650;&nbsp;#{as}"
40
51
  css_classes << "ascending"
41
52
  else
42
- options[:as] = "&#9660;&nbsp;#{options[:as]}"
53
+ as = "&#9660;&nbsp;#{as}"
43
54
  css_classes << "descending"
44
55
  end
45
56
  html_options[:class] = css_classes.join(" ")
46
57
  end
47
58
 
48
- url_options = params.merge(:order => new_sort)
49
-
50
- options[:as] = raw(options[:as]) if defined?(RailsXss)
51
-
52
- a_link(options[:as], html_escape(url_for(url_options)),html_options)
53
- end
54
-
55
- # Adds AJAX auto complete functionality to the text input field with the
56
- # DOM ID specified by +field_id+.
57
- #
58
- # Required +options+ is:
59
- # <tt>:url</tt>:: URL to call for auto completion results
60
- # in url_for format.
61
- #
62
- # Additional +options+ are:
63
- # <tt>:update</tt>:: Specifies the DOM ID of the element whose
64
- # innerHTML should be updated with the auto complete
65
- # entries returned by the AJAX request.
66
- # Defaults to <tt>field_id</tt> + '_auto_complete'
67
- # <tt>:with</tt>:: A JavaScript expression specifying the
68
- # parameters for the XMLHttpRequest. This defaults
69
- # to 'fieldname=value'.
70
- # <tt>:frequency</tt>:: Determines the time to wait after the last keystroke
71
- # for the AJAX request to be initiated.
72
- # <tt>:indicator</tt>:: Specifies the DOM ID of an element which will be
73
- # displayed while auto complete is running.
74
- # <tt>:tokens</tt>:: A string or an array of strings containing
75
- # separator tokens for tokenized incremental
76
- # auto completion. Example: <tt>:tokens => ','</tt> would
77
- # allow multiple auto completion entries, separated
78
- # by commas.
79
- # <tt>:min_chars</tt>:: The minimum number of characters that should be
80
- # in the input field before an Ajax call is made
81
- # to the server.
82
- # <tt>:on_hide</tt>:: A Javascript expression that is called when the
83
- # auto completion div is hidden. The expression
84
- # should take two variables: element and update.
85
- # Element is a DOM element for the field, update
86
- # is a DOM element for the div from which the
87
- # innerHTML is replaced.
88
- # <tt>:on_show</tt>:: Like on_hide, only now the expression is called
89
- # then the div is shown.
90
- # <tt>:after_update_element</tt>:: A Javascript expression that is called when the
91
- # user has selected one of the proposed values.
92
- # The expression should take two variables: element and value.
93
- # Element is a DOM element for the field, value
94
- # is the value selected by the user.
95
- # <tt>:select</tt>:: Pick the class of the element from which the value for
96
- # insertion should be extracted. If this is not specified,
97
- # the entire element is used.
98
- # <tt>:method</tt>:: Specifies the HTTP verb to use when the auto completion
99
- # request is made. Defaults to POST.
100
- def auto_complete_field(field_id, options = {})
101
- function = "var #{field_id}_auto_completer = new Ajax.Autocompleter("
102
- function << "'#{field_id}', "
103
- function << "'" + (options[:update] || "#{field_id}_auto_complete") + "', "
104
- function << "'#{url_for(options[:url])}'"
105
-
106
- js_options = {}
107
- js_options[:tokens] = array_or_string_for_javascript(options[:tokens]) if options[:tokens]
108
- js_options[:callback] = "function(element, value) { return #{options[:with]} }" if options[:with]
109
- js_options[:indicator] = "'#{options[:indicator]}'" if options[:indicator]
110
- js_options[:select] = "'#{options[:select]}'" if options[:select]
111
- js_options[:paramName] = "'#{options[:param_name]}'" if options[:param_name]
112
- js_options[:frequency] = "#{options[:frequency]}" if options[:frequency]
113
- js_options[:method] = "'#{options[:method].to_s}'" if options[:method]
114
-
115
- { :after_update_element => :afterUpdateElement,
116
- :on_show => :onShow, :on_hide => :onHide, :min_chars => :minChars }.each do |k,v|
117
- js_options[v] = options[k] if options[k]
118
- end
59
+ url_options = url_options.to_h if url_options.respond_to?(:permit) # convert ActionController::Parameters if given
60
+ url_options = url_options.merge(:order => new_sort)
119
61
 
120
- function << (', ' + options_for_javascript(js_options) + ')')
62
+ as = raw(as) if defined?(RailsXss)
121
63
 
122
- javascript_tag(function)
123
- end
124
-
125
- def auto_complete_field_jquery(method, url, options = {})
126
- function = <<-JAVASCRIPT
127
- $.widget( "custom.catcomplete", $.ui.autocomplete, {
128
- _renderMenu: function( ul, items ) {
129
- var self = this,
130
- currentCategory = "";
131
- $.each( items, function( index, item ) {
132
- if ( item.category != undefined && item.category != currentCategory ) {
133
- ul.append( "<li class='ui-autocomplete-category'>" + item.category + "</li>" );
134
- currentCategory = item.category;
135
- }
136
- if ( item.error != undefined ) {
137
- ul.append( "<li class='ui-autocomplete-error'>" + item.error + "</li>" );
138
- }
139
- if( item.completed != undefined ) {
140
- $( "<li></li>" ).data( "item.autocomplete", item )
141
- .append( "<a>" + "<strong class='ui-autocomplete-completed'>" + item.completed + "</strong>" + item.part + "</a>" )
142
- .appendTo( ul );
143
- } else {
144
- if(typeof(self._renderItemData) === "function") {
145
- self._renderItemData( ul, item );
146
- } else {
147
- self._renderItem( ul, item );
148
- }
149
- }
150
- });
151
- }
152
- });
153
-
154
- $("##{method}")
155
- .catcomplete({
156
- source: function( request, response ) { $.getJSON( "#{url}", { #{method}: request.term }, response ); },
157
- minLength: #{options[:min_length] || 0},
158
- delay: #{options[:delay] || 200},
159
- select: function(event, ui) { $( this ).catcomplete( "search" , ui.item.value); },
160
- search: function(event, ui) { $(".auto_complete_clear").hide(); },
161
- open: function(event, ui) { $(".auto_complete_clear").show(); }
162
- });
163
-
164
- $("##{method}").bind( "focus", function( event ) {
165
- if( $( this )[0].value == "" ) {
166
- $( this ).catcomplete( "search" );
167
- }
168
- });
169
-
170
- JAVASCRIPT
171
-
172
- javascript_tag(function)
173
- end
174
-
175
- def auto_complete_clear_value_button(field_id)
176
- html_options = {:tabindex => '-1',:class=>"auto_complete_clear",:title =>'Clear Search', :onclick=>"document.getElementById('#{field_id}').value = '';"}
177
- a_link("", "#", html_options)
64
+ a_link(as, html_escape(url_for(url_options)),html_options)
178
65
  end
179
66
 
180
67
  def a_link(name, href, html_options)
@@ -182,41 +69,5 @@ module ScopedSearch
182
69
  link = "<a href=\"#{href}\"#{tag_options}>#{name}</a>"
183
70
  return link.respond_to?(:html_safe) ? link.html_safe : link
184
71
  end
185
-
186
- # Use this method in your view to generate a return for the AJAX auto complete requests.
187
- #
188
- # The auto_complete_result can of course also be called from a view belonging to the
189
- # auto_complete action if you need to decorate it further.
190
- def auto_complete_result(entries, phrase = nil)
191
- return unless entries
192
- items = entries.map { |entry| content_tag("li", phrase ? highlight(entry, phrase) : h(entry)) }
193
- content_tag("ul", items)
194
- end
195
-
196
- # Wrapper for text_field with added AJAX auto completion functionality.
197
- #
198
- # In your controller, you'll need to define an action called
199
- # auto_complete_method to respond the AJAX calls,
200
- def auto_complete_field_tag(method, val,tag_options = {}, completion_options = {})
201
- auto_completer_options = { :url => { :action => "auto_complete_#{method}" } }.update(completion_options)
202
- options = tag_options.merge(:class => "auto_complete_input " << tag_options[:class].to_s)
203
- text_field_tag(method, val,options) +
204
- auto_complete_clear_value_button(method) +
205
- content_tag("div", "", :id => "#{method}_auto_complete", :class => "auto_complete") +
206
- auto_complete_field(method, auto_completer_options)
207
- end
208
-
209
- # Wrapper for text_field with added JQuery auto completion functionality.
210
- #
211
- # In your controller, you'll need to define an action called
212
- # auto_complete_method to respond the JQuery calls,
213
- def auto_complete_field_tag_jquery(method, val,tag_options = {}, completion_options = {})
214
- url = url_for(:action => "auto_complete_#{method}", :filter => completion_options[:filter])
215
- options = tag_options.merge(:class => "auto_complete_input " << tag_options[:class].to_s)
216
- text_field_tag(method, val, options) + auto_complete_clear_value_button(method) +
217
- auto_complete_field_jquery(method, url, completion_options)
218
- end
219
- deprecate :auto_complete_field_tag_jquery, :auto_complete_field_tag, :auto_complete_result
220
-
221
72
  end
222
73
  end
@@ -0,0 +1,7 @@
1
+ module ScopedSearch
2
+ # This class will be used to store standard validators.
3
+ class Validators
4
+ NUMERIC = ->(value) { !!(value =~ ScopedSearch::Definition::NUMERICAL_REGXP) }
5
+ INTEGER = ->(value) { !!(value =~ ScopedSearch::Definition::INTEGER_REGXP) }
6
+ end
7
+ end
@@ -1,3 +1,3 @@
1
1
  module ScopedSearch
2
- VERSION = "3.3.0"
2
+ VERSION = "4.0.0"
3
3
  end
@@ -29,8 +29,8 @@ Gem::Specification.new do |gem|
29
29
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
30
30
  gem.require_paths = ["lib"]
31
31
 
32
- gem.required_ruby_version = '>= 1.9.3'
33
- gem.add_runtime_dependency('activerecord', '>= 3.2.0')
32
+ gem.required_ruby_version = '>= 2.0.0'
33
+ gem.add_runtime_dependency('activerecord', '>= 4.2.0')
34
34
  gem.add_development_dependency('rspec', '~> 3.0')
35
35
  gem.add_development_dependency('rake')
36
36
 
@@ -37,20 +37,20 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
37
37
 
38
38
  scoped_search :on => [:string, :date]
39
39
  scoped_search :on => [:int], :complete_value => true
40
- scoped_search :on => :another, :default_operator => :eq, :alias => :alias
40
+ scoped_search :on => :another, :default_operator => :eq, :aliases => [:alias], :complete_value => true
41
41
  scoped_search :on => :explicit, :only_explicit => true, :complete_value => true
42
42
  scoped_search :on => :deprecated, :complete_enabled => false
43
- scoped_search :on => :related, :in => :bars, :rename => 'bars.related'.to_sym
44
- scoped_search :on => :other_a, :in => :bars, :rename => 'bars.other_a'.to_sym
45
- scoped_search :on => :other_b, :in => :bars, :rename => 'bars.other_b'.to_sym
46
- scoped_search :on => :other_c, :in => :bars, :rename => 'bars.other_c'.to_sym
43
+ scoped_search :on => :related, :relation => :bars, :rename => 'bars.related'.to_sym
44
+ scoped_search :on => :other_a, :relation => :bars, :rename => 'bars.other_a'.to_sym
45
+ scoped_search :on => :other_b, :relation => :bars, :rename => 'bars.other_b'.to_sym
46
+ scoped_search :on => :other_c, :relation => :bars, :rename => 'bars.other_c'.to_sym
47
47
  end
48
48
 
49
49
  class ::Infoo < ::Foo
50
50
  end
51
51
 
52
52
  @foo_1 = Foo.create!(:string => 'foo', :another => 'temp 1', :explicit => 'baz', :int => 9 , :date => 'February 8, 2011' , :unindexed => 10)
53
- Foo.create!(:string => 'bar', :another => 'temp 2', :explicit => 'baz', :int => 22 , :date => 'February 10, 2011', :unindexed => 10)
53
+ Foo.create!(:string => 'bar', :another => 'temp "2"', :explicit => 'baz', :int => 22 , :date => 'February 10, 2011', :unindexed => 10)
54
54
  Foo.create!(:string => 'baz', :another => nil, :explicit => nil , :int => nil, :date => nil , :unindexed => nil)
55
55
  20.times { Foo.create!(:explicit => "aaa") }
56
56
 
@@ -143,6 +143,10 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
143
143
  it "should complete values should contain baz" do
144
144
  Foo.complete_for('explicit = ').should contain('explicit = baz')
145
145
  end
146
+
147
+ it "should complete values with quotes where required" do
148
+ Foo.complete_for('alias = ').should contain('alias = "temp \"2\""')
149
+ end
146
150
  end
147
151
 
148
152
  context 'auto complete relations' do
@@ -32,7 +32,7 @@ require "spec_helper"
32
32
  has_many :facts
33
33
  has_many :keys, :through => :facts
34
34
 
35
- scoped_search :in => :facts, :on => :value, :rename => :facts, :in_key => :keys, :on_key => :name, :complete_value => true
35
+ scoped_search :relation => :facts, :on => :value, :rename => :facts, :in_key => :keys, :on_key => :name, :complete_value => true
36
36
  end
37
37
  class ::MyItem < ::Item
38
38
  end
@@ -114,16 +114,6 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
114
114
  @class.search_for('date = 09-01-02').length.should == 1
115
115
  end
116
116
 
117
- if RUBY_VERSION.to_f == 1.8
118
- it "should accept MM/DD/YY as date format" do
119
- @class.search_for('date = 01/02/09').length.should == 1
120
- end
121
-
122
- it "should accept MM/DD/YYYY as date format" do
123
- @class.search_for('date = 01/02/2009').length.should == 1
124
- end
125
- end
126
-
127
117
  it "should accept YYYY/MM/DD as date format" do
128
118
  @class.search_for('date = 2009/01/02').length.should == 1
129
119
  end
@@ -46,7 +46,7 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
46
46
  ActiveRecord::Migration.create_table(:loos) { |t| t.string :foo; t.integer :har_id }
47
47
  class Loo < ActiveRecord::Base
48
48
  belongs_to :har
49
- scoped_search :in => :har, :on => :related
49
+ scoped_search :relation => :har, :on => :related
50
50
  end
51
51
 
52
52
  @har_record = Har.create!(:related => 'bar')
@@ -93,7 +93,7 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
93
93
  class Goo < ActiveRecord::Base
94
94
  has_many :jars
95
95
  scoped_search :on => :foo
96
- scoped_search :in => :jars, :on => :related
96
+ scoped_search :relation => :jars, :on => :related
97
97
  end
98
98
 
99
99
  @foo_1 = Goo.create!(:foo => 'foo')
@@ -158,7 +158,7 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
158
158
  class Hoo < ActiveRecord::Base
159
159
  has_one :car
160
160
  scoped_search :on => :foo
161
- scoped_search :in => :car, :on => :related
161
+ scoped_search :relation => :car, :on => :related
162
162
  end
163
163
 
164
164
  @hoo_1 = Hoo.create!(:foo => 'foo')
@@ -206,7 +206,7 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
206
206
  # The class on which to call search_for
207
207
  class Joo < ActiveRecord::Base
208
208
  has_and_belongs_to_many :dars
209
- scoped_search :in => :dars, :on => :related
209
+ scoped_search :relation => :dars, :on => :related
210
210
  end
211
211
 
212
212
  @joo_1 = Joo.create!(:foo => 'foo')
@@ -265,7 +265,7 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
265
265
  # as an indication for a polymorphic relation.
266
266
  has_many :bazs, :through => :mars, :source => :baz
267
267
 
268
- scoped_search :in => :bazs, :on => :related
268
+ scoped_search :relation => :bazs, :on => :related
269
269
  end
270
270
 
271
271
  @koo_1 = Koo.create!(:foo => 'foo')
@@ -316,7 +316,7 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
316
316
  belongs_to :zar
317
317
  has_many :bazs, :through => :zar
318
318
 
319
- scoped_search :in => :bazs, :on => :related
319
+ scoped_search :relation => :bazs, :on => :related
320
320
  end
321
321
 
322
322
  baz_1 = Baz.create(:related => 'baz')
@@ -362,7 +362,7 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
362
362
  has_many :taggables
363
363
  has_many :dogs, :through => :taggables, :source => :taggable, :source_type => 'Dog'
364
364
 
365
- scoped_search :in => :dogs, :on => :related, :rename => :dog
365
+ scoped_search :relation => :dogs, :on => :related, :rename => :dog
366
366
  end
367
367
 
368
368
  # The class on which to call search_for
@@ -370,7 +370,7 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
370
370
  has_many :taggables, :as => :taggable
371
371
  has_many :tags, :through => :taggables
372
372
 
373
- scoped_search :in => :tags, :on => :foo
373
+ scoped_search :relation => :tags, :on => :foo
374
374
  end
375
375
 
376
376
  class Cat < ActiveRecord::Base
@@ -438,7 +438,7 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
438
438
  has_many :zaps
439
439
  has_many :pazs, :through => :zaps
440
440
 
441
- scoped_search :in => :pazs, :on => :related
441
+ scoped_search :relation => :pazs, :on => :related
442
442
  end
443
443
 
444
444
  @moo_1 = Moo.create!(:foo => 'foo')
@@ -471,6 +471,59 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
471
471
  end
472
472
  end
473
473
 
474
+ context 'querying a :has_many => :through relation with same name on target class with custom condition' do
475
+
476
+ before do
477
+
478
+ # Create some tables
479
+ ActiveRecord::Migration.create_table(:user_groups) { |t| t.integer :user_id; t.integer :group_id }
480
+ ActiveRecord::Migration.create_table(:conflicts) { |t| t.integer :group_id; t.integer :user_id }
481
+ ActiveRecord::Migration.create_table(:groups) { |t| t.string :related; t.integer :user_id }
482
+ ActiveRecord::Migration.create_table(:users) { |t| t.string :foo }
483
+
484
+ # The related classes
485
+ class UserGroup < ActiveRecord::Base; belongs_to :user; belongs_to :group; end
486
+ class Conflict < ActiveRecord::Base; belongs_to :user; belongs_to :group; end
487
+ class Group < ActiveRecord::Base
488
+ has_many :user_groups
489
+ has_many :users, :through => :conflicts, :source_type => 'User', :source => :user
490
+ end
491
+
492
+ # The class on which to call search_for
493
+ class User < ActiveRecord::Base
494
+ has_many :user_groups
495
+ has_many :groups, :through => :user_groups
496
+
497
+ scoped_search :relation => :groups, :on => :related
498
+ end
499
+
500
+ @user_1 = User.create!(:foo => 'foo')
501
+ @user_2 = User.create!(:foo => 'foo too')
502
+ @user_3 = User.create!(:foo => 'foo three')
503
+
504
+ @group_1 = Group.create(:related => 'value')
505
+ @group_2 = Group.create(:related => 'value too!')
506
+
507
+ @bar_1 = UserGroup.create!(:user => @user_1, :group => @group_1)
508
+ @bar_2 = UserGroup.create!(:user => @user_1)
509
+ @bar_3 = UserGroup.create!(:user => @user_2, :group => @group_1)
510
+ @bar_3 = UserGroup.create!(:user => @user_2, :group => @group_2)
511
+ @bar_3 = UserGroup.create!(:user => @user_2, :group => @group_2)
512
+ @bar_4 = UserGroup.create!(:user => @user_3)
513
+ end
514
+
515
+ after do
516
+ ActiveRecord::Migration.drop_table(:user_groups)
517
+ ActiveRecord::Migration.drop_table(:users)
518
+ ActiveRecord::Migration.drop_table(:groups)
519
+ ActiveRecord::Migration.drop_table(:conflicts)
520
+ end
521
+
522
+ it "should find the one record that is related based on forward groups relation" do
523
+ User.search_for('related=value AND related="value too!"').length.should == 1
524
+ end
525
+ end
526
+
474
527
 
475
528
  context 'querying a :has_many => :through relation with modules' do
476
529
 
@@ -492,7 +545,7 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
492
545
  has_many :bazs, :through => :mars
493
546
  self.table_name = "zan_koos"
494
547
 
495
- scoped_search :in => :bazs, :on => :related
548
+ scoped_search :relation => :bazs, :on => :related
496
549
  end
497
550
  end
498
551
 
@@ -16,7 +16,7 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
16
16
  :description => :string
17
17
  ) do |klass|
18
18
  klass.scoped_search :on => :string
19
- klass.scoped_search :on => :another, :default_operator => :eq, :alias => :alias, :default_order => :desc
19
+ klass.scoped_search :on => :another, :default_operator => :eq, :aliases => [:alias], :default_order => :desc
20
20
  klass.scoped_search :on => :explicit, :only_explicit => true
21
21
  klass.scoped_search :on => :description
22
22
  end
@@ -150,7 +150,7 @@ describe ScopedSearch::QueryLanguage::AST::OperatorNode do
150
150
  end
151
151
 
152
152
  it "should raise an error of the LHS is requested" do
153
- lambda { @node.lhs }.should raise_error
153
+ lambda { @node.lhs }.should raise_error(ScopedSearch::Exception, "Operator does not have a LHS")
154
154
  end
155
155
  end
156
156
 
@@ -187,11 +187,11 @@ describe ScopedSearch::QueryLanguage::AST::OperatorNode do
187
187
  end
188
188
 
189
189
  it "should raise an error of the LHS is requested" do
190
- lambda { @node.lhs }.should raise_error
190
+ lambda { @node.lhs }.should raise_error(ScopedSearch::Exception, "Operators with more than 2 children do not have LHS/RHS")
191
191
  end
192
192
 
193
193
  it "should raise an error of the RHS is requested" do
194
- lambda { @node.rhs }.should raise_error
194
+ lambda { @node.rhs }.should raise_error(ScopedSearch::Exception, "Operators with more than 2 children do not have LHS/RHS")
195
195
  end
196
196
  end
197
197
  end
@@ -9,6 +9,42 @@ describe ScopedSearch::Definition do
9
9
  end
10
10
 
11
11
  describe ScopedSearch::Definition::Field do
12
+ describe '#initialize' do
13
+ it "should raise an exception with missing field or 'on' keyword" do
14
+ lambda {
15
+ @definition.define
16
+ }.should raise_error(ArgumentError, "Missing field or 'on' keyword argument")
17
+ end
18
+
19
+ it "should raise an exception with unknown keyword arguments" do
20
+ lambda {
21
+ @definition.define(:field, :nonexisting => 'foo')
22
+ }.should raise_error(ArgumentError, "Unknown arguments to scoped_search: nonexisting")
23
+ end
24
+
25
+ it "should alias :in to :relation" do
26
+ ActiveSupport::Deprecation.should_receive(:warn).with("'in' argument deprecated, prefer 'relation' since scoped_search 4.0.0", anything)
27
+ @definition.define(:field, :in => 'foo').relation.should eq('foo')
28
+ end
29
+
30
+ it "should accept :relation" do
31
+ ActiveSupport::Deprecation.should_not_receive(:warn)
32
+ @definition.define(:field, :relation => 'foo').relation.should eq('foo')
33
+ end
34
+
35
+ it "should alias :alias to :aliases" do
36
+ ActiveSupport::Deprecation.should_receive(:warn).with("'alias' argument deprecated, prefer aliases: [..] since scoped_search 4.0.0", anything)
37
+ @definition.define(:field, :alias => 'foo')
38
+ @definition.fields.keys.should eq([:field, :foo])
39
+ end
40
+
41
+ it "should accept :relation" do
42
+ ActiveSupport::Deprecation.should_not_receive(:warn)
43
+ @definition.define(:field, :aliases => ['foo'])
44
+ @definition.fields.keys.should eq([:field, :foo])
45
+ end
46
+ end
47
+
12
48
  describe '#column' do
13
49
  it "should raise an exception when using an unknown field" do
14
50
  lambda {
@@ -33,4 +33,26 @@ describe ScopedSearch::QueryBuilder do
33
33
  @definition.klass.connection.stub("class").and_return(connection)
34
34
  ScopedSearch::QueryBuilder.class_for(@definition).should == ScopedSearch::QueryBuilder::PostgreSQLAdapter
35
35
  end
36
+
37
+ it "should validate value if validator selected" do
38
+ field = double('field')
39
+ field.stub(:only_explicit).and_return(true)
40
+ field.stub(:field).and_return(:test_field)
41
+ field.stub(:validator).and_return(->(_value) { false })
42
+
43
+ @definition.stub(:field_by_name).and_return(field)
44
+
45
+ lambda { ScopedSearch::QueryBuilder.build_query(@definition, 'test_field = test_val') }.should raise_error(ScopedSearch::QueryNotSupported)
46
+ end
47
+
48
+ it "should display custom error from validator" do
49
+ field = double('field')
50
+ field.stub(:only_explicit).and_return(true)
51
+ field.stub(:field).and_return(:test_field)
52
+ field.stub(:validator).and_return(->(_value) { raise ScopedSearch::QueryNotSupported, 'my custom message' })
53
+
54
+ @definition.stub(:field_by_name).and_return(field)
55
+
56
+ lambda { ScopedSearch::QueryBuilder.build_query(@definition, 'test_field = test_val') }.should raise_error('my custom message')
57
+ end
36
58
  end
@@ -92,4 +92,24 @@ describe ScopedSearch::RailsHelper do
92
92
  params[:order] = "field DESC"
93
93
  sort("field")
94
94
  end
95
+
96
+ context 'with ActionController::Parameters' do
97
+ let(:ac_params) { double('ActionController::Parameters') }
98
+
99
+ it "should call to_h on passed params object" do
100
+ should_receive(:url_for).with(
101
+ "controller" => "resources",
102
+ "action" => "search",
103
+ "walrus" => "unicorns",
104
+ "order" => "field ASC"
105
+ ).and_return("/example")
106
+
107
+ params[:walrus] = "unicorns"
108
+
109
+ ac_params.should_receive(:respond_to?).with(:permit).and_return(true)
110
+ ac_params.should_receive(:to_h).and_return(params)
111
+
112
+ sort("field", url_options: ac_params)
113
+ end
114
+ end
95
115
  end
@@ -0,0 +1,27 @@
1
+ require "spec_helper"
2
+
3
+ describe ScopedSearch::Validators do
4
+ describe 'NUMERIC' do
5
+ it 'should accept integer value' do
6
+ ScopedSearch::Validators::NUMERIC.call('123').should eq(true)
7
+ end
8
+
9
+ it 'should accept float value' do
10
+ ScopedSearch::Validators::NUMERIC.call('123.5').should eq(true)
11
+ end
12
+
13
+ it 'should reject string value' do
14
+ ScopedSearch::Validators::NUMERIC.call('abc').should eq(false)
15
+ end
16
+ end
17
+
18
+ describe 'INTEGER' do
19
+ it 'should accept numeric value' do
20
+ ScopedSearch::Validators::INTEGER.call('123').should eq(true)
21
+ end
22
+
23
+ it 'should reject string value' do
24
+ ScopedSearch::Validators::INTEGER.call('abc').should eq(false)
25
+ end
26
+ end
27
+ end
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: 3.3.0
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Amos Benari
@@ -10,48 +10,48 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2016-08-09 00:00:00.000000000 Z
13
+ date: 2016-12-05 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord
17
17
  requirement: !ruby/object:Gem::Requirement
18
18
  requirements:
19
- - - '>='
19
+ - - ">="
20
20
  - !ruby/object:Gem::Version
21
- version: 3.2.0
21
+ version: 4.2.0
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
- - - '>='
26
+ - - ">="
27
27
  - !ruby/object:Gem::Version
28
- version: 3.2.0
28
+ version: 4.2.0
29
29
  - !ruby/object:Gem::Dependency
30
30
  name: rspec
31
31
  requirement: !ruby/object:Gem::Requirement
32
32
  requirements:
33
- - - ~>
33
+ - - "~>"
34
34
  - !ruby/object:Gem::Version
35
35
  version: '3.0'
36
36
  type: :development
37
37
  prerelease: false
38
38
  version_requirements: !ruby/object:Gem::Requirement
39
39
  requirements:
40
- - - ~>
40
+ - - "~>"
41
41
  - !ruby/object:Gem::Version
42
42
  version: '3.0'
43
43
  - !ruby/object:Gem::Dependency
44
44
  name: rake
45
45
  requirement: !ruby/object:Gem::Requirement
46
46
  requirements:
47
- - - '>='
47
+ - - ">="
48
48
  - !ruby/object:Gem::Version
49
49
  version: '0'
50
50
  type: :development
51
51
  prerelease: false
52
52
  version_requirements: !ruby/object:Gem::Requirement
53
53
  requirements:
54
- - - '>='
54
+ - - ">="
55
55
  - !ruby/object:Gem::Version
56
56
  version: '0'
57
57
  description: |2
@@ -77,14 +77,11 @@ extra_rdoc_files:
77
77
  - CONTRIBUTING.rdoc
78
78
  - LICENSE
79
79
  files:
80
- - .gitignore
81
- - .travis.yml
80
+ - ".gitignore"
81
+ - ".travis.yml"
82
82
  - CHANGELOG.rdoc
83
83
  - CONTRIBUTING.rdoc
84
84
  - Gemfile
85
- - Gemfile.activerecord32
86
- - Gemfile.activerecord40
87
- - Gemfile.activerecord41
88
85
  - Gemfile.activerecord42
89
86
  - Gemfile.activerecord50
90
87
  - LICENSE
@@ -105,6 +102,7 @@ files:
105
102
  - lib/scoped_search/query_language/tokenizer.rb
106
103
  - lib/scoped_search/rails_helper.rb
107
104
  - lib/scoped_search/railtie.rb
105
+ - lib/scoped_search/validators.rb
108
106
  - lib/scoped_search/version.rb
109
107
  - scoped_search.gemspec
110
108
  - spec/database.jruby.yml
@@ -128,33 +126,34 @@ files:
128
126
  - spec/unit/query_builder_spec.rb
129
127
  - spec/unit/rails_helper_spec.rb
130
128
  - spec/unit/tokenizer_spec.rb
129
+ - spec/unit/validators_spec.rb
131
130
  homepage: https://github.com/wvanbergen/scoped_search/wiki
132
131
  licenses:
133
132
  - MIT
134
133
  metadata: {}
135
134
  post_install_message:
136
135
  rdoc_options:
137
- - --title
136
+ - "--title"
138
137
  - scoped_search
139
- - --main
138
+ - "--main"
140
139
  - README.rdoc
141
- - --line-numbers
142
- - --inline-source
140
+ - "--line-numbers"
141
+ - "--inline-source"
143
142
  require_paths:
144
143
  - lib
145
144
  required_ruby_version: !ruby/object:Gem::Requirement
146
145
  requirements:
147
- - - '>='
146
+ - - ">="
148
147
  - !ruby/object:Gem::Version
149
- version: 1.9.3
148
+ version: 2.0.0
150
149
  required_rubygems_version: !ruby/object:Gem::Requirement
151
150
  requirements:
152
- - - '>='
151
+ - - ">="
153
152
  - !ruby/object:Gem::Version
154
153
  version: '0'
155
154
  requirements: []
156
155
  rubyforge_project:
157
- rubygems_version: 2.0.14.1
156
+ rubygems_version: 2.6.8
158
157
  signing_key:
159
158
  specification_version: 4
160
159
  summary: Easily search you ActiveRecord models with a simple query language using
@@ -181,3 +180,4 @@ test_files:
181
180
  - spec/unit/query_builder_spec.rb
182
181
  - spec/unit/rails_helper_spec.rb
183
182
  - spec/unit/tokenizer_spec.rb
183
+ - spec/unit/validators_spec.rb
@@ -1,16 +0,0 @@
1
- source 'https://rubygems.org'
2
- gemspec
3
-
4
- gem 'activerecord', '~> 3.2.0'
5
-
6
- platforms :jruby do
7
- gem 'activerecord-jdbcsqlite3-adapter'
8
- gem 'activerecord-jdbcmysql-adapter'
9
- gem 'activerecord-jdbcpostgresql-adapter'
10
- end
11
-
12
- platforms :ruby do
13
- gem 'sqlite3'
14
- gem 'mysql2', '~> 0.3.11'
15
- gem 'pg'
16
- end
@@ -1,16 +0,0 @@
1
- source 'https://rubygems.org'
2
- gemspec
3
-
4
- gem 'activerecord', '~> 4.0.0'
5
-
6
- platforms :jruby do
7
- gem 'activerecord-jdbcsqlite3-adapter'
8
- gem 'activerecord-jdbcmysql-adapter'
9
- gem 'activerecord-jdbcpostgresql-adapter'
10
- end
11
-
12
- platforms :ruby do
13
- gem 'sqlite3'
14
- gem 'mysql2', '~> 0.3.11'
15
- gem 'pg'
16
- end
@@ -1,16 +0,0 @@
1
- source 'https://rubygems.org'
2
- gemspec
3
-
4
- gem 'activerecord', '~> 4.1.0'
5
-
6
- platforms :jruby do
7
- gem 'activerecord-jdbcsqlite3-adapter'
8
- gem 'activerecord-jdbcmysql-adapter'
9
- gem 'activerecord-jdbcpostgresql-adapter'
10
- end
11
-
12
- platforms :ruby do
13
- gem 'sqlite3'
14
- gem 'mysql2', '~> 0.3.11'
15
- gem 'pg'
16
- end