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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c24a5ed875375fb5da93be3f90ac863b0c72d676ecc1fe0959933924452b043d
4
- data.tar.gz: 2f8aacdef384ce75c7fc2e4b4b697ba62e68293deeac5754597d10dffc044052
3
+ metadata.gz: abf0ad461389302c6eef573bc313b2f0753a7dfaa02c23fdf685acce47be111c
4
+ data.tar.gz: 79209897cf738ba373be55f2b9fe4543d15880ae466c9a577e23cc1abb06ee3d
5
5
  SHA512:
6
- metadata.gz: 9541480b36218fb3c1b9b7c48a3297411c8875ec47bb1735b6b394127c02ad90010d79787931c0d0154d662b40f4c236627710ca7b2fa01edb7513f27e043fb9
7
- data.tar.gz: 5927fbd54e4a019b2d9f9fd7f7a60cf929fd46eac8f15c4c0e8cdb0cab2cac80a2b432e4398a70013d6bf88a519df60d2b6f571fb4f7f5db2d141292762b9eba
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
- Has scope allows you to map incoming controller parameters to named scopes in your resources.
7
- Imagine the following model called graduations:
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
- You can use those named scopes as filters by declaring them on your controller:
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
- Now, if you want to apply them to an specific resource, you just need to call `apply_scopes`:
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
- #=> acts like a normal request
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
- #=> calls the named scope and bring featured graduations
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
- You can retrieve all the scopes applied in one action with `current_scopes` method.
57
- In the last case, it would return: `{ featured: true, by_degree: 'phd' }`.
87
+ #### Check for currently applied scopes
58
88
 
59
- ## Installation
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
- Add `has_scope` to your Gemfile or install it from Rubygems.
91
+ Coming back to one of the examples above:
62
92
 
63
93
  ```ruby
64
- gem 'has_scope'
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
- HasScope supports several options:
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. The controller, current scope and value are yielded
120
- to the block so the user can apply the scope on its own. This is useful in case we
121
- need to manipulate the given value:
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|
@@ -1,3 +1,3 @@
1
1
  module HasScope
2
- VERSION = "0.8.0"
2
+ VERSION = "0.8.1"
3
3
  end
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.0
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: 2021-02-15 00:00:00.000000000 Z
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.2.6
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: []
@@ -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