has_scope 0.7.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +35 -19
- data/lib/has_scope.rb +38 -17
- data/lib/has_scope/version.rb +1 -1
- data/test/has_scope_test.rb +213 -81
- data/test/test_helper.rb +1 -6
- metadata +11 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c24a5ed875375fb5da93be3f90ac863b0c72d676ecc1fe0959933924452b043d
|
4
|
+
data.tar.gz: 2f8aacdef384ce75c7fc2e4b4b697ba62e68293deeac5754597d10dffc044052
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9541480b36218fb3c1b9b7c48a3297411c8875ec47bb1735b6b394127c02ad90010d79787931c0d0154d662b40f4c236627710ca7b2fa01edb7513f27e043fb9
|
7
|
+
data.tar.gz: 5927fbd54e4a019b2d9f9fd7f7a60cf929fd46eac8f15c4c0e8cdb0cab2cac80a2b432e4398a70013d6bf88a519df60d2b6f571fb4f7f5db2d141292762b9eba
|
data/README.md
CHANGED
@@ -1,16 +1,15 @@
|
|
1
1
|
## HasScope
|
2
2
|
|
3
3
|
[](http://badge.fury.io/rb/has_scope)
|
4
|
-
[](https://codeclimate.com/github/plataformatec/has_scope)
|
4
|
+
[](https://codeclimate.com/github/heartcombo/has_scope)
|
6
5
|
|
7
6
|
Has scope allows you to map incoming controller parameters to named scopes in your resources.
|
8
7
|
Imagine the following model called graduations:
|
9
8
|
|
10
9
|
```ruby
|
11
10
|
class Graduation < ActiveRecord::Base
|
12
|
-
scope :featured, -> { where(:
|
13
|
-
scope :by_degree, -> degree { where(:
|
11
|
+
scope :featured, -> { where(featured: true) }
|
12
|
+
scope :by_degree, -> degree { where(degree: degree) }
|
14
13
|
scope :by_period, -> started_at, ended_at { where("started_at = ? AND ended_at = ?", started_at, ended_at) }
|
15
14
|
end
|
16
15
|
```
|
@@ -19,7 +18,7 @@ You can use those named scopes as filters by declaring them on your controller:
|
|
19
18
|
|
20
19
|
```ruby
|
21
20
|
class GraduationsController < ApplicationController
|
22
|
-
has_scope :featured, :
|
21
|
+
has_scope :featured, type: :boolean
|
23
22
|
has_scope :by_degree
|
24
23
|
end
|
25
24
|
```
|
@@ -28,9 +27,9 @@ Now, if you want to apply them to an specific resource, you just need to call `a
|
|
28
27
|
|
29
28
|
```ruby
|
30
29
|
class GraduationsController < ApplicationController
|
31
|
-
has_scope :featured, :
|
30
|
+
has_scope :featured, type: :boolean
|
32
31
|
has_scope :by_degree
|
33
|
-
has_scope :by_period, :
|
32
|
+
has_scope :by_period, using: %i[started_at ended_at], type: :hash
|
34
33
|
|
35
34
|
def index
|
36
35
|
@graduations = apply_scopes(Graduation).all
|
@@ -55,7 +54,7 @@ Then for each request:
|
|
55
54
|
```
|
56
55
|
|
57
56
|
You can retrieve all the scopes applied in one action with `current_scopes` method.
|
58
|
-
In the last case, it would return: { :
|
57
|
+
In the last case, it would return: `{ featured: true, by_degree: 'phd' }`.
|
59
58
|
|
60
59
|
## Installation
|
61
60
|
|
@@ -83,23 +82,23 @@ HasScope supports several options:
|
|
83
82
|
|
84
83
|
* `:using` - The subkeys to be used as args when type is a hash.
|
85
84
|
|
86
|
-
* `:
|
85
|
+
* `:in` - A shortcut for combining the `:using` option with nested hashes.
|
86
|
+
|
87
|
+
* `:if` - Specifies a method or proc to call to determine if the scope should apply. Passing a string is deprecated and it will be removed in a future version.
|
87
88
|
|
88
|
-
* `:unless` - Specifies a method
|
89
|
+
* `:unless` - Specifies a method or proc to call to determine if the scope should NOT apply. Passing a string is deprecated and it will be removed in a future version.
|
89
90
|
|
90
91
|
* `:default` - Default value for the scope. Whenever supplied the scope is always called.
|
91
92
|
|
92
93
|
* `:allow_blank` - Blank values are not sent to scopes by default. Set to true to overwrite.
|
93
94
|
|
94
|
-
* `:in` - A shortcut for combining the `:using` option with nested hashes.
|
95
|
-
|
96
95
|
## Boolean usage
|
97
96
|
|
98
97
|
If `type: :boolean` is set it just calls the named scope, without any arguments, when parameter
|
99
98
|
is set to a "true" value. `'true'` and `'1'` are parsed as `true`, everything else as `false`.
|
100
99
|
|
101
|
-
When boolean scope is set up with `allow_blank: true`, it will call the scope
|
102
|
-
|
100
|
+
When boolean scope is set up with `allow_blank: true`, it will call the scope with the value as
|
101
|
+
any usual scope.
|
103
102
|
|
104
103
|
```ruby
|
105
104
|
has_scope :visible, type: :boolean
|
@@ -110,6 +109,11 @@ scope :visible, -> { where(visible: true) }
|
|
110
109
|
scope :active, ->(value = true) { where(active: value) }
|
111
110
|
```
|
112
111
|
|
112
|
+
_Note_: it is not possible to apply a boolean scope with just the query param being present, e.g.
|
113
|
+
`?active`, that's not considered a "true" value (the param value will be `nil`), and thus the
|
114
|
+
scope will be called with `false` as argument. In order for the scope to receive a `true` argument
|
115
|
+
the param value must be set to one of the "true" values above, e.g. `?active=true` or `?active=1`.
|
116
|
+
|
113
117
|
## Block usage
|
114
118
|
|
115
119
|
`has_scope` also accepts a block. The controller, current scope and value are yielded
|
@@ -118,7 +122,7 @@ need to manipulate the given value:
|
|
118
122
|
|
119
123
|
```ruby
|
120
124
|
has_scope :category do |controller, scope, value|
|
121
|
-
|
125
|
+
value != 'all' ? scope.by_category(value) : scope
|
122
126
|
end
|
123
127
|
```
|
124
128
|
|
@@ -126,7 +130,7 @@ When used with booleans without `:allow_blank`, it just receives two arguments
|
|
126
130
|
and is just invoked if true is given:
|
127
131
|
|
128
132
|
```ruby
|
129
|
-
has_scope :not_voted_by_me, :
|
133
|
+
has_scope :not_voted_by_me, type: :boolean do |controller, scope|
|
130
134
|
scope.not_voted_by(controller.current_user.id)
|
131
135
|
end
|
132
136
|
```
|
@@ -159,10 +163,22 @@ scope :available, ->(*) { where(blocked: false) }
|
|
159
163
|
This will allow usual users to get only available items, but admins will
|
160
164
|
be able to access blocked items too.
|
161
165
|
|
166
|
+
## Check which scopes have been applied
|
167
|
+
|
168
|
+
To check which scopes have been applied, you can call `current_scopes` from the controller or view.
|
169
|
+
This returns a hash with the scope name as the key and the scope value as the value.
|
170
|
+
|
171
|
+
For example, if a boolean `:active` scope has been applied, `current_scopes` will return `{ active: true }`.
|
172
|
+
|
173
|
+
## Supported Ruby / Rails versions
|
174
|
+
|
175
|
+
We intend to maintain support for all Ruby / Rails versions that haven't reached end-of-life.
|
176
|
+
|
177
|
+
For more information about specific versions please check [Ruby](https://www.ruby-lang.org/en/downloads/branches/)
|
178
|
+
and [Rails](https://guides.rubyonrails.org/maintenance_policy.html) maintenance policies, and our test matrix.
|
179
|
+
|
162
180
|
## Bugs and Feedback
|
163
181
|
|
164
182
|
If you discover any bugs or want to drop a line, feel free to create an issue on GitHub.
|
165
183
|
|
166
|
-
http://
|
167
|
-
|
168
|
-
MIT License. Copyright 2009-2016 Plataformatec. http://blog.plataformatec.com.br
|
184
|
+
MIT License. Copyright 2009-2019 Plataformatec. http://blog.plataformatec.com.br
|
data/lib/has_scope.rb
CHANGED
@@ -1,17 +1,20 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'action_controller'
|
3
|
+
|
1
4
|
module HasScope
|
2
5
|
TRUE_VALUES = ["true", true, "1", 1]
|
3
6
|
|
4
7
|
ALLOWED_TYPES = {
|
5
|
-
:
|
6
|
-
:
|
7
|
-
:
|
8
|
-
:
|
8
|
+
array: [[ Array ]],
|
9
|
+
hash: [[ Hash, ActionController::Parameters ]],
|
10
|
+
boolean: [[ Object ], -> v { TRUE_VALUES.include?(v) }],
|
11
|
+
default: [[ String, Numeric ]],
|
9
12
|
}
|
10
13
|
|
11
14
|
def self.included(base)
|
12
15
|
base.class_eval do
|
13
16
|
extend ClassMethods
|
14
|
-
class_attribute :scopes_configuration, :
|
17
|
+
class_attribute :scopes_configuration, instance_writer: false
|
15
18
|
self.scopes_configuration = {}
|
16
19
|
end
|
17
20
|
end
|
@@ -36,6 +39,8 @@ module HasScope
|
|
36
39
|
# * <tt>:using</tt> - If type is a hash, you can provide :using to convert the hash to
|
37
40
|
# a named scope call with several arguments.
|
38
41
|
#
|
42
|
+
# * <tt>:in</tt> - A shortcut for combining the `:using` option with nested hashes.
|
43
|
+
#
|
39
44
|
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
|
40
45
|
# if the scope should apply
|
41
46
|
#
|
@@ -57,7 +62,7 @@ module HasScope
|
|
57
62
|
# value != "all" ? scope.by_category(value) : scope
|
58
63
|
# end
|
59
64
|
#
|
60
|
-
# has_scope :not_voted_by_me, :
|
65
|
+
# has_scope :not_voted_by_me, type: :boolean do |controller, scope|
|
61
66
|
# scope.not_voted_by(controller.current_user.id)
|
62
67
|
# end
|
63
68
|
#
|
@@ -69,6 +74,10 @@ module HasScope
|
|
69
74
|
if options.key?(:in)
|
70
75
|
options[:as] = options[:in]
|
71
76
|
options[:using] = scopes
|
77
|
+
|
78
|
+
if options.key?(:default) && !options[:default].is_a?(Hash)
|
79
|
+
options[:default] = scopes.each_with_object({}) { |scope, hash| hash[scope] = options[:default] }
|
80
|
+
end
|
72
81
|
end
|
73
82
|
|
74
83
|
if options.key?(:using)
|
@@ -87,7 +96,7 @@ module HasScope
|
|
87
96
|
self.scopes_configuration = scopes_configuration.dup
|
88
97
|
|
89
98
|
scopes.each do |scope|
|
90
|
-
scopes_configuration[scope] ||= { :
|
99
|
+
scopes_configuration[scope] ||= { as: scope, type: :default, block: block }
|
91
100
|
scopes_configuration[scope] = self.scopes_configuration[scope].merge(options)
|
92
101
|
end
|
93
102
|
end
|
@@ -97,16 +106,16 @@ module HasScope
|
|
97
106
|
|
98
107
|
# Receives an object where scopes will be applied to.
|
99
108
|
#
|
100
|
-
# class GraduationsController <
|
101
|
-
# has_scope :featured, :
|
102
|
-
# has_scope :by_degree, :
|
109
|
+
# class GraduationsController < ApplicationController
|
110
|
+
# has_scope :featured, type: true, only: :index
|
111
|
+
# has_scope :by_degree, only: :index
|
103
112
|
#
|
104
113
|
# def index
|
105
114
|
# @graduations = apply_scopes(Graduation).all
|
106
115
|
# end
|
107
116
|
# end
|
108
117
|
#
|
109
|
-
def apply_scopes(target, hash=params)
|
118
|
+
def apply_scopes(target, hash = params)
|
110
119
|
scopes_configuration.each do |scope, options|
|
111
120
|
next unless apply_scope_to_action?(options)
|
112
121
|
key = options[:as]
|
@@ -120,12 +129,20 @@ module HasScope
|
|
120
129
|
end
|
121
130
|
end
|
122
131
|
|
123
|
-
value = parse_value(options[:type],
|
132
|
+
value = parse_value(options[:type], value)
|
124
133
|
value = normalize_blanks(value)
|
125
134
|
|
126
|
-
if
|
135
|
+
if value && options.key?(:using)
|
136
|
+
scope_value = value.values_at(*options[:using])
|
137
|
+
call_scope &&= scope_value.all?(&:present?) || options[:allow_blank]
|
138
|
+
else
|
139
|
+
scope_value = value
|
140
|
+
call_scope &&= value.present? || options[:allow_blank]
|
141
|
+
end
|
142
|
+
|
143
|
+
if call_scope
|
127
144
|
current_scopes[key] = value
|
128
|
-
target = call_scope_by_type(options[:type], scope, target,
|
145
|
+
target = call_scope_by_type(options[:type], scope, target, scope_value, options)
|
129
146
|
end
|
130
147
|
end
|
131
148
|
|
@@ -133,7 +150,7 @@ module HasScope
|
|
133
150
|
end
|
134
151
|
|
135
152
|
# Set the real value for the current scope if type check.
|
136
|
-
def parse_value(type,
|
153
|
+
def parse_value(type, value) #:nodoc:
|
137
154
|
klasses, parser = ALLOWED_TYPES[type]
|
138
155
|
if klasses.any? { |klass| value.is_a?(klass) }
|
139
156
|
parser ? parser.call(value) : value
|
@@ -160,8 +177,7 @@ module HasScope
|
|
160
177
|
|
161
178
|
if type == :boolean && !options[:allow_blank]
|
162
179
|
block ? block.call(self, target) : target.send(scope)
|
163
|
-
elsif
|
164
|
-
value = value.values_at(*options[:using])
|
180
|
+
elsif options.key?(:using)
|
165
181
|
block ? block.call(self, target, value) : target.send(scope, *value)
|
166
182
|
else
|
167
183
|
block ? block.call(self, target, value) : target.send(scope, value)
|
@@ -185,6 +201,11 @@ module HasScope
|
|
185
201
|
def applicable?(string_proc_or_symbol, expected) #:nodoc:
|
186
202
|
case string_proc_or_symbol
|
187
203
|
when String
|
204
|
+
ActiveSupport::Deprecation.warn <<-DEPRECATION.squish
|
205
|
+
[HasScope] Passing a string to determine if the scope should be applied
|
206
|
+
is deprecated and it will be removed in a future version of HasScope.
|
207
|
+
DEPRECATION
|
208
|
+
|
188
209
|
eval(string_proc_or_symbol) == expected
|
189
210
|
when Proc
|
190
211
|
string_proc_or_symbol.call(self) == expected
|
data/lib/has_scope/version.rb
CHANGED
data/test/has_scope_test.rb
CHANGED
@@ -5,21 +5,30 @@ HasScope::ALLOWED_TYPES[:date] = [[String], -> v { Date.parse(v) rescue nil }]
|
|
5
5
|
class Tree; end
|
6
6
|
|
7
7
|
class TreesController < ApplicationController
|
8
|
-
has_scope :color, :
|
9
|
-
has_scope :only_tall, :
|
10
|
-
has_scope :shadown_range, :
|
11
|
-
has_scope :root_type, :
|
12
|
-
has_scope :planted_before, :
|
13
|
-
has_scope :planted_after, :
|
14
|
-
has_scope :calculate_height, :
|
15
|
-
has_scope :paginate, :
|
16
|
-
has_scope :
|
17
|
-
has_scope :
|
18
|
-
has_scope :
|
19
|
-
has_scope :
|
20
|
-
has_scope :
|
21
|
-
|
22
|
-
has_scope :
|
8
|
+
has_scope :color, unless: :show_all_colors?
|
9
|
+
has_scope :only_tall, type: :boolean, only: :index, if: :restrict_to_only_tall_trees?
|
10
|
+
has_scope :shadown_range, default: 10, except: [ :index, :show, :new ]
|
11
|
+
has_scope :root_type, as: :root, allow_blank: true
|
12
|
+
has_scope :planted_before, default: proc { Date.today }
|
13
|
+
has_scope :planted_after, type: :date
|
14
|
+
has_scope :calculate_height, default: proc { |c| c.session[:height] || 20 }, only: :new
|
15
|
+
has_scope :paginate, type: :hash
|
16
|
+
has_scope :paginate_blank, type: :hash, allow_blank: true
|
17
|
+
has_scope :paginate_default, type: :hash, default: { page: 1, per_page: 10 }, only: :edit
|
18
|
+
has_scope :args_paginate, type: :hash, using: [:page, :per_page]
|
19
|
+
has_scope :args_paginate_blank, using: [:page, :per_page], allow_blank: true
|
20
|
+
has_scope :args_paginate_default, using: [:page, :per_page], default: { page: 1, per_page: 10 }, only: :edit
|
21
|
+
has_scope :categories, type: :array
|
22
|
+
has_scope :title, in: :q
|
23
|
+
has_scope :content, in: :q
|
24
|
+
has_scope :metadata, in: :q
|
25
|
+
has_scope :metadata_blank, in: :q, allow_blank: true
|
26
|
+
has_scope :metadata_default, in: :q, default: "default", only: :edit
|
27
|
+
has_scope :conifer, type: :boolean, allow_blank: true
|
28
|
+
has_scope :eval_plant, if: "params[:eval_plant].present?", unless: "params[:skip_eval_plant].present?"
|
29
|
+
has_scope :proc_plant, if: -> c { c.params[:proc_plant].present? }, unless: -> c { c.params[:skip_proc_plant].present? }
|
30
|
+
|
31
|
+
has_scope :only_short, type: :boolean do |controller, scope|
|
23
32
|
scope.only_really_short!(controller.object_id)
|
24
33
|
end
|
25
34
|
|
@@ -42,6 +51,16 @@ class TreesController < ApplicationController
|
|
42
51
|
alias :edit :show
|
43
52
|
|
44
53
|
protected
|
54
|
+
# Silence deprecations in the test suite, except for the actual deprecated String if/unless options.
|
55
|
+
# TODO: remove with the deprecation.
|
56
|
+
def apply_scopes(*)
|
57
|
+
if params[:eval_plant]
|
58
|
+
super
|
59
|
+
else
|
60
|
+
ActiveSupport::Deprecation.silence { super }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
45
64
|
def restrict_to_only_tall_trees?
|
46
65
|
true
|
47
66
|
end
|
@@ -50,20 +69,13 @@ class TreesController < ApplicationController
|
|
50
69
|
false
|
51
70
|
end
|
52
71
|
|
53
|
-
|
54
|
-
|
55
|
-
render body: action_name
|
56
|
-
end
|
57
|
-
else
|
58
|
-
# TODO: Remove this when we only support Rails 5.
|
59
|
-
def default_render
|
60
|
-
render text: action_name
|
61
|
-
end
|
72
|
+
def default_render
|
73
|
+
render body: action_name
|
62
74
|
end
|
63
75
|
end
|
64
76
|
|
65
77
|
class BonsaisController < TreesController
|
66
|
-
has_scope :categories, :
|
78
|
+
has_scope :categories, if: :categories?
|
67
79
|
|
68
80
|
protected
|
69
81
|
def categories?
|
@@ -77,15 +89,19 @@ class HasScopeTest < ActionController::TestCase
|
|
77
89
|
def test_boolean_scope_is_called_when_boolean_param_is_true
|
78
90
|
Tree.expects(:only_tall).with().returns(Tree).in_sequence
|
79
91
|
Tree.expects(:all).returns([mock_tree]).in_sequence
|
80
|
-
|
92
|
+
|
93
|
+
get :index, params: { only_tall: 'true' }
|
94
|
+
|
81
95
|
assert_equal([mock_tree], assigns(:@trees))
|
82
|
-
assert_equal({ :
|
96
|
+
assert_equal({ only_tall: true }, current_scopes)
|
83
97
|
end
|
84
98
|
|
85
99
|
def test_boolean_scope_is_not_called_when_boolean_param_is_false
|
86
100
|
Tree.expects(:only_tall).never
|
87
101
|
Tree.expects(:all).returns([mock_tree])
|
88
|
-
|
102
|
+
|
103
|
+
get :index, params: { only_tall: 'false' }
|
104
|
+
|
89
105
|
assert_equal([mock_tree], assigns(:@trees))
|
90
106
|
assert_equal({ }, current_scopes)
|
91
107
|
end
|
@@ -93,23 +109,29 @@ class HasScopeTest < ActionController::TestCase
|
|
93
109
|
def test_boolean_scope_with_allow_blank_is_called_when_boolean_param_is_true
|
94
110
|
Tree.expects(:conifer).with(true).returns(Tree).in_sequence
|
95
111
|
Tree.expects(:all).returns([mock_tree]).in_sequence
|
96
|
-
|
112
|
+
|
113
|
+
get :index, params: { conifer: 'true' }
|
114
|
+
|
97
115
|
assert_equal([mock_tree], assigns(:@trees))
|
98
|
-
assert_equal({ :
|
116
|
+
assert_equal({ conifer: true }, current_scopes)
|
99
117
|
end
|
100
118
|
|
101
119
|
def test_boolean_scope_with_allow_blank_is_called_when_boolean_param_is_false
|
102
120
|
Tree.expects(:conifer).with(false).returns(Tree).in_sequence
|
103
121
|
Tree.expects(:all).returns([mock_tree]).in_sequence
|
104
|
-
|
122
|
+
|
123
|
+
get :index, params: { conifer: 'not_true' }
|
124
|
+
|
105
125
|
assert_equal([mock_tree], assigns(:@trees))
|
106
|
-
assert_equal({ :
|
126
|
+
assert_equal({ conifer: false }, current_scopes)
|
107
127
|
end
|
108
128
|
|
109
129
|
def test_boolean_scope_with_allow_blank_is_not_called_when_boolean_param_is_not_present
|
110
130
|
Tree.expects(:conifer).never
|
111
131
|
Tree.expects(:all).returns([mock_tree])
|
132
|
+
|
112
133
|
get :index
|
134
|
+
|
113
135
|
assert_equal([mock_tree], assigns(:@trees))
|
114
136
|
assert_equal({ }, current_scopes)
|
115
137
|
end
|
@@ -117,7 +139,9 @@ class HasScopeTest < ActionController::TestCase
|
|
117
139
|
def test_scope_is_called_only_on_index
|
118
140
|
Tree.expects(:only_tall).never
|
119
141
|
Tree.expects(:find).with('42').returns(mock_tree)
|
120
|
-
|
142
|
+
|
143
|
+
get :show, params: { only_tall: 'true', id: '42' }
|
144
|
+
|
121
145
|
assert_equal(mock_tree, assigns(:@tree))
|
122
146
|
assert_equal({ }, current_scopes)
|
123
147
|
end
|
@@ -126,7 +150,9 @@ class HasScopeTest < ActionController::TestCase
|
|
126
150
|
@controller.stubs(:restrict_to_only_tall_trees?).returns(false)
|
127
151
|
Tree.expects(:only_tall).never
|
128
152
|
Tree.expects(:all).returns([mock_tree])
|
129
|
-
|
153
|
+
|
154
|
+
get :index, params: { only_tall: 'true' }
|
155
|
+
|
130
156
|
assert_equal([mock_tree], assigns(:@trees))
|
131
157
|
assert_equal({ }, current_scopes)
|
132
158
|
end
|
@@ -135,15 +161,41 @@ class HasScopeTest < ActionController::TestCase
|
|
135
161
|
@controller.stubs(:show_all_colors?).returns(true)
|
136
162
|
Tree.expects(:color).never
|
137
163
|
Tree.expects(:all).returns([mock_tree])
|
138
|
-
|
164
|
+
|
165
|
+
get :index, params: { color: 'blue' }
|
166
|
+
|
139
167
|
assert_equal([mock_tree], assigns(:@trees))
|
140
168
|
assert_equal({ }, current_scopes)
|
141
169
|
end
|
142
170
|
|
171
|
+
def test_scope_with_eval_string_if_and_unless_options_is_deprecated
|
172
|
+
Tree.expects(:eval_plant).with('value').returns(Tree)
|
173
|
+
Tree.expects(:all).returns([mock_tree])
|
174
|
+
|
175
|
+
assert_deprecated(/Passing a string to determine if the scope should be applied is deprecated/) do
|
176
|
+
get :index, params: { eval_plant: 'value', skip_eval_plant: nil }
|
177
|
+
end
|
178
|
+
|
179
|
+
assert_equal([mock_tree], assigns(:@trees))
|
180
|
+
assert_equal({ eval_plant: 'value' }, current_scopes)
|
181
|
+
end
|
182
|
+
|
183
|
+
def test_scope_with_proc_if_and_unless_options
|
184
|
+
Tree.expects(:proc_plant).with('value').returns(Tree)
|
185
|
+
Tree.expects(:all).returns([mock_tree])
|
186
|
+
|
187
|
+
get :index, params: { proc_plant: 'value', skip_proc_plant: nil }
|
188
|
+
|
189
|
+
assert_equal([mock_tree], assigns(:@trees))
|
190
|
+
assert_equal({ proc_plant: 'value' }, current_scopes)
|
191
|
+
end
|
192
|
+
|
143
193
|
def test_scope_is_called_except_on_index
|
144
194
|
Tree.expects(:shadown_range).never
|
145
195
|
Tree.expects(:all).returns([mock_tree])
|
146
|
-
|
196
|
+
|
197
|
+
get :index, params: { shadown_range: 20 }
|
198
|
+
|
147
199
|
assert_equal([mock_tree], assigns(:@trees))
|
148
200
|
assert_equal({ }, current_scopes)
|
149
201
|
end
|
@@ -151,15 +203,19 @@ class HasScopeTest < ActionController::TestCase
|
|
151
203
|
def test_scope_is_called_with_arguments
|
152
204
|
Tree.expects(:color).with('blue').returns(Tree).in_sequence
|
153
205
|
Tree.expects(:all).returns([mock_tree]).in_sequence
|
154
|
-
|
206
|
+
|
207
|
+
get :index, params: { color: 'blue' }
|
208
|
+
|
155
209
|
assert_equal([mock_tree], assigns(:@trees))
|
156
|
-
assert_equal({ :
|
210
|
+
assert_equal({ color: 'blue' }, current_scopes)
|
157
211
|
end
|
158
212
|
|
159
213
|
def test_scope_is_not_called_if_blank
|
160
214
|
Tree.expects(:color).never
|
161
215
|
Tree.expects(:all).returns([mock_tree]).in_sequence
|
162
|
-
|
216
|
+
|
217
|
+
get :index, params: { color: '' }
|
218
|
+
|
163
219
|
assert_equal([mock_tree], assigns(:@trees))
|
164
220
|
assert_equal({ }, current_scopes)
|
165
221
|
end
|
@@ -167,25 +223,31 @@ class HasScopeTest < ActionController::TestCase
|
|
167
223
|
def test_scope_is_called_when_blank_if_allow_blank_is_given
|
168
224
|
Tree.expects(:root_type).with('').returns(Tree)
|
169
225
|
Tree.expects(:all).returns([mock_tree]).in_sequence
|
170
|
-
|
226
|
+
|
227
|
+
get :index, params: { root: '' }
|
228
|
+
|
171
229
|
assert_equal([mock_tree], assigns(:@trees))
|
172
|
-
assert_equal({ :
|
230
|
+
assert_equal({ root: '' }, current_scopes)
|
173
231
|
end
|
174
232
|
|
175
233
|
def test_multiple_scopes_are_called
|
176
234
|
Tree.expects(:only_tall).with().returns(Tree)
|
177
235
|
Tree.expects(:color).with('blue').returns(Tree)
|
178
236
|
Tree.expects(:all).returns([mock_tree])
|
179
|
-
|
237
|
+
|
238
|
+
get :index, params: { color: 'blue', only_tall: 'true' }
|
239
|
+
|
180
240
|
assert_equal([mock_tree], assigns(:@trees))
|
181
|
-
assert_equal({ :
|
241
|
+
assert_equal({ color: 'blue', only_tall: true }, current_scopes)
|
182
242
|
end
|
183
243
|
|
184
244
|
def test_scope_of_type_hash
|
185
245
|
hash = { "page" => "1", "per_page" => "10" }
|
186
246
|
Tree.expects(:paginate).with(hash).returns(Tree)
|
187
247
|
Tree.expects(:all).returns([mock_tree])
|
188
|
-
|
248
|
+
|
249
|
+
get :index, params: { paginate: hash }
|
250
|
+
|
189
251
|
assert_equal([mock_tree], assigns(:@trees))
|
190
252
|
assert_equal({ paginate: hash }, current_scopes)
|
191
253
|
end
|
@@ -194,25 +256,53 @@ class HasScopeTest < ActionController::TestCase
|
|
194
256
|
hash = { "page" => "1", "per_page" => "10" }
|
195
257
|
Tree.expects(:args_paginate).with("1", "10").returns(Tree)
|
196
258
|
Tree.expects(:all).returns([mock_tree])
|
197
|
-
|
259
|
+
|
260
|
+
get :index, params: { args_paginate: hash }
|
261
|
+
|
198
262
|
assert_equal([mock_tree], assigns(:@trees))
|
199
|
-
assert_equal({ :
|
263
|
+
assert_equal({ args_paginate: hash }, current_scopes)
|
200
264
|
end
|
201
265
|
|
202
266
|
def test_hash_with_blank_values_is_ignored
|
203
267
|
hash = { "page" => "", "per_page" => "" }
|
204
268
|
Tree.expects(:paginate).never
|
205
269
|
Tree.expects(:all).returns([mock_tree])
|
206
|
-
|
270
|
+
|
271
|
+
get :index, params: { paginate: hash }
|
272
|
+
|
207
273
|
assert_equal([mock_tree], assigns(:@trees))
|
208
274
|
assert_equal({ }, current_scopes)
|
209
275
|
end
|
210
276
|
|
277
|
+
def test_hash_with_blank_values_and_allow_blank_is_called
|
278
|
+
hash = { "page" => "", "per_page" => "" }
|
279
|
+
Tree.expects(:paginate_blank).with({}).returns(Tree)
|
280
|
+
Tree.expects(:all).returns([mock_tree])
|
281
|
+
|
282
|
+
get :index, params: { paginate_blank: hash }
|
283
|
+
|
284
|
+
assert_equal([mock_tree], assigns(:@trees))
|
285
|
+
assert_equal({ paginate_blank: {} }, current_scopes)
|
286
|
+
end
|
287
|
+
|
288
|
+
def test_hash_with_using_and_blank_values_and_allow_blank_is_called
|
289
|
+
hash = { "page" => "", "per_page" => "" }
|
290
|
+
Tree.expects(:args_paginate_blank).with(nil, nil).returns(Tree)
|
291
|
+
Tree.expects(:all).returns([mock_tree])
|
292
|
+
|
293
|
+
get :index, params: { args_paginate_blank: hash }
|
294
|
+
|
295
|
+
assert_equal([mock_tree], assigns(:@trees))
|
296
|
+
assert_equal({ args_paginate_blank: {} }, current_scopes)
|
297
|
+
end
|
298
|
+
|
211
299
|
def test_nested_hash_with_blank_values_is_ignored
|
212
|
-
hash = { "parent" => {"children" => ""} }
|
300
|
+
hash = { "parent" => { "children" => "" } }
|
213
301
|
Tree.expects(:paginate).never
|
214
302
|
Tree.expects(:all).returns([mock_tree])
|
215
|
-
|
303
|
+
|
304
|
+
get :index, params: { paginate: hash }
|
305
|
+
|
216
306
|
assert_equal([mock_tree], assigns(:@trees))
|
217
307
|
assert_equal({ }, current_scopes)
|
218
308
|
end
|
@@ -221,7 +311,9 @@ class HasScopeTest < ActionController::TestCase
|
|
221
311
|
hash = { "parent" => [""] }
|
222
312
|
Tree.expects(:paginate).never
|
223
313
|
Tree.expects(:all).returns([mock_tree])
|
224
|
-
|
314
|
+
|
315
|
+
get :index, params: { paginate: hash }
|
316
|
+
|
225
317
|
assert_equal([mock_tree], assigns(:@trees))
|
226
318
|
assert_equal({ }, current_scopes)
|
227
319
|
end
|
@@ -230,99 +322,148 @@ class HasScopeTest < ActionController::TestCase
|
|
230
322
|
array = %w(book kitchen sport)
|
231
323
|
Tree.expects(:categories).with(array).returns(Tree)
|
232
324
|
Tree.expects(:all).returns([mock_tree])
|
233
|
-
|
325
|
+
|
326
|
+
get :index, params: { categories: array }
|
327
|
+
|
234
328
|
assert_equal([mock_tree], assigns(:@trees))
|
235
|
-
assert_equal({ :
|
329
|
+
assert_equal({ categories: array }, current_scopes)
|
236
330
|
end
|
237
331
|
|
238
332
|
def test_array_of_blank_values_is_ignored
|
239
333
|
Tree.expects(:categories).never
|
240
334
|
Tree.expects(:all).returns([mock_tree])
|
241
|
-
|
335
|
+
|
336
|
+
get :index, params: { categories: [""] }
|
337
|
+
|
242
338
|
assert_equal([mock_tree], assigns(:@trees))
|
243
339
|
assert_equal({ }, current_scopes)
|
244
340
|
end
|
245
341
|
|
246
342
|
def test_scope_of_invalid_type_silently_fails
|
247
343
|
Tree.expects(:all).returns([mock_tree])
|
248
|
-
|
344
|
+
|
345
|
+
get :index, params: { paginate: "1" }
|
346
|
+
|
249
347
|
assert_equal([mock_tree], assigns(:@trees))
|
250
348
|
assert_equal({ }, current_scopes)
|
251
349
|
end
|
252
350
|
|
253
351
|
def test_scope_is_called_with_default_value
|
254
352
|
Tree.expects(:shadown_range).with(10).returns(Tree).in_sequence
|
353
|
+
Tree.expects(:paginate_default).with('page' => 1, 'per_page' => 10).returns(Tree).in_sequence
|
354
|
+
Tree.expects(:args_paginate_default).with(1, 10).returns(Tree).in_sequence
|
355
|
+
Tree.expects(:metadata_default).with('default').returns(Tree).in_sequence
|
255
356
|
Tree.expects(:find).with('42').returns(mock_tree).in_sequence
|
256
|
-
|
357
|
+
|
358
|
+
get :edit, params: { id: '42' }
|
359
|
+
|
257
360
|
assert_equal(mock_tree, assigns(:@tree))
|
258
|
-
assert_equal({
|
361
|
+
assert_equal({
|
362
|
+
shadown_range: 10,
|
363
|
+
paginate_default: { 'page' => 1, 'per_page' => 10 },
|
364
|
+
args_paginate_default: { 'page' => 1, 'per_page' => 10 },
|
365
|
+
q: { 'metadata_default' => 'default' }
|
366
|
+
}, current_scopes)
|
259
367
|
end
|
260
368
|
|
261
369
|
def test_default_scope_value_can_be_overwritten
|
262
370
|
Tree.expects(:shadown_range).with('20').returns(Tree).in_sequence
|
371
|
+
Tree.expects(:paginate_default).with('page' => '2', 'per_page' => '20').returns(Tree).in_sequence
|
372
|
+
Tree.expects(:args_paginate_default).with('3', '15').returns(Tree).in_sequence
|
373
|
+
Tree.expects(:metadata_blank).with(nil).returns(Tree).in_sequence
|
374
|
+
Tree.expects(:metadata_default).with('other').returns(Tree).in_sequence
|
263
375
|
Tree.expects(:find).with('42').returns(mock_tree).in_sequence
|
264
|
-
|
376
|
+
|
377
|
+
get :edit, params: {
|
378
|
+
id: '42',
|
379
|
+
shadown_range: '20',
|
380
|
+
paginate_default: { page: 2, per_page: 20 },
|
381
|
+
args_paginate_default: { page: 3, per_page: 15},
|
382
|
+
q: { metadata_default: 'other' }
|
383
|
+
}
|
384
|
+
|
265
385
|
assert_equal(mock_tree, assigns(:@tree))
|
266
|
-
assert_equal({
|
386
|
+
assert_equal({
|
387
|
+
shadown_range: '20',
|
388
|
+
paginate_default: { 'page' => '2', 'per_page' => '20' },
|
389
|
+
args_paginate_default: { 'page' => '3', 'per_page' => '15' },
|
390
|
+
q: { 'metadata_default' => 'other' }
|
391
|
+
}, current_scopes)
|
267
392
|
end
|
268
393
|
|
269
394
|
def test_scope_with_different_key
|
270
395
|
Tree.expects(:root_type).with('outside').returns(Tree).in_sequence
|
271
396
|
Tree.expects(:find).with('42').returns(mock_tree).in_sequence
|
272
|
-
|
397
|
+
|
398
|
+
get :show, params: { id: '42', root: 'outside' }
|
399
|
+
|
273
400
|
assert_equal(mock_tree, assigns(:@tree))
|
274
|
-
assert_equal({ :
|
401
|
+
assert_equal({ root: 'outside' }, current_scopes)
|
275
402
|
end
|
276
403
|
|
277
404
|
def test_scope_with_default_value_as_a_proc_without_argument
|
278
405
|
Date.expects(:today).returns("today")
|
279
406
|
Tree.expects(:planted_before).with("today").returns(Tree)
|
280
407
|
Tree.expects(:all).returns([mock_tree])
|
408
|
+
|
281
409
|
get :index
|
410
|
+
|
282
411
|
assert_equal([mock_tree], assigns(:@trees))
|
283
|
-
assert_equal({ :
|
412
|
+
assert_equal({ planted_before: "today" }, current_scopes)
|
284
413
|
end
|
285
414
|
|
286
415
|
def test_scope_with_default_value_as_proc_with_argument
|
287
416
|
session[:height] = 100
|
288
417
|
Tree.expects(:calculate_height).with(100).returns(Tree).in_sequence
|
289
418
|
Tree.expects(:new).returns(mock_tree).in_sequence
|
419
|
+
|
290
420
|
get :new
|
421
|
+
|
291
422
|
assert_equal(mock_tree, assigns(:@tree))
|
292
|
-
assert_equal({ :
|
423
|
+
assert_equal({ calculate_height: 100 }, current_scopes)
|
293
424
|
end
|
294
425
|
|
295
426
|
def test_scope_with_custom_type
|
296
427
|
parsed = Date.civil(2014,11,11)
|
297
428
|
Tree.expects(:planted_after).with(parsed).returns(Tree)
|
298
429
|
Tree.expects(:all).returns([mock_tree])
|
299
|
-
|
430
|
+
|
431
|
+
get :index, params: { planted_after: "2014-11-11" }
|
432
|
+
|
300
433
|
assert_equal([mock_tree], assigns(:@trees))
|
301
|
-
assert_equal({ :
|
434
|
+
assert_equal({ planted_after: parsed }, current_scopes)
|
302
435
|
end
|
303
436
|
|
304
437
|
def test_scope_with_boolean_block
|
305
438
|
Tree.expects(:only_really_short!).with(@controller.object_id).returns(Tree)
|
306
439
|
Tree.expects(:all).returns([mock_tree])
|
307
|
-
|
440
|
+
|
441
|
+
get :index, params: { only_short: 'true' }
|
442
|
+
|
308
443
|
assert_equal([mock_tree], assigns(:@trees))
|
309
|
-
assert_equal({ :
|
444
|
+
assert_equal({ only_short: true }, current_scopes)
|
310
445
|
end
|
311
446
|
|
312
447
|
def test_scope_with_other_block_types
|
313
448
|
Tree.expects(:by_given_category).with(@controller.object_id, 'for_id').returns(Tree)
|
314
449
|
Tree.expects(:all).returns([mock_tree])
|
315
|
-
|
450
|
+
|
451
|
+
get :index, params: { by_category: 'for' }
|
452
|
+
|
316
453
|
assert_equal([mock_tree], assigns(:@trees))
|
317
|
-
assert_equal({ :
|
454
|
+
assert_equal({ by_category: 'for' }, current_scopes)
|
318
455
|
end
|
319
456
|
|
320
457
|
def test_scope_with_nested_hash_and_in_option
|
321
458
|
hash = { 'title' => 'the-title', 'content' => 'the-content' }
|
322
459
|
Tree.expects(:title).with('the-title').returns(Tree)
|
323
460
|
Tree.expects(:content).with('the-content').returns(Tree)
|
461
|
+
Tree.expects(:metadata).never
|
462
|
+
Tree.expects(:metadata_blank).with(nil).returns(Tree)
|
324
463
|
Tree.expects(:all).returns([mock_tree])
|
325
|
-
|
464
|
+
|
465
|
+
get :index, params: { q: hash }
|
466
|
+
|
326
467
|
assert_equal([mock_tree], assigns(:@trees))
|
327
468
|
assert_equal({ q: hash }, current_scopes)
|
328
469
|
end
|
@@ -334,14 +475,7 @@ class HasScopeTest < ActionController::TestCase
|
|
334
475
|
|
335
476
|
protected
|
336
477
|
|
337
|
-
|
338
|
-
# TODO: Remove this when we only support Rails 5.
|
339
|
-
def get(action, params = {})
|
340
|
-
super action, params: params
|
341
|
-
end
|
342
|
-
end
|
343
|
-
|
344
|
-
def mock_tree(stubs={})
|
478
|
+
def mock_tree(stubs = {})
|
345
479
|
@mock_tree ||= mock(stubs)
|
346
480
|
end
|
347
481
|
|
@@ -360,16 +494,14 @@ class TreeHugger
|
|
360
494
|
has_scope :color
|
361
495
|
|
362
496
|
def by_color
|
363
|
-
apply_scopes(Tree, :
|
497
|
+
apply_scopes(Tree, color: 'blue')
|
364
498
|
end
|
365
|
-
|
366
499
|
end
|
367
500
|
|
368
501
|
class HasScopeOutsideControllerTest < ActiveSupport::TestCase
|
369
|
-
|
370
502
|
def test_has_scope_usable_outside_controller
|
371
503
|
Tree.expects(:color).with('blue')
|
504
|
+
|
372
505
|
TreeHugger.new.by_color
|
373
506
|
end
|
374
|
-
|
375
507
|
end
|
data/test/test_helper.rb
CHANGED
@@ -7,17 +7,12 @@ require 'mocha/mini_test'
|
|
7
7
|
# Configure Rails
|
8
8
|
ENV['RAILS_ENV'] = 'test'
|
9
9
|
|
10
|
-
require 'active_support'
|
11
|
-
require 'active_support/core_ext/string/strip'
|
12
|
-
require 'action_controller'
|
13
|
-
require 'action_dispatch/middleware/flash'
|
14
|
-
|
15
10
|
$:.unshift File.expand_path('../../lib', __FILE__)
|
16
11
|
require 'has_scope'
|
17
12
|
|
18
13
|
HasScope::Routes = ActionDispatch::Routing::RouteSet.new
|
19
14
|
HasScope::Routes.draw do
|
20
|
-
|
15
|
+
resources :trees, only: %i[index new edit show]
|
21
16
|
end
|
22
17
|
|
23
18
|
class ApplicationController < ActionController::Base
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: has_scope
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- José Valim
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-02-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionpack
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '5.2'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '5.2'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activesupport
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '5.2'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '5.2'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -83,7 +83,7 @@ homepage: http://github.com/plataformatec/has_scope
|
|
83
83
|
licenses:
|
84
84
|
- MIT
|
85
85
|
metadata: {}
|
86
|
-
post_install_message:
|
86
|
+
post_install_message:
|
87
87
|
rdoc_options:
|
88
88
|
- "--charset=UTF-8"
|
89
89
|
require_paths:
|
@@ -92,16 +92,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
92
92
|
requirements:
|
93
93
|
- - ">="
|
94
94
|
- !ruby/object:Gem::Version
|
95
|
-
version: 2.
|
95
|
+
version: 2.5.0
|
96
96
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
97
|
requirements:
|
98
98
|
- - ">="
|
99
99
|
- !ruby/object:Gem::Version
|
100
100
|
version: '0'
|
101
101
|
requirements: []
|
102
|
-
|
103
|
-
|
104
|
-
signing_key:
|
102
|
+
rubygems_version: 3.2.6
|
103
|
+
signing_key:
|
105
104
|
specification_version: 4
|
106
105
|
summary: Maps controller filters to your resource scopes.
|
107
106
|
test_files:
|