has_scope 0.8.0 → 0.8.1
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 +138 -22
- data/lib/has_scope/version.rb +1 -1
- metadata +9 -9
- data/test/has_scope_test.rb +0 -507
- data/test/test_helper.rb +0 -28
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: abf0ad461389302c6eef573bc313b2f0753a7dfaa02c23fdf685acce47be111c
|
|
4
|
+
data.tar.gz: 79209897cf738ba373be55f2b9fe4543d15880ae466c9a577e23cc1abb06ee3d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 04a17d0ea9ee72e18ad98fa79c9d4e3a856219673fe066d1326df8b12af29a22f80282f0ec4cb8de9e25653d5f1179137fa4a8475d5fd8bf247ff12f3ab8c20e
|
|
7
|
+
data.tar.gz: 87978f08fed8aec6fe006b0d9faee4704b831a11b8b61b1c1211537965d4cd59a85784229d0e3260c49bd7f1313591ba48ac2feb0580417cb69ca1c60d435ad4
|
data/README.md
CHANGED
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
## HasScope
|
|
2
2
|
|
|
3
3
|
[](http://badge.fury.io/rb/has_scope)
|
|
4
|
-
[](https://codeclimate.com/github/heartcombo/has_scope)
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
_HasScope_ allows you to dynamically apply named scopes to your resources based on an incoming set of parameters.
|
|
6
|
+
|
|
7
|
+
The most common usage is to map incoming controller parameters to named scopes for filtering resources, but it can be used anywhere.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
Add `has_scope` to your bundle
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
bundle add has_scope
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
or add it manually to your Gemfile if you prefer.
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
gem 'has_scope'
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Examples
|
|
24
|
+
|
|
25
|
+
For the following examples we'll use a model called graduations:
|
|
8
26
|
|
|
9
27
|
```ruby
|
|
10
28
|
class Graduation < ActiveRecord::Base
|
|
@@ -14,16 +32,19 @@ class Graduation < ActiveRecord::Base
|
|
|
14
32
|
end
|
|
15
33
|
```
|
|
16
34
|
|
|
17
|
-
|
|
35
|
+
### Usage 1: Rails Controllers
|
|
36
|
+
|
|
37
|
+
_HasScope_ exposes the `has_scope` method automatically in all your controllers. This is used to declare the scopes a controller action can use to filter a resource:
|
|
18
38
|
|
|
19
39
|
```ruby
|
|
20
40
|
class GraduationsController < ApplicationController
|
|
21
41
|
has_scope :featured, type: :boolean
|
|
22
42
|
has_scope :by_degree
|
|
43
|
+
has_scope :by_period, using: %i[started_at ended_at], type: :hash
|
|
23
44
|
end
|
|
24
45
|
```
|
|
25
46
|
|
|
26
|
-
|
|
47
|
+
To apply the scopes to a specific resource, you just need to call `apply_scopes`:
|
|
27
48
|
|
|
28
49
|
```ruby
|
|
29
50
|
class GraduationsController < ApplicationController
|
|
@@ -37,36 +58,128 @@ class GraduationsController < ApplicationController
|
|
|
37
58
|
end
|
|
38
59
|
```
|
|
39
60
|
|
|
40
|
-
Then for each request:
|
|
61
|
+
Then for each request to the `index` action, _HasScope_ will automatically apply the scopes as follows:
|
|
41
62
|
|
|
42
|
-
```
|
|
43
|
-
/graduations
|
|
44
|
-
|
|
63
|
+
``` ruby
|
|
64
|
+
# GET /graduations
|
|
65
|
+
# No scopes applied
|
|
66
|
+
#=> brings all graduations
|
|
67
|
+
apply_scopes(Graduation).all == Graduation.all
|
|
45
68
|
|
|
46
|
-
/graduations?featured=true
|
|
47
|
-
|
|
69
|
+
# GET /graduations?featured=true
|
|
70
|
+
# The "featured' scope is applied
|
|
71
|
+
#=> brings featured graduations
|
|
72
|
+
apply_scopes(Graduation).all == Graduation.featured
|
|
48
73
|
|
|
49
|
-
/graduations?by_period[started_at]=20100701&by_period[ended_at]=20101013
|
|
74
|
+
# GET /graduations?by_period[started_at]=20100701&by_period[ended_at]=20101013
|
|
50
75
|
#=> brings graduations in the given period
|
|
76
|
+
apply_scopes(Graduation).all == Graduation.by_period('20100701', '20101013')
|
|
51
77
|
|
|
52
|
-
/graduations?featured=true&by_degree=phd
|
|
78
|
+
# GET /graduations?featured=true&by_degree=phd
|
|
53
79
|
#=> brings featured graduations with phd degree
|
|
80
|
+
apply_scopes(Graduation).all == Graduation.featured.by_degree('phd')
|
|
81
|
+
|
|
82
|
+
# GET /graduations?finished=true&by_degree=phd
|
|
83
|
+
#=> brings only graduations with phd degree because we didn't declare finished in our controller as a permitted scope
|
|
84
|
+
apply_scopes(Graduation).all == Graduation.by_degree('phd')
|
|
54
85
|
```
|
|
55
86
|
|
|
56
|
-
|
|
57
|
-
In the last case, it would return: `{ featured: true, by_degree: 'phd' }`.
|
|
87
|
+
#### Check for currently applied scopes
|
|
58
88
|
|
|
59
|
-
|
|
89
|
+
_HasScope_ creates a helper method called `current_scopes` to retrieve all the scopes applied. As it's a helper method, you'll be able to access it in the controller action or the view rendered in that action.
|
|
60
90
|
|
|
61
|
-
|
|
91
|
+
Coming back to one of the examples above:
|
|
62
92
|
|
|
63
93
|
```ruby
|
|
64
|
-
|
|
94
|
+
# GET /graduations?featured=true&by_degree=phd
|
|
95
|
+
#=> brings featured graduations with phd degree
|
|
96
|
+
apply_scopes(Graduation).all == Graduation.featured.by_degree('phd')
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Calling `current_scopes` after `apply_scopes` in the controller action or view would return the following:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
current_scopes
|
|
103
|
+
#=> { featured: true, by_degree: 'phd' }
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Usage 2: Standalone Mode
|
|
107
|
+
|
|
108
|
+
_HasScope_ can also be used in plain old Ruby objects (PORO). To implement the previous example using this approach, create a bare object and include `HasScope` to get access to its features:
|
|
109
|
+
|
|
110
|
+
> Note: We'll create a simple version of a query object for this example as this type of object can have multiple different implementations.
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
```ruby
|
|
114
|
+
class GraduationsSearchQuery
|
|
115
|
+
include HasScope
|
|
116
|
+
# ...
|
|
117
|
+
end
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Next, declare the scopes to be used the same way:
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
class GraduationsSearchQuery
|
|
124
|
+
include HasScope
|
|
125
|
+
|
|
126
|
+
has_scope :featured, type: :boolean
|
|
127
|
+
has_scope :by_degree
|
|
128
|
+
has_scope :by_period, using: %i[started_at ended_at], type: :hash
|
|
129
|
+
# ...
|
|
130
|
+
end
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Now, allow your object to perform the query by exposing a method that will use `apply_scopes`:
|
|
134
|
+
|
|
135
|
+
```ruby
|
|
136
|
+
class GraduationsSearchQuery
|
|
137
|
+
include HasScope
|
|
138
|
+
|
|
139
|
+
has_scope :featured, type: :boolean
|
|
140
|
+
has_scope :by_degree
|
|
141
|
+
has_scope :by_period, using: %i[started_at ended_at], type: :hash
|
|
142
|
+
|
|
143
|
+
def perform(collection: Graduation, params: {})
|
|
144
|
+
apply_scopes(collection, params)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Note that `apply_scopes` receives a `Hash` as a second argument, which represents the incoming params that determine which scopes should be applied to the model/collection. It defaults to `params` for compatibility with controllers, which is why it's not necessary to pass that second argument in the controller context.
|
|
150
|
+
|
|
151
|
+
Now in your controller you can call the `GraduationsSearchQuery` with the incoming parameters from the controller:
|
|
152
|
+
|
|
153
|
+
```ruby
|
|
154
|
+
class GraduationsController < ApplicationController
|
|
155
|
+
def index
|
|
156
|
+
graduations_query = GraduationsSearchQuery.new
|
|
157
|
+
@graduations = graduations_query.perform(collection: Graduation, params: params)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
#### Accessing `current_scopes`
|
|
163
|
+
|
|
164
|
+
In the controller context, `current_scopes` is made available as a helper method to the controller and view, but it's a `protected` method of _HasScope_'s implementation, to prevent it from becoming publicly accessible outside of _HasScope_ itself. This means that the object implementation showed above has access to `current_scopes` internally, but it's not exposed to other objects that interact with it.
|
|
165
|
+
|
|
166
|
+
If you need to access `current_scopes` elsewhere, you can change the method visibility like so:
|
|
167
|
+
|
|
168
|
+
```ruby
|
|
169
|
+
class GraduationsSearchQuery
|
|
170
|
+
include HasScope
|
|
171
|
+
|
|
172
|
+
# ...
|
|
173
|
+
|
|
174
|
+
public :current_scopes
|
|
175
|
+
|
|
176
|
+
# ...
|
|
177
|
+
end
|
|
65
178
|
```
|
|
66
179
|
|
|
67
180
|
## Options
|
|
68
181
|
|
|
69
|
-
|
|
182
|
+
`has_scope` supports several options:
|
|
70
183
|
|
|
71
184
|
* `:type` - Checks the type of the parameter sent.
|
|
72
185
|
By default, it does not allow hashes or arrays to be given,
|
|
@@ -116,9 +229,12 @@ the param value must be set to one of the "true" values above, e.g. `?active=tru
|
|
|
116
229
|
|
|
117
230
|
## Block usage
|
|
118
231
|
|
|
119
|
-
`has_scope` also accepts a block
|
|
120
|
-
|
|
121
|
-
|
|
232
|
+
`has_scope` also accepts a block in case we need to manipulate the given value and/or call the scope in some custom way. Usually three arguments are passed to the block:
|
|
233
|
+
- The instance of the controller or object where it's included
|
|
234
|
+
- The current scope chain
|
|
235
|
+
- The value of the scope to apply
|
|
236
|
+
|
|
237
|
+
> 💡 We suggest you name the first argument depending on how you're using _HasScope_. If it's the controller, use the word "controller". If it's a query object for example, use "query", or something meaningful for that context (or simply use "context"). In the following examples, we'll use controller for simplicity.
|
|
122
238
|
|
|
123
239
|
```ruby
|
|
124
240
|
has_scope :category do |controller, scope, value|
|
data/lib/has_scope/version.rb
CHANGED
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.8.
|
|
4
|
+
version: 0.8.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- José Valim
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2023-02-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: actionpack
|
|
@@ -77,12 +77,14 @@ files:
|
|
|
77
77
|
- README.md
|
|
78
78
|
- lib/has_scope.rb
|
|
79
79
|
- lib/has_scope/version.rb
|
|
80
|
-
- test/has_scope_test.rb
|
|
81
|
-
- test/test_helper.rb
|
|
82
80
|
homepage: http://github.com/plataformatec/has_scope
|
|
83
81
|
licenses:
|
|
84
82
|
- MIT
|
|
85
|
-
metadata:
|
|
83
|
+
metadata:
|
|
84
|
+
homepage_uri: https://github.com/heartcombo/has_scope
|
|
85
|
+
changelog_uri: https://github.com/heartcombo/has_scope/blob/main/CHANGELOG.md
|
|
86
|
+
source_code_uri: https://github.com/heartcombo/has_scope
|
|
87
|
+
bug_tracker_uri: https://github.com/heartcombo/has_scope/issues
|
|
86
88
|
post_install_message:
|
|
87
89
|
rdoc_options:
|
|
88
90
|
- "--charset=UTF-8"
|
|
@@ -99,10 +101,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
99
101
|
- !ruby/object:Gem::Version
|
|
100
102
|
version: '0'
|
|
101
103
|
requirements: []
|
|
102
|
-
rubygems_version: 3.
|
|
104
|
+
rubygems_version: 3.4.5
|
|
103
105
|
signing_key:
|
|
104
106
|
specification_version: 4
|
|
105
107
|
summary: Maps controller filters to your resource scopes.
|
|
106
|
-
test_files:
|
|
107
|
-
- test/test_helper.rb
|
|
108
|
-
- test/has_scope_test.rb
|
|
108
|
+
test_files: []
|
data/test/has_scope_test.rb
DELETED
|
@@ -1,507 +0,0 @@
|
|
|
1
|
-
require 'test_helper'
|
|
2
|
-
|
|
3
|
-
HasScope::ALLOWED_TYPES[:date] = [[String], -> v { Date.parse(v) rescue nil }]
|
|
4
|
-
|
|
5
|
-
class Tree; end
|
|
6
|
-
|
|
7
|
-
class TreesController < ApplicationController
|
|
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|
|
|
32
|
-
scope.only_really_short!(controller.object_id)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
has_scope :by_category do |controller, scope, value|
|
|
36
|
-
scope.by_given_category(controller.object_id, value + "_id")
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def index
|
|
40
|
-
@trees = apply_scopes(Tree).all
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def new
|
|
44
|
-
@tree = apply_scopes(Tree).new
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def show
|
|
48
|
-
@tree = apply_scopes(Tree).find(params[:id])
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
alias :edit :show
|
|
52
|
-
|
|
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
|
-
|
|
64
|
-
def restrict_to_only_tall_trees?
|
|
65
|
-
true
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def show_all_colors?
|
|
69
|
-
false
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def default_render
|
|
73
|
-
render body: action_name
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
class BonsaisController < TreesController
|
|
78
|
-
has_scope :categories, if: :categories?
|
|
79
|
-
|
|
80
|
-
protected
|
|
81
|
-
def categories?
|
|
82
|
-
false
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
class HasScopeTest < ActionController::TestCase
|
|
87
|
-
tests TreesController
|
|
88
|
-
|
|
89
|
-
def test_boolean_scope_is_called_when_boolean_param_is_true
|
|
90
|
-
Tree.expects(:only_tall).with().returns(Tree).in_sequence
|
|
91
|
-
Tree.expects(:all).returns([mock_tree]).in_sequence
|
|
92
|
-
|
|
93
|
-
get :index, params: { only_tall: 'true' }
|
|
94
|
-
|
|
95
|
-
assert_equal([mock_tree], assigns(:@trees))
|
|
96
|
-
assert_equal({ only_tall: true }, current_scopes)
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def test_boolean_scope_is_not_called_when_boolean_param_is_false
|
|
100
|
-
Tree.expects(:only_tall).never
|
|
101
|
-
Tree.expects(:all).returns([mock_tree])
|
|
102
|
-
|
|
103
|
-
get :index, params: { only_tall: 'false' }
|
|
104
|
-
|
|
105
|
-
assert_equal([mock_tree], assigns(:@trees))
|
|
106
|
-
assert_equal({ }, current_scopes)
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
def test_boolean_scope_with_allow_blank_is_called_when_boolean_param_is_true
|
|
110
|
-
Tree.expects(:conifer).with(true).returns(Tree).in_sequence
|
|
111
|
-
Tree.expects(:all).returns([mock_tree]).in_sequence
|
|
112
|
-
|
|
113
|
-
get :index, params: { conifer: 'true' }
|
|
114
|
-
|
|
115
|
-
assert_equal([mock_tree], assigns(:@trees))
|
|
116
|
-
assert_equal({ conifer: true }, current_scopes)
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def test_boolean_scope_with_allow_blank_is_called_when_boolean_param_is_false
|
|
120
|
-
Tree.expects(:conifer).with(false).returns(Tree).in_sequence
|
|
121
|
-
Tree.expects(:all).returns([mock_tree]).in_sequence
|
|
122
|
-
|
|
123
|
-
get :index, params: { conifer: 'not_true' }
|
|
124
|
-
|
|
125
|
-
assert_equal([mock_tree], assigns(:@trees))
|
|
126
|
-
assert_equal({ conifer: false }, current_scopes)
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
def test_boolean_scope_with_allow_blank_is_not_called_when_boolean_param_is_not_present
|
|
130
|
-
Tree.expects(:conifer).never
|
|
131
|
-
Tree.expects(:all).returns([mock_tree])
|
|
132
|
-
|
|
133
|
-
get :index
|
|
134
|
-
|
|
135
|
-
assert_equal([mock_tree], assigns(:@trees))
|
|
136
|
-
assert_equal({ }, current_scopes)
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
def test_scope_is_called_only_on_index
|
|
140
|
-
Tree.expects(:only_tall).never
|
|
141
|
-
Tree.expects(:find).with('42').returns(mock_tree)
|
|
142
|
-
|
|
143
|
-
get :show, params: { only_tall: 'true', id: '42' }
|
|
144
|
-
|
|
145
|
-
assert_equal(mock_tree, assigns(:@tree))
|
|
146
|
-
assert_equal({ }, current_scopes)
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
def test_scope_is_skipped_when_if_option_is_false
|
|
150
|
-
@controller.stubs(:restrict_to_only_tall_trees?).returns(false)
|
|
151
|
-
Tree.expects(:only_tall).never
|
|
152
|
-
Tree.expects(:all).returns([mock_tree])
|
|
153
|
-
|
|
154
|
-
get :index, params: { only_tall: 'true' }
|
|
155
|
-
|
|
156
|
-
assert_equal([mock_tree], assigns(:@trees))
|
|
157
|
-
assert_equal({ }, current_scopes)
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
def test_scope_is_skipped_when_unless_option_is_true
|
|
161
|
-
@controller.stubs(:show_all_colors?).returns(true)
|
|
162
|
-
Tree.expects(:color).never
|
|
163
|
-
Tree.expects(:all).returns([mock_tree])
|
|
164
|
-
|
|
165
|
-
get :index, params: { color: 'blue' }
|
|
166
|
-
|
|
167
|
-
assert_equal([mock_tree], assigns(:@trees))
|
|
168
|
-
assert_equal({ }, current_scopes)
|
|
169
|
-
end
|
|
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
|
-
|
|
193
|
-
def test_scope_is_called_except_on_index
|
|
194
|
-
Tree.expects(:shadown_range).never
|
|
195
|
-
Tree.expects(:all).returns([mock_tree])
|
|
196
|
-
|
|
197
|
-
get :index, params: { shadown_range: 20 }
|
|
198
|
-
|
|
199
|
-
assert_equal([mock_tree], assigns(:@trees))
|
|
200
|
-
assert_equal({ }, current_scopes)
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
def test_scope_is_called_with_arguments
|
|
204
|
-
Tree.expects(:color).with('blue').returns(Tree).in_sequence
|
|
205
|
-
Tree.expects(:all).returns([mock_tree]).in_sequence
|
|
206
|
-
|
|
207
|
-
get :index, params: { color: 'blue' }
|
|
208
|
-
|
|
209
|
-
assert_equal([mock_tree], assigns(:@trees))
|
|
210
|
-
assert_equal({ color: 'blue' }, current_scopes)
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
def test_scope_is_not_called_if_blank
|
|
214
|
-
Tree.expects(:color).never
|
|
215
|
-
Tree.expects(:all).returns([mock_tree]).in_sequence
|
|
216
|
-
|
|
217
|
-
get :index, params: { color: '' }
|
|
218
|
-
|
|
219
|
-
assert_equal([mock_tree], assigns(:@trees))
|
|
220
|
-
assert_equal({ }, current_scopes)
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
def test_scope_is_called_when_blank_if_allow_blank_is_given
|
|
224
|
-
Tree.expects(:root_type).with('').returns(Tree)
|
|
225
|
-
Tree.expects(:all).returns([mock_tree]).in_sequence
|
|
226
|
-
|
|
227
|
-
get :index, params: { root: '' }
|
|
228
|
-
|
|
229
|
-
assert_equal([mock_tree], assigns(:@trees))
|
|
230
|
-
assert_equal({ root: '' }, current_scopes)
|
|
231
|
-
end
|
|
232
|
-
|
|
233
|
-
def test_multiple_scopes_are_called
|
|
234
|
-
Tree.expects(:only_tall).with().returns(Tree)
|
|
235
|
-
Tree.expects(:color).with('blue').returns(Tree)
|
|
236
|
-
Tree.expects(:all).returns([mock_tree])
|
|
237
|
-
|
|
238
|
-
get :index, params: { color: 'blue', only_tall: 'true' }
|
|
239
|
-
|
|
240
|
-
assert_equal([mock_tree], assigns(:@trees))
|
|
241
|
-
assert_equal({ color: 'blue', only_tall: true }, current_scopes)
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
def test_scope_of_type_hash
|
|
245
|
-
hash = { "page" => "1", "per_page" => "10" }
|
|
246
|
-
Tree.expects(:paginate).with(hash).returns(Tree)
|
|
247
|
-
Tree.expects(:all).returns([mock_tree])
|
|
248
|
-
|
|
249
|
-
get :index, params: { paginate: hash }
|
|
250
|
-
|
|
251
|
-
assert_equal([mock_tree], assigns(:@trees))
|
|
252
|
-
assert_equal({ paginate: hash }, current_scopes)
|
|
253
|
-
end
|
|
254
|
-
|
|
255
|
-
def test_scope_of_type_hash_with_using
|
|
256
|
-
hash = { "page" => "1", "per_page" => "10" }
|
|
257
|
-
Tree.expects(:args_paginate).with("1", "10").returns(Tree)
|
|
258
|
-
Tree.expects(:all).returns([mock_tree])
|
|
259
|
-
|
|
260
|
-
get :index, params: { args_paginate: hash }
|
|
261
|
-
|
|
262
|
-
assert_equal([mock_tree], assigns(:@trees))
|
|
263
|
-
assert_equal({ args_paginate: hash }, current_scopes)
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
def test_hash_with_blank_values_is_ignored
|
|
267
|
-
hash = { "page" => "", "per_page" => "" }
|
|
268
|
-
Tree.expects(:paginate).never
|
|
269
|
-
Tree.expects(:all).returns([mock_tree])
|
|
270
|
-
|
|
271
|
-
get :index, params: { paginate: hash }
|
|
272
|
-
|
|
273
|
-
assert_equal([mock_tree], assigns(:@trees))
|
|
274
|
-
assert_equal({ }, current_scopes)
|
|
275
|
-
end
|
|
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
|
-
|
|
299
|
-
def test_nested_hash_with_blank_values_is_ignored
|
|
300
|
-
hash = { "parent" => { "children" => "" } }
|
|
301
|
-
Tree.expects(:paginate).never
|
|
302
|
-
Tree.expects(:all).returns([mock_tree])
|
|
303
|
-
|
|
304
|
-
get :index, params: { paginate: hash }
|
|
305
|
-
|
|
306
|
-
assert_equal([mock_tree], assigns(:@trees))
|
|
307
|
-
assert_equal({ }, current_scopes)
|
|
308
|
-
end
|
|
309
|
-
|
|
310
|
-
def test_nested_blank_array_param_is_ignored
|
|
311
|
-
hash = { "parent" => [""] }
|
|
312
|
-
Tree.expects(:paginate).never
|
|
313
|
-
Tree.expects(:all).returns([mock_tree])
|
|
314
|
-
|
|
315
|
-
get :index, params: { paginate: hash }
|
|
316
|
-
|
|
317
|
-
assert_equal([mock_tree], assigns(:@trees))
|
|
318
|
-
assert_equal({ }, current_scopes)
|
|
319
|
-
end
|
|
320
|
-
|
|
321
|
-
def test_scope_of_type_array
|
|
322
|
-
array = %w(book kitchen sport)
|
|
323
|
-
Tree.expects(:categories).with(array).returns(Tree)
|
|
324
|
-
Tree.expects(:all).returns([mock_tree])
|
|
325
|
-
|
|
326
|
-
get :index, params: { categories: array }
|
|
327
|
-
|
|
328
|
-
assert_equal([mock_tree], assigns(:@trees))
|
|
329
|
-
assert_equal({ categories: array }, current_scopes)
|
|
330
|
-
end
|
|
331
|
-
|
|
332
|
-
def test_array_of_blank_values_is_ignored
|
|
333
|
-
Tree.expects(:categories).never
|
|
334
|
-
Tree.expects(:all).returns([mock_tree])
|
|
335
|
-
|
|
336
|
-
get :index, params: { categories: [""] }
|
|
337
|
-
|
|
338
|
-
assert_equal([mock_tree], assigns(:@trees))
|
|
339
|
-
assert_equal({ }, current_scopes)
|
|
340
|
-
end
|
|
341
|
-
|
|
342
|
-
def test_scope_of_invalid_type_silently_fails
|
|
343
|
-
Tree.expects(:all).returns([mock_tree])
|
|
344
|
-
|
|
345
|
-
get :index, params: { paginate: "1" }
|
|
346
|
-
|
|
347
|
-
assert_equal([mock_tree], assigns(:@trees))
|
|
348
|
-
assert_equal({ }, current_scopes)
|
|
349
|
-
end
|
|
350
|
-
|
|
351
|
-
def test_scope_is_called_with_default_value
|
|
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
|
|
356
|
-
Tree.expects(:find).with('42').returns(mock_tree).in_sequence
|
|
357
|
-
|
|
358
|
-
get :edit, params: { id: '42' }
|
|
359
|
-
|
|
360
|
-
assert_equal(mock_tree, assigns(:@tree))
|
|
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)
|
|
367
|
-
end
|
|
368
|
-
|
|
369
|
-
def test_default_scope_value_can_be_overwritten
|
|
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
|
|
375
|
-
Tree.expects(:find).with('42').returns(mock_tree).in_sequence
|
|
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
|
-
|
|
385
|
-
assert_equal(mock_tree, assigns(:@tree))
|
|
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)
|
|
392
|
-
end
|
|
393
|
-
|
|
394
|
-
def test_scope_with_different_key
|
|
395
|
-
Tree.expects(:root_type).with('outside').returns(Tree).in_sequence
|
|
396
|
-
Tree.expects(:find).with('42').returns(mock_tree).in_sequence
|
|
397
|
-
|
|
398
|
-
get :show, params: { id: '42', root: 'outside' }
|
|
399
|
-
|
|
400
|
-
assert_equal(mock_tree, assigns(:@tree))
|
|
401
|
-
assert_equal({ root: 'outside' }, current_scopes)
|
|
402
|
-
end
|
|
403
|
-
|
|
404
|
-
def test_scope_with_default_value_as_a_proc_without_argument
|
|
405
|
-
Date.expects(:today).returns("today")
|
|
406
|
-
Tree.expects(:planted_before).with("today").returns(Tree)
|
|
407
|
-
Tree.expects(:all).returns([mock_tree])
|
|
408
|
-
|
|
409
|
-
get :index
|
|
410
|
-
|
|
411
|
-
assert_equal([mock_tree], assigns(:@trees))
|
|
412
|
-
assert_equal({ planted_before: "today" }, current_scopes)
|
|
413
|
-
end
|
|
414
|
-
|
|
415
|
-
def test_scope_with_default_value_as_proc_with_argument
|
|
416
|
-
session[:height] = 100
|
|
417
|
-
Tree.expects(:calculate_height).with(100).returns(Tree).in_sequence
|
|
418
|
-
Tree.expects(:new).returns(mock_tree).in_sequence
|
|
419
|
-
|
|
420
|
-
get :new
|
|
421
|
-
|
|
422
|
-
assert_equal(mock_tree, assigns(:@tree))
|
|
423
|
-
assert_equal({ calculate_height: 100 }, current_scopes)
|
|
424
|
-
end
|
|
425
|
-
|
|
426
|
-
def test_scope_with_custom_type
|
|
427
|
-
parsed = Date.civil(2014,11,11)
|
|
428
|
-
Tree.expects(:planted_after).with(parsed).returns(Tree)
|
|
429
|
-
Tree.expects(:all).returns([mock_tree])
|
|
430
|
-
|
|
431
|
-
get :index, params: { planted_after: "2014-11-11" }
|
|
432
|
-
|
|
433
|
-
assert_equal([mock_tree], assigns(:@trees))
|
|
434
|
-
assert_equal({ planted_after: parsed }, current_scopes)
|
|
435
|
-
end
|
|
436
|
-
|
|
437
|
-
def test_scope_with_boolean_block
|
|
438
|
-
Tree.expects(:only_really_short!).with(@controller.object_id).returns(Tree)
|
|
439
|
-
Tree.expects(:all).returns([mock_tree])
|
|
440
|
-
|
|
441
|
-
get :index, params: { only_short: 'true' }
|
|
442
|
-
|
|
443
|
-
assert_equal([mock_tree], assigns(:@trees))
|
|
444
|
-
assert_equal({ only_short: true }, current_scopes)
|
|
445
|
-
end
|
|
446
|
-
|
|
447
|
-
def test_scope_with_other_block_types
|
|
448
|
-
Tree.expects(:by_given_category).with(@controller.object_id, 'for_id').returns(Tree)
|
|
449
|
-
Tree.expects(:all).returns([mock_tree])
|
|
450
|
-
|
|
451
|
-
get :index, params: { by_category: 'for' }
|
|
452
|
-
|
|
453
|
-
assert_equal([mock_tree], assigns(:@trees))
|
|
454
|
-
assert_equal({ by_category: 'for' }, current_scopes)
|
|
455
|
-
end
|
|
456
|
-
|
|
457
|
-
def test_scope_with_nested_hash_and_in_option
|
|
458
|
-
hash = { 'title' => 'the-title', 'content' => 'the-content' }
|
|
459
|
-
Tree.expects(:title).with('the-title').returns(Tree)
|
|
460
|
-
Tree.expects(:content).with('the-content').returns(Tree)
|
|
461
|
-
Tree.expects(:metadata).never
|
|
462
|
-
Tree.expects(:metadata_blank).with(nil).returns(Tree)
|
|
463
|
-
Tree.expects(:all).returns([mock_tree])
|
|
464
|
-
|
|
465
|
-
get :index, params: { q: hash }
|
|
466
|
-
|
|
467
|
-
assert_equal([mock_tree], assigns(:@trees))
|
|
468
|
-
assert_equal({ q: hash }, current_scopes)
|
|
469
|
-
end
|
|
470
|
-
|
|
471
|
-
def test_overwritten_scope
|
|
472
|
-
assert_nil(TreesController.scopes_configuration[:categories][:if])
|
|
473
|
-
assert_equal(:categories?, BonsaisController.scopes_configuration[:categories][:if])
|
|
474
|
-
end
|
|
475
|
-
|
|
476
|
-
protected
|
|
477
|
-
|
|
478
|
-
def mock_tree(stubs = {})
|
|
479
|
-
@mock_tree ||= mock(stubs)
|
|
480
|
-
end
|
|
481
|
-
|
|
482
|
-
def current_scopes
|
|
483
|
-
@controller.send :current_scopes
|
|
484
|
-
end
|
|
485
|
-
|
|
486
|
-
def assigns(ivar)
|
|
487
|
-
@controller.instance_variable_get(ivar)
|
|
488
|
-
end
|
|
489
|
-
end
|
|
490
|
-
|
|
491
|
-
class TreeHugger
|
|
492
|
-
include HasScope
|
|
493
|
-
|
|
494
|
-
has_scope :color
|
|
495
|
-
|
|
496
|
-
def by_color
|
|
497
|
-
apply_scopes(Tree, color: 'blue')
|
|
498
|
-
end
|
|
499
|
-
end
|
|
500
|
-
|
|
501
|
-
class HasScopeOutsideControllerTest < ActiveSupport::TestCase
|
|
502
|
-
def test_has_scope_usable_outside_controller
|
|
503
|
-
Tree.expects(:color).with('blue')
|
|
504
|
-
|
|
505
|
-
TreeHugger.new.by_color
|
|
506
|
-
end
|
|
507
|
-
end
|
data/test/test_helper.rb
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
require 'bundler/setup'
|
|
2
|
-
|
|
3
|
-
require 'minitest/autorun'
|
|
4
|
-
require 'mocha'
|
|
5
|
-
require 'mocha/mini_test'
|
|
6
|
-
|
|
7
|
-
# Configure Rails
|
|
8
|
-
ENV['RAILS_ENV'] = 'test'
|
|
9
|
-
|
|
10
|
-
$:.unshift File.expand_path('../../lib', __FILE__)
|
|
11
|
-
require 'has_scope'
|
|
12
|
-
|
|
13
|
-
HasScope::Routes = ActionDispatch::Routing::RouteSet.new
|
|
14
|
-
HasScope::Routes.draw do
|
|
15
|
-
resources :trees, only: %i[index new edit show]
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
class ApplicationController < ActionController::Base
|
|
19
|
-
include HasScope::Routes.url_helpers
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
class ActiveSupport::TestCase
|
|
23
|
-
self.test_order = :random if respond_to?(:test_order=)
|
|
24
|
-
|
|
25
|
-
setup do
|
|
26
|
-
@routes = HasScope::Routes
|
|
27
|
-
end
|
|
28
|
-
end
|