has_scope 0.7.2 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
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