ransack 1.8.2 → 1.8.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d6c89a751336d525190857c926e5621b2f1bd911
4
- data.tar.gz: b1f8e83f8d95332438067b02f9bfff030700c7e4
3
+ metadata.gz: d068e0ef0efe6ce1a4afdb11cc36361f94bbcf68
4
+ data.tar.gz: 2ab905dd3037f405cfc262e2440cbfa557c5a799
5
5
  SHA512:
6
- metadata.gz: 0b631ba6e4bf53e1cc64b913669d374e925d3dda3beafa158162288712f3d4981b20e9b71af95e2d724c0919cb95ba86957ebb357ff0eacc31030c50e5c6b96d
7
- data.tar.gz: f3a2d612e597d28f37fbca66ea1e160881402473d0649f2618946a31884bbaca92042edfa3b81bea66e2ad62a136ebd6702f9cf62f4604533ebe73238b405bfa
6
+ metadata.gz: 354a8a5fc605f5efb7da6d1b78544f93780293418ac9a4578ae395fb608dbd69d301d5a129fbc6cf20c46427427fbeba04b0f7ea040b54771aaecbd31cf174ac
7
+ data.tar.gz: 388a7b733a5460acd980703c6d918077fc27e4777f8acf0b2f3c72b696664c1ef9997c477de74a772ea776eb56521d65cda03bb7f190441575801a4d65b479d6
@@ -3,11 +3,10 @@ language: ruby
3
3
  sudo: false
4
4
 
5
5
  rvm:
6
- - 2.3.1
7
- - 2.2.5
6
+ - 2.3.3
7
+ - 2.2.6
8
8
  - 2.1.10
9
9
  - 2.0
10
- - 1.9
11
10
 
12
11
  env:
13
12
  - RAILS=5-0-stable DB=sqlite3
@@ -50,26 +49,26 @@ matrix:
50
49
  - rvm: 2.0
51
50
  env: RAILS=5-0-stable DB=postgres
52
51
 
53
- - rvm: 1.9
54
- env: RAILS=5-0-stable DB=sqlite3
55
- - rvm: 1.9
56
- env: RAILS=5-0-stable DB=mysql
57
- - rvm: 1.9
58
- env: RAILS=5-0-stable DB=postgres
52
+ - rvm: 2.0
53
+ env: RAILS=4-2-stable DB=sqlite3
54
+ - rvm: 2.0
55
+ env: RAILS=4-2-stable DB=mysql
56
+ - rvm: 2.0
57
+ env: RAILS=4-2-stable DB=postgres
59
58
 
60
59
  include:
61
- - rvm: 2.3.1
60
+ - rvm: 2.3.3
62
61
  env: RAILS=master DB=sqlite3
63
- - rvm: 2.3.1
62
+ - rvm: 2.3.3
64
63
  env: RAILS=master DB=mysql
65
- - rvm: 2.3.1
64
+ - rvm: 2.3.3
66
65
  env: RAILS=master DB=postgres
67
66
 
68
- - rvm: 2.2.5
67
+ - rvm: 2.2.6
69
68
  env: RAILS=master DB=sqlite3
70
- - rvm: 2.2.5
69
+ - rvm: 2.2.6
71
70
  env: RAILS=master DB=mysql
72
- - rvm: 2.2.5
71
+ - rvm: 2.2.6
73
72
  env: RAILS=master DB=postgres
74
73
 
75
74
  allow_failures:
@@ -1,5 +1,48 @@
1
1
  # Change Log
2
2
 
