has_scope 0.7.2 → 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: b023c16c60cb24ca941d55d69be48713962b661a68b92f32a0af40bf952686ea
4
- data.tar.gz: 45b61de129e41764aa08bbcbd17c1287a83542d2171cc69ad8452a6da4f22085
3
+ metadata.gz: abf0ad461389302c6eef573bc313b2f0753a7dfaa02c23fdf685acce47be111c
4
+ data.tar.gz: 79209897cf738ba373be55f2b9fe4543d15880ae466c9a577e23cc1abb06ee3d
5
5
  SHA512:
6
- metadata.gz: 35392817be681d709e897972514b215be8860f2b00216745cba37931c77aa27dbf1fc13f56b1cbc775210c266de902b4a79023a8637b8736089669543ee73b9e
7
- data.tar.gz: 8ce1d74c2cfb9a76d4c7a60752aee0d86ede19ba778f7e28983eb77b446162d60091ee7ff3437025b0c841bb75e7f34de8dc4fddfdbd99331dd66707d28b7af5
6
+ metadata.gz: 04a17d0ea9ee72e18ad98fa79c9d4e3a856219673fe066d1326df8b12af29a22f80282f0ec4cb8de9e25653d5f1179137fa4a8475d5fd8bf247ff12f3ab8c20e
7
+ data.tar.gz: 87978f08fed8aec6fe006b0d9faee4704b831a11b8b61b1c1211537965d4cd59a85784229d0e3260c49bd7f1313591ba48ac2feb0580417cb69ca1c60d435ad4
data/README.md CHANGED
@@ -1,36 +1,56 @@
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
- [![Build Status](https://api.travis-ci.org/plataformatec/has_scope.svg?branch=master)](http://travis-ci.org/plataformatec/has_scope)
5
- [![Code Climate](https://codeclimate.com/github/plataformatec/has_scope.svg)](https://codeclimate.com/github/plataformatec/has_scope)
6
4
 
7
- Has scope allows you to map incoming controller parameters to named scopes in your resources.
8
- 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:
9
26
 
10
27
  ```ruby
11
28
  class Graduation < ActiveRecord::Base
12
- scope :featured, -> { where(:featured => true) }
13
- scope :by_degree, -> degree { where(:degree => degree) }
29
+ scope :featured, -> { where(featured: true) }
30
+ scope :by_degree, -> degree { where(degree: degree) }
14
31
  scope :by_period, -> started_at, ended_at { where("started_at = ? AND ended_at = ?", started_at, ended_at) }
15
32
  end
16
33
  ```
17
34
 
18
- 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:
19
38
 
20
39
  ```ruby
21
40
  class GraduationsController < ApplicationController
22
- has_scope :featured, :type => :boolean
41
+ has_scope :featured, type: :boolean
23
42
  has_scope :by_degree
43
+ has_scope :by_period, using: %i[started_at ended_at], type: :hash
24
44
  end
25
45
  ```
26
46
 
27
- 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`:
28
48
 
29
49
  ```ruby
30
50
  class GraduationsController < ApplicationController
31
- has_scope :featured, :type => :boolean
51
+ has_scope :featured, type: :boolean
32
52
  has_scope :by_degree
33
- has_scope :by_period, :using => [:started_at, :ended_at], :type => :hash
53
+ has_scope :by_period, using: %i[started_at ended_at], type: :hash
34
54
 
35
55
  def index
36
56
  @graduations = apply_scopes(Graduation).all
@@ -38,36 +58,128 @@ class GraduationsController < ApplicationController
38
58
  end
39
59
  ```
40
60
 
41
- Then for each request:
61
+ Then for each request to the `index` action, _HasScope_ will automatically apply the scopes as follows:
42
62
 
43
- ```
44
- /graduations
45
- #=> 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
46
68
 
47
- /graduations?featured=true
48
- #=> 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
49
73
 
50
- /graduations?by_period[started_at]=20100701&by_period[ended_at]=20101013
74
+ # GET /graduations?by_period[started_at]=20100701&by_period[ended_at]=20101013
51
75
  #=> brings graduations in the given period
76
+ apply_scopes(Graduation).all == Graduation.by_period('20100701', '20101013')
52
77
 
53
- /graduations?featured=true&by_degree=phd
78
+ # GET /graduations?featured=true&by_degree=phd
54
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')
55
85
  ```
56
86
 
57
- You can retrieve all the scopes applied in one action with `current_scopes` method.
58
- In the last case, it would return: { :featured => true, :by_degree => "phd" }.
87
+ #### Check for currently applied scopes
59
88
 
60
- ## 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.
61
90
 
62
- Add `has_scope` to your Gemfile or install it from Rubygems.
91
+ Coming back to one of the examples above:
63
92
 
64
93
  ```ruby
65
- 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
66
178
  ```
67
179
 
68
180
  ## Options
69
181
 
70
- HasScope supports several options:
182
+ `has_scope` supports several options:
71
183
 
72
184
  * `:type` - Checks the type of the parameter sent.
73
185
  By default, it does not allow hashes or arrays to be given,
@@ -83,23 +195,23 @@ HasScope supports several options:
83
195
 
84
196
  * `:using` - The subkeys to be used as args when type is a hash.
85
197
 
86
- * `:if` - Specifies a method, proc or string to call to determine if the scope should apply.
198
+ * `:in` - A shortcut for combining the `:using` option with nested hashes.
87
199
 
88
- * `:unless` - Specifies a method, proc or string to call to determine if the scope should NOT apply.
200
+ * `:if` - Specifies a method or proc to call to determine if the scope should apply. Passing a string is deprecated and it will be removed in a future version.
201
+
202
+ * `:unless` - Specifies a method or proc to call to determine if the scope should NOT apply. Passing a string is deprecated and it will be removed in a future version.
89
203
 
90
204
  * `:default` - Default value for the scope. Whenever supplied the scope is always called.
91
205
 
92
206
  * `:allow_blank` - Blank values are not sent to scopes by default. Set to true to overwrite.
93
207
 
94
- * `:in` - A shortcut for combining the `:using` option with nested hashes.
95
-
96
208
  ## Boolean usage
97
209
 
98
210
  If `type: :boolean` is set it just calls the named scope, without any arguments, when parameter
99
211
  is set to a "true" value. `'true'` and `'1'` are parsed as `true`, everything else as `false`.
100
212
 
101
- When boolean scope is set up with `allow_blank: true`, it will call the scope
102
- with the value as usual scope.
213
+ When boolean scope is set up with `allow_blank: true`, it will call the scope with the value as
214
+ any usual scope.
103
215
 
104
216
  ```ruby
105
217
  has_scope :visible, type: :boolean
@@ -110,15 +222,23 @@ scope :visible, -> { where(visible: true) }
110
222
  scope :active, ->(value = true) { where(active: value) }
111
223
  ```
112
224
 
225
+ _Note_: it is not possible to apply a boolean scope with just the query param being present, e.g.
226
+ `?active`, that's not considered a "true" value (the param value will be `nil`), and thus the
227
+ scope will be called with `false` as argument. In order for the scope to receive a `true` argument
228
+ the param value must be set to one of the "true" values above, e.g. `?active=true` or `?active=1`.
229
+
113
230
  ## Block usage
114
231
 
115
- `has_scope` also accepts a block. The controller, current scope and value are yielded
116
- to the block so the user can apply the scope on its own. This is useful in case we
117
- 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.
118
238
 
119
239
  ```ruby
120
240
  has_scope :category do |controller, scope, value|
121
- value != "all" ? scope.by_category(value) : scope
241
+  value != 'all' ? scope.by_category(value) : scope
122
242
  end
123
243
  ```
124
244
 
@@ -126,7 +246,7 @@ When used with booleans without `:allow_blank`, it just receives two arguments
126
246
  and is just invoked if true is given:
127
247
 
128
248
  ```ruby
129
- has_scope :not_voted_by_me, :type => :boolean do |controller, scope|
249
+ has_scope :not_voted_by_me, type: :boolean do |controller, scope|
130
250
  scope.not_voted_by(controller.current_user.id)
131
251
  end
132
252
  ```
@@ -159,10 +279,22 @@ scope :available, ->(*) { where(blocked: false) }
159
279
  This will allow usual users to get only available items, but admins will
160
280
  be able to access blocked items too.
161
281
 
282
+ ## Check which scopes have been applied
283
+
284
+ To check which scopes have been applied, you can call `current_scopes` from the controller or view.
285
+ This returns a hash with the scope name as the key and the scope value as the value.
286
+
287
+ For example, if a boolean `:active` scope has been applied, `current_scopes` will return `{ active: true }`.
288
+
289
+ ## Supported Ruby / Rails versions
290
+
291
+ We intend to maintain support for all Ruby / Rails versions that haven't reached end-of-life.
292
+
293
+ For more information about specific versions please check [Ruby](https://www.ruby-lang.org/en/downloads/branches/)
294
+ and [Rails](https://guides.rubyonrails.org/maintenance_policy.html) maintenance policies, and our test matrix.
295
+
162
296
  ## Bugs and Feedback
163
297
 
164
298
  If you discover any bugs or want to drop a line, feel free to create an issue on GitHub.
165
299
 
166
- http://github.com/plataformatec/has_scope/issues
167
-
168
- MIT License. Copyright 2009-2016 Plataformatec. http://blog.plataformatec.com.br
300
+ MIT License. Copyright 2009-2019 Plataformatec. http://blog.plataformatec.com.br
@@ -1,3 +1,3 @@
1
1
  module HasScope
2
- VERSION = "0.7.2"
2
+ VERSION = "0.8.1"
3
3
  end
data/lib/has_scope.rb CHANGED
@@ -1,17 +1,20 @@
1
+ require 'active_support'
2
+ require 'action_controller'
3
+
1
4
  module HasScope
2
5
  TRUE_VALUES = ["true", true, "1", 1]
3
6
 
4
7
  ALLOWED_TYPES = {
5
- :array => [[ Array ]],
6
- :hash => [[Hash, ActionController::Parameters]],
7
- :boolean => [[ Object ], -> v { TRUE_VALUES.include?(v) }],
8
- :default => [[ String, Numeric ]],
8
+ array: [[ Array ]],
9
+ hash: [[ Hash, ActionController::Parameters ]],
10
+ boolean: [[ Object ], -> v { TRUE_VALUES.include?(v) }],
11
+ default: [[ String, Numeric ]],
9
12
  }
10
13
 
11
14
  def self.included(base)
12
15
  base.class_eval do
13
16
  extend ClassMethods
14
- class_attribute :scopes_configuration, :instance_writer => false
17
+ class_attribute :scopes_configuration, instance_writer: false
15
18
  self.scopes_configuration = {}
16
19
  end
17
20
  end
@@ -36,6 +39,8 @@ module HasScope
36
39
  # * <tt>:using</tt> - If type is a hash, you can provide :using to convert the hash to
37
40
  # a named scope call with several arguments.
38
41
  #
42
+ # * <tt>:in</tt> - A shortcut for combining the `:using` option with nested hashes.
43
+ #
39
44
  # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
40
45
  # if the scope should apply
41
46
  #
@@ -57,7 +62,7 @@ module HasScope
57
62
  # value != "all" ? scope.by_category(value) : scope
58
63
  # end
59
64
  #
60
- # has_scope :not_voted_by_me, :type => :boolean do |controller, scope|
65
+ # has_scope :not_voted_by_me, type: :boolean do |controller, scope|
61
66
  # scope.not_voted_by(controller.current_user.id)
62
67
  # end
63
68
  #
@@ -69,6 +74,10 @@ module HasScope
69
74
  if options.key?(:in)
70
75
  options[:as] = options[:in]
71
76
  options[:using] = scopes
77
+
78
+ if options.key?(:default) && !options[:default].is_a?(Hash)
79
+ options[:default] = scopes.each_with_object({}) { |scope, hash| hash[scope] = options[:default] }
80
+ end
72
81
  end
73
82
 
74
83
  if options.key?(:using)
@@ -87,7 +96,7 @@ module HasScope
87
96
  self.scopes_configuration = scopes_configuration.dup
88
97
 
89
98
  scopes.each do |scope|
90
- scopes_configuration[scope] ||= { :as => scope, :type => :default, :block => block }
99
+ scopes_configuration[scope] ||= { as: scope, type: :default, block: block }
91
100
  scopes_configuration[scope] = self.scopes_configuration[scope].merge(options)
92
101
  end
93
102
  end
@@ -97,16 +106,16 @@ module HasScope
97
106
 
98
107
  # Receives an object where scopes will be applied to.
99
108
  #
100
- # class GraduationsController < InheritedResources::Base
101
- # has_scope :featured, :type => true, :only => :index
102
- # has_scope :by_degree, :only => :index
109
+ # class GraduationsController < ApplicationController
110
+ # has_scope :featured, type: true, only: :index
111
+ # has_scope :by_degree, only: :index
103
112
  #
104
113
  # def index
105
114
  # @graduations = apply_scopes(Graduation).all
106
115
  # end
107
116
  # end
108
117
  #
109
- def apply_scopes(target, hash=params)
118
+ def apply_scopes(target, hash = params)
110
119
  scopes_configuration.each do |scope, options|
111
120
  next unless apply_scope_to_action?(options)
112
121
  key = options[:as]
@@ -120,12 +129,20 @@ module HasScope
120
129
  end
121
130
  end
122
131
 
123
- value = parse_value(options[:type], key, value)
132
+ value = parse_value(options[:type], value)
124
133
  value = normalize_blanks(value)
125
134
 
126
- if call_scope && (value.present? || options[:allow_blank])
135
+ if value && options.key?(:using)
136
+ scope_value = value.values_at(*options[:using])
137
+ call_scope &&= scope_value.all?(&:present?) || options[:allow_blank]
138
+ else
139
+ scope_value = value
140
+ call_scope &&= value.present? || options[:allow_blank]
141
+ end
142
+
143
+ if call_scope
127
144
  current_scopes[key] = value
128
- target = call_scope_by_type(options[:type], scope, target, value, options)
145
+ target = call_scope_by_type(options[:type], scope, target, scope_value, options)
129
146
  end
130
147
  end
131
148
 
@@ -133,7 +150,7 @@ module HasScope
133
150
  end
134
151
 
135
152
  # Set the real value for the current scope if type check.
136
- def parse_value(type, key, value) #:nodoc:
153
+ def parse_value(type, value) #:nodoc:
137
154
  klasses, parser = ALLOWED_TYPES[type]
138
155
  if klasses.any? { |klass| value.is_a?(klass) }
139
156
  parser ? parser.call(value) : value
@@ -160,8 +177,7 @@ module HasScope
160
177
 
161
178
  if type == :boolean && !options[:allow_blank]
162
179
  block ? block.call(self, target) : target.send(scope)
163
- elsif value && options.key?(:using)
164
- value = value.values_at(*options[:using])
180
+ elsif options.key?(:using)
165
181
  block ? block.call(self, target, value) : target.send(scope, *value)
166
182
  else
167
183
  block ? block.call(self, target, value) : target.send(scope, value)
@@ -185,6 +201,11 @@ module HasScope
185
201
  def applicable?(string_proc_or_symbol, expected) #:nodoc:
186
202
  case string_proc_or_symbol
187
203
  when String
204
+ ActiveSupport::Deprecation.warn <<-DEPRECATION.squish
205
+ [HasScope] Passing a string to determine if the scope should be applied
206
+ is deprecated and it will be removed in a future version of HasScope.
207
+ DEPRECATION
208
+
188
209
  eval(string_proc_or_symbol) == expected
189
210
  when Proc
190
211
  string_proc_or_symbol.call(self) == expected
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.7.2
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - José Valim
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-10 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
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '4.1'
19
+ version: '5.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '4.1'
26
+ version: '5.2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activesupport
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '4.1'
33
+ version: '5.2'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '4.1'
40
+ version: '5.2'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -77,13 +77,15 @@ 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: {}
86
- post_install_message:
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
88
+ post_install_message:
87
89
  rdoc_options:
88
90
  - "--charset=UTF-8"
89
91
  require_paths:
@@ -92,18 +94,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
92
94
  requirements:
93
95
  - - ">="
94
96
  - !ruby/object:Gem::Version
95
- version: 2.1.7
97
+ version: 2.5.0
96
98
  required_rubygems_version: !ruby/object:Gem::Requirement
97
99
  requirements:
98
100
  - - ">="
99
101
  - !ruby/object:Gem::Version
100
102
  version: '0'
101
103
  requirements: []
102
- rubyforge_project:
103
- rubygems_version: 2.7.6
104
- signing_key:
104
+ rubygems_version: 3.4.5
105
+ signing_key:
105
106
  specification_version: 4
106
107
  summary: Maps controller filters to your resource scopes.
107
- test_files:
108
- - test/test_helper.rb
109
- - test/has_scope_test.rb
108
+ test_files: []
@@ -1,375 +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 :args_paginate, :type => :hash, :using => [:page, :per_page]
17
- has_scope :categories, :type => :array
18
- has_scope :title, :in => :q
19
- has_scope :content, :in => :q
20
- has_scope :conifer, type: :boolean, :allow_blank => true
21
-
22
- has_scope :only_short, :type => :boolean do |controller, scope|
23
- scope.only_really_short!(controller.object_id)
24
- end
25
-
26
- has_scope :by_category do |controller, scope, value|
27
- scope.by_given_category(controller.object_id, value + "_id")
28
- end
29
-
30
- def index
31
- @trees = apply_scopes(Tree).all
32
- end
33
-
34
- def new
35
- @tree = apply_scopes(Tree).new
36
- end
37
-
38
- def show
39
- @tree = apply_scopes(Tree).find(params[:id])
40
- end
41
-
42
- alias :edit :show
43
-
44
- protected
45
- def restrict_to_only_tall_trees?
46
- true
47
- end
48
-
49
- def show_all_colors?
50
- false
51
- end
52
-
53
- if ActionPack::VERSION::MAJOR == 5
54
- def default_render
55
- render body: action_name
56
- end
57
- else
58
- # TODO: Remove this when we only support Rails 5.
59
- def default_render
60
- render text: action_name
61
- end
62
- end
63
- end
64
-
65
- class BonsaisController < TreesController
66
- has_scope :categories, :if => :categories?
67
-
68
- protected
69
- def categories?
70
- false
71
- end
72
- end
73
-
74
- class HasScopeTest < ActionController::TestCase
75
- tests TreesController
76
-
77
- def test_boolean_scope_is_called_when_boolean_param_is_true
78
- Tree.expects(:only_tall).with().returns(Tree).in_sequence
79
- Tree.expects(:all).returns([mock_tree]).in_sequence
80
- get :index, :only_tall => 'true'
81
- assert_equal([mock_tree], assigns(:@trees))
82
- assert_equal({ :only_tall => true }, current_scopes)
83
- end
84
-
85
- def test_boolean_scope_is_not_called_when_boolean_param_is_false
86
- Tree.expects(:only_tall).never
87
- Tree.expects(:all).returns([mock_tree])
88
- get :index, :only_tall => 'false'
89
- assert_equal([mock_tree], assigns(:@trees))
90
- assert_equal({ }, current_scopes)
91
- end
92
-
93
- def test_boolean_scope_with_allow_blank_is_called_when_boolean_param_is_true
94
- Tree.expects(:conifer).with(true).returns(Tree).in_sequence
95
- Tree.expects(:all).returns([mock_tree]).in_sequence
96
- get :index, :conifer => 'true'
97
- assert_equal([mock_tree], assigns(:@trees))
98
- assert_equal({ :conifer => true }, current_scopes)
99
- end
100
-
101
- def test_boolean_scope_with_allow_blank_is_called_when_boolean_param_is_false
102
- Tree.expects(:conifer).with(false).returns(Tree).in_sequence
103
- Tree.expects(:all).returns([mock_tree]).in_sequence
104
- get :index, :conifer => 'not_true'
105
- assert_equal([mock_tree], assigns(:@trees))
106
- assert_equal({ :conifer => false }, current_scopes)
107
- end
108
-
109
- def test_boolean_scope_with_allow_blank_is_not_called_when_boolean_param_is_not_present
110
- Tree.expects(:conifer).never
111
- Tree.expects(:all).returns([mock_tree])
112
- get :index
113
- assert_equal([mock_tree], assigns(:@trees))
114
- assert_equal({ }, current_scopes)
115
- end
116
-
117
- def test_scope_is_called_only_on_index
118
- Tree.expects(:only_tall).never
119
- Tree.expects(:find).with('42').returns(mock_tree)
120
- get :show, :only_tall => 'true', :id => '42'
121
- assert_equal(mock_tree, assigns(:@tree))
122
- assert_equal({ }, current_scopes)
123
- end
124
-
125
- def test_scope_is_skipped_when_if_option_is_false
126
- @controller.stubs(:restrict_to_only_tall_trees?).returns(false)
127
- Tree.expects(:only_tall).never
128
- Tree.expects(:all).returns([mock_tree])
129
- get :index, :only_tall => 'true'
130
- assert_equal([mock_tree], assigns(:@trees))
131
- assert_equal({ }, current_scopes)
132
- end
133
-
134
- def test_scope_is_skipped_when_unless_option_is_true
135
- @controller.stubs(:show_all_colors?).returns(true)
136
- Tree.expects(:color).never
137
- Tree.expects(:all).returns([mock_tree])
138
- get :index, :color => 'blue'
139
- assert_equal([mock_tree], assigns(:@trees))
140
- assert_equal({ }, current_scopes)
141
- end
142
-
143
- def test_scope_is_called_except_on_index
144
- Tree.expects(:shadown_range).never
145
- Tree.expects(:all).returns([mock_tree])
146
- get :index, :shadown_range => 20
147
- assert_equal([mock_tree], assigns(:@trees))
148
- assert_equal({ }, current_scopes)
149
- end
150
-
151
- def test_scope_is_called_with_arguments
152
- Tree.expects(:color).with('blue').returns(Tree).in_sequence
153
- Tree.expects(:all).returns([mock_tree]).in_sequence
154
- get :index, :color => 'blue'
155
- assert_equal([mock_tree], assigns(:@trees))
156
- assert_equal({ :color => 'blue' }, current_scopes)
157
- end
158
-
159
- def test_scope_is_not_called_if_blank
160
- Tree.expects(:color).never
161
- Tree.expects(:all).returns([mock_tree]).in_sequence
162
- get :index, :color => ''
163
- assert_equal([mock_tree], assigns(:@trees))
164
- assert_equal({ }, current_scopes)
165
- end
166
-
167
- def test_scope_is_called_when_blank_if_allow_blank_is_given
168
- Tree.expects(:root_type).with('').returns(Tree)
169
- Tree.expects(:all).returns([mock_tree]).in_sequence
170
- get :index, :root => ''
171
- assert_equal([mock_tree], assigns(:@trees))
172
- assert_equal({ :root => '' }, current_scopes)
173
- end
174
-
175
- def test_multiple_scopes_are_called
176
- Tree.expects(:only_tall).with().returns(Tree)
177
- Tree.expects(:color).with('blue').returns(Tree)
178
- Tree.expects(:all).returns([mock_tree])
179
- get :index, :color => 'blue', :only_tall => 'true'
180
- assert_equal([mock_tree], assigns(:@trees))
181
- assert_equal({ :color => 'blue', :only_tall => true }, current_scopes)
182
- end
183
-
184
- def test_scope_of_type_hash
185
- hash = { "page" => "1", "per_page" => "10" }
186
- Tree.expects(:paginate).with(hash).returns(Tree)
187
- Tree.expects(:all).returns([mock_tree])
188
- get :index, :paginate => hash
189
- assert_equal([mock_tree], assigns(:@trees))
190
- assert_equal({ paginate: hash }, current_scopes)
191
- end
192
-
193
- def test_scope_of_type_hash_with_using
194
- hash = { "page" => "1", "per_page" => "10" }
195
- Tree.expects(:args_paginate).with("1", "10").returns(Tree)
196
- Tree.expects(:all).returns([mock_tree])
197
- get :index, :args_paginate => hash
198
- assert_equal([mock_tree], assigns(:@trees))
199
- assert_equal({ :args_paginate => hash }, current_scopes)
200
- end
201
-
202
- def test_hash_with_blank_values_is_ignored
203
- hash = { "page" => "", "per_page" => "" }
204
- Tree.expects(:paginate).never
205
- Tree.expects(:all).returns([mock_tree])
206
- get :index, :paginate => hash
207
- assert_equal([mock_tree], assigns(:@trees))
208
- assert_equal({ }, current_scopes)
209
- end
210
-
211
- def test_nested_hash_with_blank_values_is_ignored
212
- hash = { "parent" => {"children" => ""} }
213
- Tree.expects(:paginate).never
214
- Tree.expects(:all).returns([mock_tree])
215
- get :index, :paginate => hash
216
- assert_equal([mock_tree], assigns(:@trees))
217
- assert_equal({ }, current_scopes)
218
- end
219
-
220
- def test_nested_blank_array_param_is_ignored
221
- hash = { "parent" => [""] }
222
- Tree.expects(:paginate).never
223
- Tree.expects(:all).returns([mock_tree])
224
- get :index, :paginate => hash
225
- assert_equal([mock_tree], assigns(:@trees))
226
- assert_equal({ }, current_scopes)
227
- end
228
-
229
- def test_scope_of_type_array
230
- array = %w(book kitchen sport)
231
- Tree.expects(:categories).with(array).returns(Tree)
232
- Tree.expects(:all).returns([mock_tree])
233
- get :index, :categories => array
234
- assert_equal([mock_tree], assigns(:@trees))
235
- assert_equal({ :categories => array }, current_scopes)
236
- end
237
-
238
- def test_array_of_blank_values_is_ignored
239
- Tree.expects(:categories).never
240
- Tree.expects(:all).returns([mock_tree])
241
- get :index, :categories => [""]
242
- assert_equal([mock_tree], assigns(:@trees))
243
- assert_equal({ }, current_scopes)
244
- end
245
-
246
- def test_scope_of_invalid_type_silently_fails
247
- Tree.expects(:all).returns([mock_tree])
248
- get :index, :paginate => "1"
249
- assert_equal([mock_tree], assigns(:@trees))
250
- assert_equal({ }, current_scopes)
251
- end
252
-
253
- def test_scope_is_called_with_default_value
254
- Tree.expects(:shadown_range).with(10).returns(Tree).in_sequence
255
- Tree.expects(:find).with('42').returns(mock_tree).in_sequence
256
- get :edit, :id => '42'
257
- assert_equal(mock_tree, assigns(:@tree))
258
- assert_equal({ :shadown_range => 10 }, current_scopes)
259
- end
260
-
261
- def test_default_scope_value_can_be_overwritten
262
- Tree.expects(:shadown_range).with('20').returns(Tree).in_sequence
263
- Tree.expects(:find).with('42').returns(mock_tree).in_sequence
264
- get :edit, :id => '42', :shadown_range => '20'
265
- assert_equal(mock_tree, assigns(:@tree))
266
- assert_equal({ :shadown_range => '20' }, current_scopes)
267
- end
268
-
269
- def test_scope_with_different_key
270
- Tree.expects(:root_type).with('outside').returns(Tree).in_sequence
271
- Tree.expects(:find).with('42').returns(mock_tree).in_sequence
272
- get :show, :id => '42', :root => 'outside'
273
- assert_equal(mock_tree, assigns(:@tree))
274
- assert_equal({ :root => 'outside' }, current_scopes)
275
- end
276
-
277
- def test_scope_with_default_value_as_a_proc_without_argument
278
- Date.expects(:today).returns("today")
279
- Tree.expects(:planted_before).with("today").returns(Tree)
280
- Tree.expects(:all).returns([mock_tree])
281
- get :index
282
- assert_equal([mock_tree], assigns(:@trees))
283
- assert_equal({ :planted_before => "today" }, current_scopes)
284
- end
285
-
286
- def test_scope_with_default_value_as_proc_with_argument
287
- session[:height] = 100
288
- Tree.expects(:calculate_height).with(100).returns(Tree).in_sequence
289
- Tree.expects(:new).returns(mock_tree).in_sequence
290
- get :new
291
- assert_equal(mock_tree, assigns(:@tree))
292
- assert_equal({ :calculate_height => 100 }, current_scopes)
293
- end
294
-
295
- def test_scope_with_custom_type
296
- parsed = Date.civil(2014,11,11)
297
- Tree.expects(:planted_after).with(parsed).returns(Tree)
298
- Tree.expects(:all).returns([mock_tree])
299
- get :index, :planted_after => "2014-11-11"
300
- assert_equal([mock_tree], assigns(:@trees))
301
- assert_equal({ :planted_after => parsed }, current_scopes)
302
- end
303
-
304
- def test_scope_with_boolean_block
305
- Tree.expects(:only_really_short!).with(@controller.object_id).returns(Tree)
306
- Tree.expects(:all).returns([mock_tree])
307
- get :index, :only_short => 'true'
308
- assert_equal([mock_tree], assigns(:@trees))
309
- assert_equal({ :only_short => true }, current_scopes)
310
- end
311
-
312
- def test_scope_with_other_block_types
313
- Tree.expects(:by_given_category).with(@controller.object_id, 'for_id').returns(Tree)
314
- Tree.expects(:all).returns([mock_tree])
315
- get :index, :by_category => 'for'
316
- assert_equal([mock_tree], assigns(:@trees))
317
- assert_equal({ :by_category => 'for' }, current_scopes)
318
- end
319
-
320
- def test_scope_with_nested_hash_and_in_option
321
- hash = { 'title' => 'the-title', 'content' => 'the-content' }
322
- Tree.expects(:title).with('the-title').returns(Tree)
323
- Tree.expects(:content).with('the-content').returns(Tree)
324
- Tree.expects(:all).returns([mock_tree])
325
- get :index, q: hash
326
- assert_equal([mock_tree], assigns(:@trees))
327
- assert_equal({ q: hash }, current_scopes)
328
- end
329
-
330
- def test_overwritten_scope
331
- assert_nil(TreesController.scopes_configuration[:categories][:if])
332
- assert_equal(:categories?, BonsaisController.scopes_configuration[:categories][:if])
333
- end
334
-
335
- protected
336
-
337
- if ActionPack::VERSION::MAJOR == 5
338
- # TODO: Remove this when we only support Rails 5.
339
- def get(action, params = {})
340
- super action, params: params
341
- end
342
- end
343
-
344
- def mock_tree(stubs={})
345
- @mock_tree ||= mock(stubs)
346
- end
347
-
348
- def current_scopes
349
- @controller.send :current_scopes
350
- end
351
-
352
- def assigns(ivar)
353
- @controller.instance_variable_get(ivar)
354
- end
355
- end
356
-
357
- class TreeHugger
358
- include HasScope
359
-
360
- has_scope :color
361
-
362
- def by_color
363
- apply_scopes(Tree, :color => 'blue')
364
- end
365
-
366
- end
367
-
368
- class HasScopeOutsideControllerTest < ActiveSupport::TestCase
369
-
370
- def test_has_scope_usable_outside_controller
371
- Tree.expects(:color).with('blue')
372
- TreeHugger.new.by_color
373
- end
374
-
375
- end
data/test/test_helper.rb DELETED
@@ -1,33 +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
- require 'active_support'
11
- require 'active_support/core_ext/string/strip'
12
- require 'action_controller'
13
- require 'action_dispatch/middleware/flash'
14
-
15
- $:.unshift File.expand_path('../../lib', __FILE__)
16
- require 'has_scope'
17
-
18
- HasScope::Routes = ActionDispatch::Routing::RouteSet.new
19
- HasScope::Routes.draw do
20
- get '/:controller(/:action(/:id))'
21
- end
22
-
23
- class ApplicationController < ActionController::Base
24
- include HasScope::Routes.url_helpers
25
- end
26
-
27
- class ActiveSupport::TestCase
28
- self.test_order = :random if respond_to?(:test_order=)
29
-
30
- setup do
31
- @routes = HasScope::Routes
32
- end
33
- end