has_scope 0.8.0 → 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem Version](https://fury-badge.herokuapp.com/rb/has_scope.svg)](http://badge.fury.io/rb/has_scope)
|
4
|
-
[![Code Climate](https://codeclimate.com/github/heartcombo/has_scope.svg)](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
|