3
+ ## Unreleased
4
+
5
+ ### Added
6
+
7
+ * Add a config option to customize the up and down arrows used for direction
8
+ indicators in Ransack sort links.
9
+ PR [#726](https://github.com/activerecord-hackery/ransack/pull/726).
10
+
11
+ *Garett Arrowood*
12
+
13
+ * Add ability to turn off sanitization of custom scope arguments.
14
+ PR [#742](https://github.com/activerecord-hackery/ransack/pull/742).
15
+
16
+ *Garett Arrowood*
17
+
18
+ ### Fixed
19
+
20
+ * Use class attributes properly so that inheritance is respected.
21
+ PR [#717](https://github.com/activerecord-hackery/ransack/pull/717).
22
+ This fixes two bugs:
23
+
24
+ 1. In the Mongoid adapter, subclasses were not properly inheriting their
25
+ parents' Ransack aliases because each class defined its own set of
26
+ aliases.
27
+
28
+ 2. In the Active Record adapter, Ransack aliases were defined in such a way
29
+ that the parent's (and grandparent's, etc.) aliases were overwritten by
30
+ the child, meaning that all aliases were ultimately kept on
31
+ `ActiveRecord::Base`. This had the unfortunate effect of enforcing
32
+ uniqueness of Ransack alias names across all models rather than per
33
+ model. Depending on the load order of models, earlier definitions of an
34
+ alias in other models were clobbered.
35
+
36
+ *Steve Richert (laserlemon)*
37
+
38
+ * Use `ActiveSupport.on_load` hooks to include Ransack in Active Record,
39
+ avoiding autoloading the constant too soon. PR
40
+ [#719](https://github.com/activerecord-hackery/ransack/pull/719). Reference:
41
+ [This comment in rails#23589]
42
+ (https://github.com/rails/rails/issues/23589#issuecomment-229247727).
43
+
44
+ *Yuji Yaginuma (y-yagi)*
45
+
3
46
  ## Version 1.8.2 - 2016-08-08
4
47
  ### Fixed
5
48
 
data/README.md CHANGED
@@ -7,20 +7,21 @@
7
7
  [![Code Climate](https://codeclimate.com/github/activerecord-hackery/ransack/badges/gpa.svg)]
8
8
  (https://codeclimate.com/github/activerecord-hackery/ransack)
9
9
 
10
- Ransack is a rewrite of [MetaSearch]
11
- (https://github.com/activerecord-hackery/meta_search)
10
+ Ransack is a rewrite of [MetaSearch](https://github.com/activerecord-hackery/meta_search)
12
11
  created by [Ernie Miller](http://twitter.com/erniemiller)
13
- and maintained by [Ryan Bigg](http://twitter.com/ryanbigg),
14
- [Jon Atack](http://twitter.com/jonatack) and a great group of [contributors]
15
- (https://github.com/activerecord-hackery/ransack/graphs/contributors).
12
+ and developed/maintained for years by
13
+ [Jon Atack](http://twitter.com/jonatack) and
14
+ [Ryan Bigg](http://twitter.com/ryanbigg) with the help of a great group of
15
+ [contributors](https://github.com/activerecord-hackery/ransack/graphs/contributors).
16
16
  While it supports many of the same features as MetaSearch, its underlying
17
17
  implementation differs greatly from MetaSearch,
18
18
  and backwards compatibility is not a design goal.
19
19
 
20
- Ransack enables the creation of both simple and
21
- [advanced](http://ransack-demo.herokuapp.com/users/advanced_search)
22
- search forms for your Ruby on Rails application (demo source code
23
- [here](https://github.com/activerecord-hackery/ransack_demo)).
20
+ Ransack enables the creation of both
21
+ [simple](http://ransack-demo.herokuapp.com) and
22
+ [advanced](http://ransack-demo.herokuapp.com/users/advanced_search) search forms
23
+ for your Ruby on Rails application
24
+ ([demo source code here](https://github.com/activerecord-hackery/ransack_demo)).
24
25
  If you're looking for something that simplifies query generation at the model
25
26
  or controller layer, you're probably not looking for Ransack (or MetaSearch,
26
27
  for that matter). Try [Squeel](https://github.com/activerecord-hackery/squeel)
@@ -56,11 +57,6 @@ branch:
56
57
  gem 'ransack', github: 'activerecord-hackery/ransack'
57
58
  ```
58
59
 
59
- If you are using Rails 5 or master and need pagination compatible with it and
60
- Ransack, there is a [Rails 5 version of the `will_paginate` gem here](https://github.com/jonatack/will_paginate).
61
- It is also optimized for Ruby 2.2+. To use it, in your Gemfile:
62
- `gem 'will_paginate', github: 'jonatack/will_paginate'`.
63
-
64
60
  ## Issues tracker
65
61
 
66
62
  * Before filing an issue, please read the [Contributing Guide](CONTRIBUTING.md).
@@ -82,8 +78,7 @@ If you're coming from MetaSearch, things to note:
82
78
  1. The default param key for search params is now `:q`, instead of `:search`.
83
79
  This is primarily to shorten query strings, though advanced queries (below)
84
80
  will still run afoul of URL length limits in most browsers and require a
85
- switch to HTTP POST requests. This key is [configurable]
86
- (https://github.com/activerecord-hackery/ransack/wiki/Configuration).
81
+ switch to HTTP POST requests. This key is [configurable](https://github.com/activerecord-hackery/ransack/wiki/Configuration).
87
82
 
88
83
  2. `form_for` is now `search_form_for`, and validates that a Ransack::Search
89
84
  object is passed to it.
@@ -93,7 +88,7 @@ If you're coming from MetaSearch, things to note:
93
88
  ActiveRecord::Relation in the case of the ActiveRecord adapter) via a call to
94
89
  `Ransack#result`.
95
90
 
96
- ####In your controller
91
+ #### In your controller
97
92
 
98
93
  ```ruby
99
94
  def index
@@ -114,13 +109,13 @@ def index
114
109
  end
115
110
  ```
116
111
 
117
- ####In your view
112
+ #### In your view
118
113
 
119
114
  The two primary Ransack view helpers are `search_form_for` and `sort_link`,
120
115
  which are defined in
121
116
  [Ransack::Helpers::FormHelper](lib/ransack/helpers/form_helper.rb).
122
117
 
123
- ####Ransack's `search_form_for` helper replaces `form_for` for creating the view search form
118
+ #### Ransack's `search_form_for` helper replaces `form_for` for creating the view search form
124
119
 
125
120
  ```erb
126
121
  <%= search_form_for @q do |f| %>
@@ -156,7 +151,7 @@ The `search_form_for` answer format can be set like this:
156
151
  <%= search_form_for(@q, format: :json) do |f| %>
157
152
  ```
158
153
 
159
- ####Ransack's `sort_link` helper creates table headers that are sortable links
154
+ #### Ransack's `sort_link` helper creates table headers that are sortable links
160
155
 
161
156
  ```erb
162
157
  <%= sort_link(@q, :name) %>
@@ -205,15 +200,23 @@ This example toggles the sort directions of both fields, by default
205
200
  initially sorting the `last_name` field by ascending order, and the
206
201
  `first_name` field by descending order.
207
202
 
208
- The sort link may be displayed without the order indicator arrow by passing
209
- `hide_indicator: true`:
210
203
 
211
- ```erb
212
- <%= sort_link(@q, :name, hide_indicator: true) %>
204
+ The sort link order indicator arrows may be globally customized by setting a
205
+ `custom_arrows` option in an initializer file like
206
+ `config/initializers/ransack.rb`:
207
+
208
+ ```ruby
209
+ Ransack.configure do |c|
210
+ c.custom_arrows = {
211
+ up_arrow: '<i class="custom-up-arrow-icon"></i>',
212
+ down_arrow: 'U+02193'
213
+ }
214
+ end
213
215
  ```
214
216
 
215
- Alternatively, all sort links may be displayed without the order indicator arrow
216
- by adding this to an initializer file like `config/initializers/ransack.rb`:
217
+ All sort links may be displayed without the order indicator
218
+ arrows by setting `hide_sort_order_indicators` to true in the initializer file.
219
+ Note that this hides the arrows even if they were customized:
217
220
 
218
221
  ```ruby
219
222
  Ransack.configure do |c|
@@ -221,7 +224,14 @@ Ransack.configure do |c|
221
224
  end
222
225
  ```
223
226
 
224
- ####Ransack's `sort_url` helper is like a `sort_link` but returns only the url
227
+ Without setting it globally, individual sort links may be displayed without
228
+ the order indicator arrow by passing `hide_indicator: true` in the sort link:
229
+
230
+ ```erb
231
+ <%= sort_link(@q, :name, hide_indicator: true) %>
232
+ ```
233
+
234
+ #### Ransack's `sort_url` helper is like a `sort_link` but returns only the url
225
235
 
226
236
  `sort_url` has the same API as `sort_link`:
227
237
 
@@ -275,11 +285,12 @@ end
275
285
 
276
286
  Once you've done so, you can make use of the helpers in [Ransack::Helpers::FormBuilder](lib/ransack/helpers/form_builder.rb) to
277
287
  construct much more complex search forms, such as the one on the
278
- [demo page](http://ransack-demo.heroku.com) (source code [here](https://github.com/activerecord-hackery/ransack_demo)).
288
+ [demo app](http://ransack-demo.herokuapp.com/users/advanced_search)
289
+ (source code [here](https://github.com/activerecord-hackery/ransack_demo)).
279
290
 
280
291
  ### Ransack #search method
281
292
 
282
- Ransack will try to to make the class method `#search` available in your
293
+ Ransack will try to make the class method `#search` available in your
283
294
  models, but if `#search` has already been defined elsewhere, you can always use
284
295
  the default `#ransack` class method. So the following are equivalent:
285
296
 
@@ -395,13 +406,41 @@ query parameters in your URLs.
395
406
  <% end %>
396
407
  ```
397
408
 
409
+ ### Search Matchers
410
+
411
+ List of all possible predicates
412
+
413
+ * `*_eq` - equal
414
+ * `*_not_eq` - not equal
415
+ * `*_matches` - matches with `LIKE`, e.g. `q[email_matches]=%@gmail.com`
416
+ * Also: `*_does_not_match`, `*_matches_any`, `*_matches_all`, `*_does_not_match_any`, `*_does_not_match_all`
417
+ * `*_lt` - less than
418
+ * `*_lteq` - less than or equal
419
+ * `*_gt` - greater than
420
+ * `*_gteq` - greater than or equal
421
+ * `*_present` - not null and not empty, e.g. `q[name_present]=1` (SQL: `col is not null AND col != ''`)
422
+ * `*_blank` - is null or empty. (SQL: `col is null OR col = ''`)
423
+ * `*_null`, `*_not_null` - is null, is not null
424
+ * `*_in` - match any values in array, e.g. `q[name_in][]=Alice&q[name_in][]=Bob`
425
+ * `*_not_in` - match none of values in array
426
+ * `*_lt_any`, `*_lteq_any`, `*_gt_any`, `*_gteq_any` - Compare to list of values, at least positive. (SQL: `col > value1 OR col > value2`)
427
+ * `*_matches_any`, `*_does_not_match_any` - same as above but with `LIKE`
428
+ * `*_lt_all`, `*_lteq_all`, `*_gt_all`, `*_gteq_all` - Compare to list of values, all positive. (SQL: `col > value1 AND col > value2`)
429
+ * `*_matches_all`, `*_does_not_match_all` - same as above but with `LIKE`
430
+ * `*_not_eq_all` - none of values in a set
431
+ * `*_start`, `*_not_start`, `*_start_any`, `*_start_all`, `*_not_start_any`, `*_not_start_all` - start with, (SQL: `col LIKE 'value%'`)
432
+ * `*_end`, `*_not_end`, `*_end_any`, `*_end_all`, `*_not_end_any`, `*_not_end_all` - end with, (SQL: `col LIKE '%value'`)
433
+ * `*_cont`, `*_cont_any`, `*_cont_all`, `*_not_cont`, `*_not_cont_any`, `*_not_cont_all` - contains value, using `LIKE`
434
+ * `*_true`, `*_false` - is true and is false
435
+
436
+ (See full list: https://github.com/activerecord-hackery/ransack/blob/master/lib/ransack/locale/en.yml#L15 and [wiki](https://github.com/activerecord-hackery/ransack/wiki/Basic-Searching))
437
+
398
438
  ### Using Ransackers to add custom search functions via Arel
399
439
 
400
440
  The main premise behind Ransack is to provide access to
401
441
  **Arel predicate methods**. Ransack provides special methods, called
402
442
  _ransackers_, for creating additional search functions via Arel. More
403
- information about `ransacker` methods can be found [here in the wiki]
404
- (https://github.com/activerecord-hackery/ransack/wiki/Using-Ransackers).
443
+ information about `ransacker` methods can be found [here in the wiki](https://github.com/activerecord-hackery/ransack/wiki/Using-Ransackers).
405
444
  Feel free to contribute working `ransacker` code examples to the wiki!
406
445
 
407
446
  ### Problem with DISTINCT selects
@@ -511,8 +550,7 @@ for an `auth_object` key in the options hash which can be used by your own
511
550
  overridden methods.
512
551
 
513
552
  Here is an example that puts all this together, adapted from
514
- [this blog post by Ernie Miller]
515
- (http://erniemiller.org/2012/05/11/why-your-ruby-class-macros-might-suck-mine-did/).
553
+ [this blog post by Ernie Miller](http://erniemiller.org/2012/05/11/why-your-ruby-class-macros-might-suck-mine-did/).
516
554
  In an `Article` model, add the following `ransackable_attributes` class method
517
555
  (preferably private):
518
556
 
@@ -613,7 +651,21 @@ Employee.ransack({ salary_gt: 100_000 }, { auth_object: current_user })
613
651
  In Rails 3 and 4, if the `true` value is being passed via url params or some
614
652
  other mechanism that will convert it to a string, the true value may not be
615
653
  passed to the ransackable scope unless you wrap it in an array
616
- (i.e. `activated: ['true']`). This is currently resolved in Rails 5 :smiley:
654
+ (i.e. `activated: ['true']`). Ransack will take care of changing 'true' into a
655
+ boolean. This is currently resolved in Rails 5 :smiley:
656
+
657
+ However, perhaps you have `user_id: [1]` and you do not want Ransack to convert
658
+ 1 into a boolean. (Values sanitized to booleans can be found in the
659
+ [constants.rb](https://github.com/activerecord-hackery/ransack/blob/master/lib/ransack/constants.rb#L28)).
660
+ To turn this off, and handle type conversions yourself, set
661
+ `sanitize_custom_scope_booleans` to false in an initializer file like
662
+ config/initializers/ransack.rb:
663
+
664
+ ```ruby
665
+ Ransack.configure do |c|
666
+ c.sanitize_custom_scope_booleans = false
667
+ end
668
+ ```
617
669
 
618
670
  Scopes are a recent addition to Ransack and currently have a few caveats:
619
671
  First, a scope involving child associations needs to be defined in the parent
@@ -622,8 +674,7 @@ argument are not easily usable yet, because the array currently needs to be
622
674
  wrapped in an array to function (see
623
675
  [this issue](https://github.com/activerecord-hackery/ransack/issues/404)),
624
676
  which is not compatible with Ransack form helpers. For this use case, it may be
625
- better for now to use [ransackers]
626
- (https://github.com/activerecord-hackery/ransack/wiki/Using-Ransackers) instead,
677
+ better for now to use [ransackers](https://github.com/activerecord-hackery/ransack/wiki/Using-Ransackers) instead,
627
678
  where feasible. Pull requests with solutions and tests are welcome!
628
679
 
629
680
  ### Grouping queries by OR instead of AND
@@ -747,9 +798,8 @@ en:
747
798
 
748
799
  ## Mongoid
749
800
 
750
- Ransack now works with Mongoid in the same way as Active Record, except that
751
- with Mongoid, associations are not currently supported. A demo app may be found
752
- [here](http://ransack-mongodb-demo.herokuapp.com/) and the demo source code is
801
+ Ransack works with Mongoid in the same way as Active Record, except that with
802
+ Mongoid, associations are not currently supported. Demo source code may be found
753
803
  [here](https://github.com/Zhomart/ransack-mongodb-demo). A `result` method
754
804
  called on a `ransack` search returns a `Mongoid::Criteria` object:
755
805
 
@@ -7,13 +7,6 @@ Ransack::Adapters.object_mapper.require_constants
7
7
  module Ransack
8
8
  extend Configuration
9
9
  class UntraversableAssociationError < StandardError; end;
10
-
11
- SUPPORTS_ATTRIBUTE_ALIAS =
12
- begin
13
- ActiveRecord::Base.respond_to?(:attribute_aliases)
14
- rescue NameError
15
- false
16
- end
17
10
  end
18
11
 
19
12
  Ransack.configure do |config|
@@ -1,5 +1,15 @@
1
1
  require 'ransack/adapters/active_record/base'
2
- ActiveRecord::Base.extend Ransack::Adapters::ActiveRecord::Base
2
+
3
+ ActiveSupport.on_load(:active_record) do
4
+ extend Ransack::Adapters::ActiveRecord::Base
5
+
6
+ Ransack::SUPPORTS_ATTRIBUTE_ALIAS =
7
+ begin
8
+ ActiveRecord::Base.respond_to?(:attribute_aliases)
9
+ rescue NameError
10
+ false
11
+ end
12
+ end
3
13
 
4
14
  require 'ransack/adapters/active_record/context'
5
15
 
@@ -23,7 +23,8 @@ module Ransack
23
23
  end
24
24
 
25
25
  def ransack_alias(new_name, old_name)
26
- self._ransack_aliases.store(new_name.to_s, old_name.to_s)
26
+ self._ransack_aliases = _ransack_aliases.merge new_name.to_s =>
27
+ old_name.to_s
27
28
  end
28
29
 
29
30
  # Ransackable_attributes, by default, returns all column names
@@ -7,9 +7,14 @@ module Ransack
7
7
  association = attribute.parent
8
8
  if negative? && attribute.associated_collection?
9
9
  query = context.build_correlated_subquery(association)
10
- query.where(format_predicate(attribute).not)
11
10
  context.remove_association(association)
12
- Arel::Nodes::NotIn.new(context.primary_key, Arel.sql(query.to_sql))
11
+ if self.predicate_name == 'not_null' && self.value
12
+ query.where(format_predicate(attribute))
13
+ Arel::Nodes::In.new(context.primary_key, Arel.sql(query.to_sql))
14
+ else
15
+ query.where(format_predicate(attribute).not)
16
+ Arel::Nodes::NotIn.new(context.primary_key, Arel.sql(query.to_sql))
17
+ end
13
18
  else
14
19
  format_predicate(attribute)
15
20
  end
@@ -11,3 +11,5 @@ when /^3\.2\./
11
11
  else
12
12
  require 'ransack/adapters/mongoid/context'
13
13
  end
14
+
15
+ Ransack::SUPPORTS_ATTRIBUTE_ALIAS = false
@@ -8,6 +8,10 @@ module Ransack
8
8
  extend ActiveSupport::Concern
9
9
 
10
10
  included do
11
+ class_attribute :_ransackers
12
+ class_attribute :_ransack_aliases
13
+ self._ransackers ||= {}
14
+ self._ransack_aliases ||= {}
11
15
  end
12
16
 
13
17
  class ColumnWrapper < SimpleDelegator
@@ -33,22 +37,6 @@ module Ransack
33
37
  end
34
38
 
35
39
  module ClassMethods
36
- def _ransack_aliases
37
- @_ransack_aliases ||= {}
38
- end
39
-
40
- def _ransack_aliases=(value)
41
- @_ransack_aliases = value
42
- end
43
-
44
- def _ransackers
45
- @_ransackers ||= {}
46
- end
47
-
48
- def _ransackers=(value)
49
- @_ransackers = value
50
- end
51
-
52
40
  def ransack(params = {}, options = {})
53
41
  params = params.presence || {}
54
42
  Search.new(self, params ? params.delete_if {
@@ -58,7 +46,8 @@ module Ransack
58
46
  alias_method :search, :ransack
59
47
 
60
48
  def ransack_alias(new_name, old_name)
61
- self._ransack_aliases.store(new_name.to_s, old_name.to_s)
49
+ self._ransack_aliases = _ransack_aliases.merge new_name.to_s =>
50
+ old_name.to_s
62
51
  end
63
52
 
64
53
  def ransacker(name, opts = {}, &block)
@@ -9,7 +9,10 @@ module Ransack
9
9
  self.options = {
10
10
  :search_key => :q,
11
11
  :ignore_unknown_conditions => true,
12
- :hide_sort_order_indicators => false
12
+ :hide_sort_order_indicators => false,
13
+ :up_arrow => '&#9660;'.freeze,
14
+ :down_arrow => '&#9650;'.freeze,
15
+ :sanitize_scope_args => true
13
16
  }
14
17
 
15
18
  def configure
@@ -75,6 +78,43 @@ module Ransack
75
78
  self.options[:ignore_unknown_conditions] = boolean
76
79
  end
77
80
 
81
+ # By default, Ransack displays sort order indicator arrows with HTML codes:
82
+ #
83
+ # up_arrow: '&#9660;'
84
+ # down_arrow: '&#9650;'
85
+ #
86
+ # One or both defaults may be globally overridden in an initializer file
87
+ # like `config/initializers/ransack.rb` as follows:
88
+ #
89
+ # Ransack.configure do |config|
90
+ # # Globally set the up arrow to an icon and the down arrow to unicode.
91
+ # config.custom_arrows = {
92
+ # up_arrow: '<i class="fa fa-long-arrow-up"></i>',
93
+ # down_arrow: 'U+02193'
94
+ # }
95
+ # end
96
+ #
97
+ def custom_arrows=(opts = {})
98
+ self.options[:up_arrow] = opts[:up_arrow].freeze if opts[:up_arrow]
99
+ self.options[:down_arrow] = opts[:down_arrow].freeze if opts[:down_arrow]
100
+ end
101
+
102
+ # Ransack sanitizes many values in your custom scopes into booleans.
103
+ # [1, '1', 't', 'T', 'true', 'TRUE'] all evaluate to true.
104
+ # [0, '0', 'f', 'F', 'false', 'FALSE'] all evaluate to false.
105
+ #
106
+ # This default may be globally overridden in an initializer file like
107
+ # `config/initializers/ransack.rb` as follows:
108
+ #
109
+ # Ransack.configure do |config|
110
+ # # Accept my custom scope values as what they are.
111
+ # config.sanitize_custom_scope_booleans = false
112
+ # end
113
+ #
114
+ def sanitize_custom_scope_booleans=(boolean)
115
+ self.options[:sanitize_scope_args] = boolean
116
+ end
117
+
78
118
  # By default, Ransack displays sort order indicator arrows in sort links.
79
119
  # The default may be globally overridden in an initializer file like
80
120
  # `config/initializers/ransack.rb` as follows:
@@ -110,11 +110,11 @@ module Ransack
110
110
  end
111
111
 
112
112
  def up_arrow
113
- '&#9660;'.freeze
113
+ Ransack.options[:up_arrow]
114
114
  end
115
115
 
116
116
  def down_arrow
117
- '&#9650;'.freeze
117
+ Ransack.options[:down_arrow]
118
118
  end
119
119
 
120
120
  def name
@@ -123,12 +123,18 @@ module Ransack
123
123
  private
124
124
 
125
125
  def add_scope(key, args)
126
+ sanitized_args = if Ransack.options[:sanitize_scope_args]
127
+ sanitized_scope_args(args)
128
+ else
129
+ args
130
+ end
131
+
126
132
  if @context.scope_arity(key) == 1
127
133
  @scope_args[key] = args.is_a?(Array) ? args[0] : args
128
134
  else
129
- @scope_args[key] = args.is_a?(Array) ? sanitized_scope_args(args) : args
135
+ @scope_args[key] = args.is_a?(Array) ? sanitized_args : args
130
136
  end
131
- @context.chain_scope(key, sanitized_scope_args(args))
137
+ @context.chain_scope(key, sanitized_args)
132
138
  end
133
139
 
134
140
  def sanitized_scope_args(args)
@@ -1,3 +1,3 @@
1
1
  module Ransack
2
- VERSION = '1.8.2'
2
+ VERSION = '1.8.3'
3
3
  end
@@ -65,6 +65,27 @@ module Ransack
65
65
  s = Person.ransack(term_cont: 'nomatch')
66
66
  expect(s.result.to_a).to eq []
67
67
  end
68
+
69
+ it 'makes aliases available to subclasses' do
70
+ yngwie = Musician.create!(name: 'Yngwie Malmsteen')
71
+
72
+ musicians = Musician.ransack(term_cont: 'ngw').result
73
+ expect(musicians).to eq([yngwie])
74
+ end
75
+
76
+ it 'handles naming collisions gracefully' do
77
+ frank = Person.create!(name: 'Frank Stallone')
78
+
79
+ people = Person.ransack(term_cont: 'allon').result
80
+ expect(people).to eq([frank])
81
+
82
+ Class.new(Article) do
83
+ ransack_alias :term, :title
84
+ end
85
+
86
+ people = Person.ransack(term_cont: 'allon').result
87
+ expect(people).to eq([frank])
88
+ end
68
89
  end
69
90
 
70
91
  describe '#ransacker' do
@@ -274,8 +295,6 @@ module Ransack
274
295
  end
275
296
 
276
297
  describe '#ransackable_associations' do
277
- before { pending "not implemented for mongoid" }
278
-
279
298
  subject { Person.ransackable_associations }
280
299
 
281
300
  it { should include 'parent' }
@@ -36,17 +36,73 @@ module Ransack
36
36
  end
37
37
 
38
38
  it 'changes default search key parameter' do
39
- # store original state so we can restore it later
40
- before = Ransack.options.clone
39
+ default = Ransack.options.clone
41
40
 
42
- Ransack.configure do |config|
43
- config.search_key = :query
44
- end
41
+ Ransack.configure { |c| c.search_key = :query }
45
42
 
46
43
  expect(Ransack.options[:search_key]).to eq :query
47
44
 
48
- # restore original state so we don't break other tests
49
- Ransack.options = before
45
+ Ransack.options = default
46
+ end
47
+
48
+ it 'should have default values for arrows' do
49
+ expect(Ransack.options[:up_arrow]).to eq '&#9660;'
50
+ expect(Ransack.options[:down_arrow]).to eq '&#9650;'
51
+ end
52
+
53
+ it 'changes the default value for the up arrow only' do
54
+ default, new_up_arrow = Ransack.options.clone, 'U+02191'
55
+
56
+ Ransack.configure { |c| c.custom_arrows = { up_arrow: new_up_arrow } }
57
+
58
+ expect(Ransack.options[:down_arrow]).to eq default[:down_arrow]
59
+ expect(Ransack.options[:up_arrow]).to eq new_up_arrow
60
+
61
+ Ransack.options = default
62
+ end
63
+
64
+ it 'changes the default value for the down arrow only' do
65
+ default, new_down_arrow = Ransack.options.clone, '<i class="down"></i>'
66
+
67
+ Ransack.configure { |c| c.custom_arrows = { down_arrow: new_down_arrow } }
68
+
69
+ expect(Ransack.options[:up_arrow]).to eq default[:up_arrow]
70
+ expect(Ransack.options[:down_arrow]).to eq new_down_arrow
71
+
72
+ Ransack.options = default
73
+ end
74
+
75
+ it 'changes the default value for both arrows' do
76
+ default = Ransack.options.clone
77
+ new_up_arrow = '<i class="fa fa-long-arrow-up"></i>'
78
+ new_down_arrow = 'U+02193'
79
+
80
+ Ransack.configure do |c|
81
+ c.custom_arrows = { up_arrow: new_up_arrow, down_arrow: new_down_arrow }
82
+ end
83
+
84
+ expect(Ransack.options[:up_arrow]).to eq new_up_arrow
85
+ expect(Ransack.options[:down_arrow]).to eq new_down_arrow
86
+
87
+ Ransack.options = default
88
+ end
89
+
90
+ it 'consecutive arrow customizations respect previous customizations' do
91
+ default = Ransack.options.clone
92
+
93
+ Ransack.configure { |c| c.custom_arrows = { up_arrow: 'up' } }
94
+ expect(Ransack.options[:down_arrow]).to eq default[:down_arrow]
95
+
96
+ Ransack.configure { |c| c.custom_arrows = { down_arrow: 'DOWN' } }
97
+ expect(Ransack.options[:up_arrow]).to eq 'up'
98
+
99
+ Ransack.configure { |c| c.custom_arrows = { up_arrow: '<i>U-Arrow</i>' } }
100
+ expect(Ransack.options[:down_arrow]).to eq 'DOWN'
101
+
102
+ Ransack.configure { |c| c.custom_arrows = { down_arrow: 'down arrow-2' } }
103
+ expect(Ransack.options[:up_arrow]).to eq '<i>U-Arrow</i>'
104
+
105
+ Ransack.options = default
50
106
  end
51
107
 
52
108
  it 'adds predicates that take arrays, overriding compounds' do
@@ -76,8 +132,10 @@ module Ransack
76
132
  )
77
133
  end
78
134
 
79
- expect(Ransack.predicates['test_in_predicate'].wants_array).to eq true
80
- expect(Ransack.predicates['test_not_in_predicate'].wants_array).to eq true
135
+ expect(Ransack.predicates['test_in_predicate'].wants_array)
136
+ .to eq true
137
+ expect(Ransack.predicates['test_not_in_predicate'].wants_array)
138
+ .to eq true
81
139
  end
82
140
 
83
141
  it 'explicitly does not want array for in/not_in predicates' do
@@ -94,8 +152,10 @@ module Ransack
94
152
  )
95
153
  end
96
154
 
97
- expect(Ransack.predicates['test_in_predicate_no_array'].wants_array).to eq false
98
- expect(Ransack.predicates['test_not_in_predicate_no_array'].wants_array).to eq false
155
+ expect(Ransack.predicates['test_in_predicate_no_array'].wants_array)
156
+ .to eq false
157
+ expect(Ransack.predicates['test_not_in_predicate_no_array'].wants_array)
158
+ .to eq false
99
159
  end
100
160
  end
101
161
  end
@@ -433,7 +433,6 @@ module Ransack
433
433
  end
434
434
 
435
435
  context 'with joins' do
436
- before { pending 'not implemented for mongoid' }
437
436
  it 'allows chaining to access nested conditions' do
438
437
  @s.groupings = [
439
438
  { :m => 'or', :name_eq => 'Ernie', :children_name_eq => 'Ernie' }
@@ -63,6 +63,9 @@ class Person
63
63
  end
64
64
  end
65
65
 
66
+ class Musician < Person
67
+ end
68
+
66
69
  class Article
67
70
  include Mongoid::Document
68
71
 
@@ -65,25 +65,31 @@ module Ransack
65
65
  expect(s.result.to_sql).to (include 'age > 18')
66
66
  end
67
67
 
68
- # TODO: Implement a way to pass true/false values like 0 or 1 to
69
- # scopes (e.g. with `in` / `not_in` predicates), without Ransack
70
- # converting them to true/false boolean values instead.
71
-
72
- # it 'passes true values to scopes', focus: true do
73
- # s = Person.ransack('over_age' => 1)
74
- # expect(s.result.to_sql).to (include 'age > 1')
75
- # end
76
-
77
- # it 'passes false values to scopes', focus: true do
78
- # s = Person.ransack('over_age' => 0)
79
- # expect(s.result.to_sql).to (include 'age > 0')
80
- # end
81
-
82
68
  it 'chains scopes' do
83
69
  s = Person.ransack('over_age' => 18, 'active' => true)
84
70
  expect(s.result.to_sql).to (include 'age > 18')
85
71
  expect(s.result.to_sql).to (include 'active = 1')
86
72
  end
73
+
74
+ context "with sanitize_custom_scope_booleans set to false" do
75
+ before(:all) do
76
+ Ransack.configure { |c| c.sanitize_custom_scope_booleans = false }
77
+ end
78
+
79
+ after(:all) do
80
+ Ransack.configure { |c| c.sanitize_custom_scope_booleans = true }
81
+ end
82
+
83
+ it 'passes true values to scopes' do
84
+ s = Person.ransack('over_age' => 1)
85
+ expect(s.result.to_sql).to (include 'age > 1')
86
+ end
87
+
88
+ it 'passes false values to scopes' do
89
+ s = Person.ransack('over_age' => 0)
90
+ expect(s.result.to_sql).to (include 'age > 0')
91
+ end
92
+ end
87
93
  end
88
94
 
89
95
  it 'does not raise exception for string :params argument' do
@@ -190,6 +196,27 @@ module Ransack
190
196
  s = Person.ransack(daddy_eq: 'Drake')
191
197
  expect(s.result.to_a).to eq []
192
198
  end
199
+
200
+ it 'makes aliases available to subclasses' do
201
+ yngwie = Musician.create!(name: 'Yngwie Malmsteen')
202
+
203
+ musicians = Musician.ransack(term_cont: 'ngw').result
204
+ expect(musicians).to eq([yngwie])
205
+ end
206
+
207
+ it 'handles naming collisions gracefully' do
208
+ frank = Person.create!(name: 'Frank Stallone')
209
+
210
+ people = Person.ransack(term_cont: 'allon').result
211
+ expect(people).to eq([frank])
212
+
213
+ Class.new(Article) do
214
+ ransack_alias :term, :title
215
+ end
216
+
217
+ people = Person.ransack(term_cont: 'allon').result
218
+ expect(people).to eq([frank])
219
+ end
193
220
  end
194
221
 
195
222
  describe '#ransacker' do
@@ -3,9 +3,7 @@ require 'spec_helper'
3
3
  module Ransack
4
4
  describe Configuration do
5
5
  it 'yields Ransack on configure' do
6
- Ransack.configure do |config|
7
- expect(config).to eq Ransack
8
- end
6
+ Ransack.configure { |config| expect(config).to eq Ransack }
9
7
  end
10
8
 
11
9
  it 'adds predicates' do
@@ -38,17 +36,73 @@ module Ransack
38
36
  end
39
37
 
40
38
  it 'changes default search key parameter' do
41
- # store original state so we can restore it later
42
- before = Ransack.options.clone
39
+ default = Ransack.options.clone
43
40
 
44
- Ransack.configure do |config|
45
- config.search_key = :query
46
- end
41
+ Ransack.configure { |c| c.search_key = :query }
47
42
 
48
43
  expect(Ransack.options[:search_key]).to eq :query
49
44
 
50
- # restore original state so we don't break other tests
51
- Ransack.options = before
45
+ Ransack.options = default
46
+ end
47
+
48
+ it 'should have default values for arrows' do
49
+ expect(Ransack.options[:up_arrow]).to eq '&#9660;'
50
+ expect(Ransack.options[:down_arrow]).to eq '&#9650;'
51
+ end
52
+
53
+ it 'changes the default value for the up arrow only' do
54
+ default, new_up_arrow = Ransack.options.clone, 'U+02191'
55
+
56
+ Ransack.configure { |c| c.custom_arrows = { up_arrow: new_up_arrow } }
57
+
58
+ expect(Ransack.options[:down_arrow]).to eq default[:down_arrow]
59
+ expect(Ransack.options[:up_arrow]).to eq new_up_arrow
60
+
61
+ Ransack.options = default
62
+ end
63
+
64
+ it 'changes the default value for the down arrow only' do
65
+ default, new_down_arrow = Ransack.options.clone, '<i class="down"></i>'
66
+
67
+ Ransack.configure { |c| c.custom_arrows = { down_arrow: new_down_arrow } }
68
+
69
+ expect(Ransack.options[:up_arrow]).to eq default[:up_arrow]
70
+ expect(Ransack.options[:down_arrow]).to eq new_down_arrow
71
+
72
+ Ransack.options = default
73
+ end
74
+
75
+ it 'changes the default value for both arrows' do
76
+ default = Ransack.options.clone
77
+ new_up_arrow = '<i class="fa fa-long-arrow-up"></i>'
78
+ new_down_arrow = 'U+02193'
79
+
80
+ Ransack.configure do |c|
81
+ c.custom_arrows = { up_arrow: new_up_arrow, down_arrow: new_down_arrow }
82
+ end
83
+
84
+ expect(Ransack.options[:up_arrow]).to eq new_up_arrow
85
+ expect(Ransack.options[:down_arrow]).to eq new_down_arrow
86
+
87
+ Ransack.options = default
88
+ end
89
+
90
+ it 'consecutive arrow customizations respect previous customizations' do
91
+ default = Ransack.options.clone
92
+
93
+ Ransack.configure { |c| c.custom_arrows = { up_arrow: 'up' } }
94
+ expect(Ransack.options[:down_arrow]).to eq default[:down_arrow]
95
+
96
+ Ransack.configure { |c| c.custom_arrows = { down_arrow: 'DOWN' } }
97
+ expect(Ransack.options[:up_arrow]).to eq 'up'
98
+
99
+ Ransack.configure { |c| c.custom_arrows = { up_arrow: '<i>U-Arrow</i>' } }
100
+ expect(Ransack.options[:down_arrow]).to eq 'DOWN'
101
+
102
+ Ransack.configure { |c| c.custom_arrows = { down_arrow: 'down arrow-2' } }
103
+ expect(Ransack.options[:up_arrow]).to eq '<i>U-Arrow</i>'
104
+
105
+ Ransack.options = default
52
106
  end
53
107
 
54
108
  it 'adds predicates that take arrays, overriding compounds' do
@@ -78,8 +132,10 @@ module Ransack
78
132
  )
79
133
  end
80
134
 
81
- expect(Ransack.predicates['test_in_predicate'].wants_array).to eq true
82
- expect(Ransack.predicates['test_not_in_predicate'].wants_array).to eq true
135
+ expect(Ransack.predicates['test_in_predicate'].wants_array)
136
+ .to eq true
137
+ expect(Ransack.predicates['test_not_in_predicate'].wants_array)
138
+ .to eq true
83
139
  end
84
140
 
85
141
  it 'explicitly does not want array for in/not_in predicates' do
@@ -96,8 +152,10 @@ module Ransack
96
152
  )
97
153
  end
98
154
 
99
- expect(Ransack.predicates['test_in_predicate_no_array'].wants_array).to eq false
100
- expect(Ransack.predicates['test_not_in_predicate_no_array'].wants_array).to eq false
155
+ expect(Ransack.predicates['test_in_predicate_no_array'].wants_array)
156
+ .to eq false
157
+ expect(Ransack.predicates['test_not_in_predicate_no_array'].wants_array)
158
+ .to eq false
101
159
  end
102
160
  end
103
161
  end
@@ -649,10 +649,57 @@ module Ransack
649
649
  it { should match /Full Name&nbsp;&#9660;/ }
650
650
  end
651
651
 
652
- describe '#sort_link with config set to globally hide order indicators' do
652
+ describe '#sort_link with config set with custom up_arrow' do
653
+ before do
654
+ Ransack.configure { |c| c.custom_arrows = { up_arrow: "\u{1F446}" } }
655
+ end
656
+
657
+ after do
658
+ Ransack.configure { |c| c.custom_arrows = { up_arrow: "&#9660;" } }
659
+ end
660
+
661
+ subject { @controller.view_context
662
+ .sort_link(
663
+ [:main_app, Person.search(sorts: ['name desc'])],
664
+ :name,
665
+ controller: 'people',
666
+ hide_indicator: false
667
+ )
668
+ }
669
+
670
+ it { should match /Full Name&nbsp;\u{1F446}/ }
671
+ end
672
+
673
+ describe '#sort_link with config set with custom down_arrow' do
674
+ before do
675
+ Ransack.configure { |c| c.custom_arrows = { down_arrow: "\u{1F447}" } }
676
+ end
677
+
678
+ after do
679
+ Ransack.configure { |c| c.custom_arrows = { down_arrow: "&#9650;" } }
680
+ end
681
+
682
+ subject { @controller.view_context
683
+ .sort_link(
684
+ [:main_app, Person.search(sorts: ['name asc'])],
685
+ :name,
686
+ controller: 'people',
687
+ hide_indicator: false
688
+ )
689
+ }
690
+
691
+ it { should match /Full Name&nbsp;\u{1F447}/ }
692
+ end
693
+
694
+ describe '#sort_link with config set to hide arrows' do
653
695
  before do
654
696
  Ransack.configure { |c| c.hide_sort_order_indicators = true }
655
697
  end
698
+
699
+ after do
700
+ Ransack.configure { |c| c.hide_sort_order_indicators = false }
701
+ end
702
+
656
703
  subject { @controller.view_context
657
704
  .sort_link(
658
705
  [:main_app, Person.search(sorts: ['name desc'])],
@@ -660,13 +707,15 @@ module Ransack
660
707
  controller: 'people'
661
708
  )
662
709
  }
710
+
663
711
  it { should_not match /&#9660;|&#9650;/ }
664
712
  end
665
713
 
666
- describe '#sort_link with config set to globally show order indicators' do
714
+ describe '#sort_link with config set to show arrows (default setting)' do
667
715
  before do
668
716
  Ransack.configure { |c| c.hide_sort_order_indicators = false }
669
717
  end
718
+
670
719
  subject { @controller.view_context
671
720
  .sort_link(
672
721
  [:main_app, Person.search(sorts: ['name desc'])],
@@ -674,9 +723,62 @@ module Ransack
674
723
  controller: 'people'
675
724
  )
676
725
  }
726
+
677
727
  it { should match /Full Name&nbsp;&#9660;/ }
678
728
  end
679
729
 
730
+ describe '#sort_link w/config to hide arrows + custom arrow, hides all' do
731
+ before do
732
+ Ransack.configure do |c|
733
+ c.hide_sort_order_indicators = true
734
+ c.custom_arrows = { down_arrow: 'down' }
735
+ end
736
+ end
737
+
738
+ after do
739
+ Ransack.configure do |c|
740
+ c.hide_sort_order_indicators = false
741
+ c.custom_arrows = { down_arrow: '&#9650;' }
742
+ end
743
+ end
744
+
745
+ subject { @controller.view_context
746
+ .sort_link(
747
+ [:main_app, Person.search(sorts: ['name desc'])],
748
+ :name,
749
+ controller: 'people'
750
+ )
751
+ }
752
+
753
+ it { should_not match /&#9660;|down/ }
754
+ end
755
+
756
+ describe '#sort_link with config set to show arrows + custom arrow' do
757
+ before do
758
+ Ransack.configure do |c|
759
+ c.hide_sort_order_indicators = false
760
+ c.custom_arrows = { up_arrow: 'up-value' }
761
+ end
762
+ end
763
+
764
+ after do
765
+ Ransack.configure do |c|
766
+ c.hide_sort_order_indicators = false
767
+ c.custom_arrows = { up_arrow: '&#9660;' }
768
+ end
769
+ end
770
+
771
+ subject { @controller.view_context
772
+ .sort_link(
773
+ [:main_app, Person.search(sorts: ['name desc'])],
774
+ :name,
775
+ controller: 'people'
776
+ )
777
+ }
778
+
779
+ it { should match /&#9650;|up-value/ }
780
+ end
781
+
680
782
  describe '#sort_link with a block' do
681
783
  subject { @controller.view_context
682
784
  .sort_link(
@@ -329,6 +329,28 @@ module Ransack
329
329
  field = "#{quote_table_name("people")}.#{quote_column_name("name")}"
330
330
  expect(@s.result.to_sql).to match /#{field} IS NULL/
331
331
  end
332
+
333
+ describe 'with association qeury' do
334
+ it 'generates a value IS NOT NULL query' do
335
+ @s.comments_id_not_null = true
336
+ sql = @s.result.to_sql
337
+ parent_field = "#{quote_table_name("people")}.#{quote_column_name("id")}"
338
+ expect(sql).to match /#{parent_field} IN/
339
+ field = "#{quote_table_name("comments")}.#{quote_column_name("id")}"
340
+ expect(sql).to match /#{field} IS NOT NULL/
341
+ expect(sql).not_to match /AND NOT/
342
+ end
343
+
344
+ it 'generates a value IS NULL query when assigned false' do
345
+ @s.comments_id_not_null = false
346
+ sql = @s.result.to_sql
347
+ parent_field = "#{quote_table_name("people")}.#{quote_column_name("id")}"
348
+ expect(sql).to match /#{parent_field} NOT IN/
349
+ field = "#{quote_table_name("comments")}.#{quote_column_name("id")}"
350
+ expect(sql).to match /#{field} IS NULL/
351
+ expect(sql).to match /AND NOT/
352
+ end
353
+ end
332
354
  end
333
355
 
334
356
  describe 'present' do
@@ -127,6 +127,9 @@ class Person < ActiveRecord::Base
127
127
  end
128
128
  end
129
129
 
130
+ class Musician < Person
131
+ end
132
+
130
133
  class Article < ActiveRecord::Base
131
134
  belongs_to :person
132
135
  has_many :comments
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ransack
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.2
4
+ version: 1.8.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ernie Miller
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2016-08-08 00:00:00.000000000 Z
13
+ date: 2017-06-15 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: actionpack
@@ -317,7 +317,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
317
317
  version: '0'
318
318
  requirements: []
319
319
  rubyforge_project: ransack
320
- rubygems_version: 2.6.4
320
+ rubygems_version: 2.5.2
321
321
  signing_key:
322
322
  specification_version: 4
323
323
  summary: Object-based searching for Active Record and Mongoid (currently).