has_scope 0.6.0.rc → 0.8.0

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
- SHA1:
3
- metadata.gz: eb5c20d9ff1cb1bbd626b15574b9159fd50dfa47
4
- data.tar.gz: 783dd0bc45a0bf8d97a16c77bdee0f24a52c926a
2
+ SHA256:
3
+ metadata.gz: c24a5ed875375fb5da93be3f90ac863b0c72d676ecc1fe0959933924452b043d
4
+ data.tar.gz: 2f8aacdef384ce75c7fc2e4b4b697ba62e68293deeac5754597d10dffc044052
5
5
  SHA512:
6
- metadata.gz: 56bc3e1fc2043a227dd8bf18e34896e6b5c6d5693a61be1ef45b49f13cf035157a572efffdde6b3e95ef1f230cd498a8d044a004689635eb0367d7cdbf607efc
7
- data.tar.gz: 9cd682408a65660d72646f32a9447ceca58d498af77fdc502ea7ad0f9c39ff2aaa3f5a3fcb0f907c75ec99f6c62e7af3837c762b70e5dd1836c0b902b3a5b643
6
+ metadata.gz: 9541480b36218fb3c1b9b7c48a3297411c8875ec47bb1735b6b394127c02ad90010d79787931c0d0154d662b40f4c236627710ca7b2fa01edb7513f27e043fb9
7
+ data.tar.gz: 5927fbd54e4a019b2d9f9fd7f7a60cf929fd46eac8f15c4c0e8cdb0cab2cac80a2b432e4398a70013d6bf88a519df60d2b6f571fb4f7f5db2d141292762b9eba
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2009-2013 Plataforma Tecnologia. http://blog.plataformatec.com.br
1
+ Copyright 2009-2017 Plataforma Tecnologia. http://blog.plataformatec.com.br
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,16 +1,16 @@
1
1
  ## HasScope
2
2
 
3
- [![Gem Version](https://fury-badge.herokuapp.com/rb/has_scope.png)](http://badge.fury.io/rb/has_scope)
4
- [![Build Status](https://api.travis-ci.org/plataformatec/has_scope.png?branch=master)](http://travis-ci.org/plataformatec/has_scope)
5
- [![Code Climate](https://codeclimate.com/github/plataformatec/has_scope.png)](https://codeclimate.com/github/plataformatec/has_scope)
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)
6
5
 
7
- Has scope allows you to easily create controller filters based on your resources named scopes.
6
+ Has scope allows you to map incoming controller parameters to named scopes in your resources.
8
7
  Imagine the following model called graduations:
9
8
 
10
9
  ```ruby
11
10
  class Graduation < ActiveRecord::Base
12
- scope :featured, -> { where(:featured => true) }
13
- scope :by_degree, -> degree { where(:degree => degree) }
11
+ scope :featured, -> { where(featured: true) }
12
+ scope :by_degree, -> degree { where(degree: degree) }
13
+ scope :by_period, -> started_at, ended_at { where("started_at = ? AND ended_at = ?", started_at, ended_at) }
14
14
  end
15
15
  ```
16
16
 
@@ -18,7 +18,7 @@ You can use those named scopes as filters by declaring them on your controller:
18
18
 
19
19
  ```ruby
20
20
  class GraduationsController < ApplicationController
21
- has_scope :featured, :type => :boolean
21
+ has_scope :featured, type: :boolean
22
22
  has_scope :by_degree
23
23
  end
24
24
  ```
@@ -27,9 +27,9 @@ Now, if you want to apply them to an specific resource, you just need to call `a
27
27
 
28
28
  ```ruby
29
29
  class GraduationsController < ApplicationController
30
- has_scope :featured, :type => :boolean
30
+ has_scope :featured, type: :boolean
31
31
  has_scope :by_degree
32
- has_scope :by_period, :using => [:started_at, :ended_at]
32
+ has_scope :by_period, using: %i[started_at ended_at], type: :hash
33
33
 
34
34
  def index
35
35
  @graduations = apply_scopes(Graduation).all
@@ -46,7 +46,7 @@ Then for each request:
46
46
  /graduations?featured=true
47
47
  #=> calls the named scope and bring featured graduations
48
48
 
49
- /graduations?params[by_period][started_at]=20100701&params[by_period][ended_at]=20101013
49
+ /graduations?by_period[started_at]=20100701&by_period[ended_at]=20101013
50
50
  #=> brings graduations in the given period
51
51
 
52
52
  /graduations?featured=true&by_degree=phd
@@ -54,7 +54,7 @@ Then for each request:
54
54
  ```
55
55
 
56
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" }.
57
+ In the last case, it would return: `{ featured: true, by_degree: 'phd' }`.
58
58
 
59
59
  ## Installation
60
60
 
@@ -68,7 +68,11 @@ gem 'has_scope'
68
68
 
69
69
  HasScope supports several options:
70
70
 
71
- * `:type` - Checks the type of the parameter sent. If set to :boolean it just calls the named scope, without any argument. By default, it does not allow hashes or arrays to be given, except if type :hash or :array are set.
71
+ * `:type` - Checks the type of the parameter sent.
72
+ By default, it does not allow hashes or arrays to be given,
73
+ except if type `:hash` or `:array` are set.
74
+ Symbols are never permitted to prevent memory leaks, so ensure any routing
75
+ constraints you have that add parameters use string values.
72
76
 
73
77
  * `:only` - In which actions the scope is applied.
74
78
 
@@ -78,14 +82,38 @@ HasScope supports several options:
78
82
 
79
83
  * `:using` - The subkeys to be used as args when type is a hash.
80
84
 
81
- * `:if` - Specifies a method, proc or string to call to determine if the scope should apply.
85
+ * `:in` - A shortcut for combining the `:using` option with nested hashes.
82
86
 
83
- * `:unless` - Specifies a method, proc or string to call to determine if the scope should NOT apply.
87
+ * `: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.
88
+
89
+ * `: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.
84
90
 
85
91
  * `:default` - Default value for the scope. Whenever supplied the scope is always called.
86
92
 
87
93
  * `:allow_blank` - Blank values are not sent to scopes by default. Set to true to overwrite.
88
94
 
95
+ ## Boolean usage
96
+
97
+ If `type: :boolean` is set it just calls the named scope, without any arguments, when parameter
98
+ is set to a "true" value. `'true'` and `'1'` are parsed as `true`, everything else as `false`.
99
+
100
+ When boolean scope is set up with `allow_blank: true`, it will call the scope with the value as
101
+ any usual scope.
102
+
103
+ ```ruby
104
+ has_scope :visible, type: :boolean
105
+ has_scope :active, type: :boolean, allow_blank: true
106
+
107
+ # and models with
108
+ scope :visible, -> { where(visible: true) }
109
+ scope :active, ->(value = true) { where(active: value) }
110
+ ```
111
+
112
+ _Note_: it is not possible to apply a boolean scope with just the query param being present, e.g.
113
+ `?active`, that's not considered a "true" value (the param value will be `nil`), and thus the
114
+ scope will be called with `false` as argument. In order for the scope to receive a `true` argument
115
+ the param value must be set to one of the "true" values above, e.g. `?active=true` or `?active=1`.
116
+
89
117
  ## Block usage
90
118
 
91
119
  `has_scope` also accepts a block. The controller, current scope and value are yielded
@@ -94,22 +122,63 @@ need to manipulate the given value:
94
122
 
95
123
  ```ruby
96
124
  has_scope :category do |controller, scope, value|
97
- value != "all" ? scope.by_category(value) : scope
125
+  value != 'all' ? scope.by_category(value) : scope
98
126
  end
99
127
  ```
100
128
 
101
- When used with booleans, it just receives two arguments and is just invoked if true is given:
129
+ When used with booleans without `:allow_blank`, it just receives two arguments
130
+ and is just invoked if true is given:
102
131
 
103
132
  ```ruby
104
- has_scope :not_voted_by_me, :type => :boolean do |controller, scope|
133
+ has_scope :not_voted_by_me, type: :boolean do |controller, scope|
105
134
  scope.not_voted_by(controller.current_user.id)
106
135
  end
107
136
  ```
108
137
 
138
+ ## Keyword arguments
139
+
140
+ Scopes with keyword arguments need to be called in a block:
141
+
142
+ ```ruby
143
+ # in the model
144
+ scope :for_course, lambda { |course_id:| where(course_id: course_id) }
145
+
146
+ # in the controller
147
+ has_scope :for_course do |controller, scope, value|
148
+ scope.for_course(course_id: value)
149
+ end
150
+ ```
151
+
152
+ ## Apply scope on every request
153
+
154
+ To apply scope on every request set default value and `allow_blank: true`:
155
+
156
+ ```ruby
157
+ has_scope :available, default: nil, allow_blank: true, only: :show, unless: :admin?
158
+
159
+ # model:
160
+ scope :available, ->(*) { where(blocked: false) }
161
+ ```
162
+
163
+ This will allow usual users to get only available items, but admins will
164
+ be able to access blocked items too.
165
+
166
+ ## Check which scopes have been applied
167
+
168
+ To check which scopes have been applied, you can call `current_scopes` from the controller or view.
169
+ This returns a hash with the scope name as the key and the scope value as the value.
170
+
171
+ For example, if a boolean `:active` scope has been applied, `current_scopes` will return `{ active: true }`.
172
+
173
+ ## Supported Ruby / Rails versions
174
+
175
+ We intend to maintain support for all Ruby / Rails versions that haven't reached end-of-life.
176
+
177
+ For more information about specific versions please check [Ruby](https://www.ruby-lang.org/en/downloads/branches/)
178
+ and [Rails](https://guides.rubyonrails.org/maintenance_policy.html) maintenance policies, and our test matrix.
179
+
109
180
  ## Bugs and Feedback
110
181
 
111
182
  If you discover any bugs or want to drop a line, feel free to create an issue on GitHub.
112
183
 
113
- http://github.com/plataformatec/has_scope/issues
114
-
115
- MIT License. Copyright 2009-2013 Plataformatec. http://blog.plataformatec.com.br
184
+ MIT License. Copyright 2009-2019 Plataformatec. http://blog.plataformatec.com.br
data/lib/has_scope.rb CHANGED
@@ -1,17 +1,21 @@
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 ],
7
- :boolean => [ Object ],
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
18
+ self.scopes_configuration = {}
15
19
  end
16
20
  end
17
21
 
@@ -35,6 +39,8 @@ module HasScope
35
39
  # * <tt>:using</tt> - If type is a hash, you can provide :using to convert the hash to
36
40
  # a named scope call with several arguments.
37
41
  #
42
+ # * <tt>:in</tt> - A shortcut for combining the `:using` option with nested hashes.
43
+ #
38
44
  # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
39
45
  # if the scope should apply
40
46
  #
@@ -56,14 +62,23 @@ module HasScope
56
62
  # value != "all" ? scope.by_category(value) : scope
57
63
  # end
58
64
  #
59
- # has_scope :not_voted_by_me, :type => :boolean do |controller, scope|
65
+ # has_scope :not_voted_by_me, type: :boolean do |controller, scope|
60
66
  # scope.not_voted_by(controller.current_user.id)
61
67
  # end
62
68
  #
63
69
  def has_scope(*scopes, &block)
64
70
  options = scopes.extract_options!
65
71
  options.symbolize_keys!
66
- options.assert_valid_keys(:type, :only, :except, :if, :unless, :default, :as, :using, :allow_blank)
72
+ options.assert_valid_keys(:type, :only, :except, :if, :unless, :default, :as, :using, :allow_blank, :in)
73
+
74
+ if options.key?(:in)
75
+ options[:as] = options[:in]
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
81
+ end
67
82
 
68
83
  if options.key?(:using)
69
84
  if options.key?(:type) && options[:type] != :hash
@@ -78,11 +93,11 @@ module HasScope
78
93
  options[:only] = Array(options[:only])
79
94
  options[:except] = Array(options[:except])
80
95
 
81
- self.scopes_configuration = (self.scopes_configuration || {}).dup
96
+ self.scopes_configuration = scopes_configuration.dup
82
97
 
83
98
  scopes.each do |scope|
84
- self.scopes_configuration[scope] ||= { :as => scope, :type => :default, :block => block }
85
- self.scopes_configuration[scope] = self.scopes_configuration[scope].merge(options)
99
+ scopes_configuration[scope] ||= { as: scope, type: :default, block: block }
100
+ scopes_configuration[scope] = self.scopes_configuration[scope].merge(options)
86
101
  end
87
102
  end
88
103
  end
@@ -91,19 +106,17 @@ module HasScope
91
106
 
92
107
  # Receives an object where scopes will be applied to.
93
108
  #
94
- # class GraduationsController < InheritedResources::Base
95
- # has_scope :featured, :type => true, :only => :index
96
- # 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
97
112
  #
98
113
  # def index
99
114
  # @graduations = apply_scopes(Graduation).all
100
115
  # end
101
116
  # end
102
117
  #
103
- def apply_scopes(target, hash=params)
104
- return target unless scopes_configuration
105
-
106
- self.scopes_configuration.each do |scope, options|
118
+ def apply_scopes(target, hash = params)
119
+ scopes_configuration.each do |scope, options|
107
120
  next unless apply_scope_to_action?(options)
108
121
  key = options[:as]
109
122
 
@@ -111,15 +124,25 @@ module HasScope
111
124
  value, call_scope = hash[key], true
112
125
  elsif options.key?(:default)
113
126
  value, call_scope = options[:default], true
114
- value = value.call(self) if value.is_a?(Proc)
127
+ if value.is_a?(Proc)
128
+ value = value.arity == 0 ? value.call : value.call(self)
129
+ end
115
130
  end
116
131
 
117
- value = parse_value(options[:type], key, value)
132
+ value = parse_value(options[:type], value)
118
133
  value = normalize_blanks(value)
119
134
 
120
- 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
121
144
  current_scopes[key] = value
122
- target = call_scope_by_type(options[:type], scope, target, value, options)
145
+ target = call_scope_by_type(options[:type], scope, target, scope_value, options)
123
146
  end
124
147
  end
125
148
 
@@ -127,21 +150,22 @@ module HasScope
127
150
  end
128
151
 
129
152
  # Set the real value for the current scope if type check.
130
- def parse_value(type, key, value) #:nodoc:
131
- if type == :boolean
132
- TRUE_VALUES.include?(value)
133
- elsif value && ALLOWED_TYPES[type].any?{ |klass| value.is_a?(klass) }
134
- value
153
+ def parse_value(type, value) #:nodoc:
154
+ klasses, parser = ALLOWED_TYPES[type]
155
+ if klasses.any? { |klass| value.is_a?(klass) }
156
+ parser ? parser.call(value) : value
135
157
  end
136
158
  end
137
159
 
138
160
  # Screens pseudo-blank params.
139
161
  def normalize_blanks(value) #:nodoc:
140
- return value if value.nil?
141
- if value.is_a?(Array)
162
+ case value
163
+ when Array
142
164
  value.select { |v| v.present? }
143
- elsif value.is_a?(Hash)
165
+ when Hash
144
166
  value.select { |k, v| normalize_blanks(v).present? }.with_indifferent_access
167
+ when ActionController::Parameters
168
+ normalize_blanks(value.to_unsafe_h)
145
169
  else
146
170
  value
147
171
  end
@@ -151,10 +175,9 @@ module HasScope
151
175
  def call_scope_by_type(type, scope, target, value, options) #:nodoc:
152
176
  block = options[:block]
153
177
 
154
- if type == :boolean
178
+ if type == :boolean && !options[:allow_blank]
155
179
  block ? block.call(self, target) : target.send(scope)
156
- elsif value && options.key?(:using)
157
- value = value.values_at(*options[:using])
180
+ elsif options.key?(:using)
158
181
  block ? block.call(self, target, value) : target.send(scope, *value)
159
182
  else
160
183
  block ? block.call(self, target, value) : target.send(scope, value)
@@ -177,14 +200,19 @@ module HasScope
177
200
  # method, or string evals to the expected value.
178
201
  def applicable?(string_proc_or_symbol, expected) #:nodoc:
179
202
  case string_proc_or_symbol
180
- when String
181
- eval(string_proc_or_symbol) == expected
182
- when Proc
183
- string_proc_or_symbol.call(self) == expected
184
- when Symbol
185
- send(string_proc_or_symbol) == expected
186
- else
187
- true
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
+
209
+ eval(string_proc_or_symbol) == expected
210
+ when Proc
211
+ string_proc_or_symbol.call(self) == expected
212
+ when Symbol
213
+ send(string_proc_or_symbol) == expected
214
+ else
215
+ true
188
216
  end
189
217
  end
190
218
 
@@ -196,5 +224,5 @@ end
196
224
 
197
225
  ActiveSupport.on_load :action_controller do
198
226
  include HasScope
199
- helper_method :current_scopes
227
+ helper_method :current_scopes if respond_to?(:helper_method)
200
228
  end
@@ -1,3 +1,3 @@
1
1
  module HasScope
2
- VERSION = "0.6.0.rc"
2
+ VERSION = "0.8.0"
3
3
  end
@@ -1,19 +1,34 @@
1
1
  require 'test_helper'
2
2
 
3
- class Tree
4
- end
3
+ HasScope::ALLOWED_TYPES[:date] = [[String], -> v { Date.parse(v) rescue nil }]
4
+
5
+ class Tree; end
5
6
 
6
7
  class TreesController < ApplicationController
7
- has_scope :color, :unless => :show_all_colors?
8
- has_scope :only_tall, :type => :boolean, :only => :index, :if => :restrict_to_only_tall_trees?
9
- has_scope :shadown_range, :default => 10, :except => [ :index, :show, :new ]
10
- has_scope :root_type, :as => :root, :allow_blank => true
11
- has_scope :calculate_height, :default => proc {|c| c.session[:height] || 20 }, :only => :new
12
- has_scope :paginate, :type => :hash
13
- has_scope :args_paginate, :type => :hash, :using => [:page, :per_page]
14
- has_scope :categories, :type => :array
15
-
16
- has_scope :only_short, :type => :boolean do |controller, scope|
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|
17
32
  scope.only_really_short!(controller.object_id)
18
33
  end
19
34
 
@@ -32,9 +47,20 @@ class TreesController < ApplicationController
32
47
  def show
33
48
  @tree = apply_scopes(Tree).find(params[:id])
34
49
  end
50
+
35
51
  alias :edit :show
36
52
 
37
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
+
38
64
  def restrict_to_only_tall_trees?
39
65
  true
40
66
  end
@@ -44,12 +70,12 @@ class TreesController < ApplicationController
44
70
  end
45
71
 
46
72
  def default_render
47
- render :text => action_name
73
+ render body: action_name
48
74
  end
49
75
  end
50
76
 
51
77
  class BonsaisController < TreesController
52
- has_scope :categories, :if => :categories?
78
+ has_scope :categories, if: :categories?
53
79
 
54
80
  protected
55
81
  def categories?
@@ -63,24 +89,60 @@ class HasScopeTest < ActionController::TestCase
63
89
  def test_boolean_scope_is_called_when_boolean_param_is_true
64
90
  Tree.expects(:only_tall).with().returns(Tree).in_sequence
65
91
  Tree.expects(:all).returns([mock_tree]).in_sequence
66
- get :index, :only_tall => 'true'
67
- assert_equal([mock_tree], assigns(:trees))
68
- assert_equal({ :only_tall => true }, current_scopes)
92
+
93
+ get :index, params: { only_tall: 'true' }
94
+
95
+ assert_equal([mock_tree], assigns(:@trees))
96
+ assert_equal({ only_tall: true }, current_scopes)
69
97
  end
70
98
 
71
99
  def test_boolean_scope_is_not_called_when_boolean_param_is_false
72
100
  Tree.expects(:only_tall).never
73
101
  Tree.expects(:all).returns([mock_tree])
74
- get :index, :only_tall => 'false'
75
- assert_equal([mock_tree], assigns(:trees))
76
- assert_equal({}, current_scopes)
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)
77
137
  end
78
138
 
79
139
  def test_scope_is_called_only_on_index
80
140
  Tree.expects(:only_tall).never
81
141
  Tree.expects(:find).with('42').returns(mock_tree)
82
- get :show, :only_tall => 'true', :id => '42'
83
- assert_equal(mock_tree, assigns(:tree))
142
+
143
+ get :show, params: { only_tall: 'true', id: '42' }
144
+
145
+ assert_equal(mock_tree, assigns(:@tree))
84
146
  assert_equal({ }, current_scopes)
85
147
  end
86
148
 
@@ -88,8 +150,10 @@ class HasScopeTest < ActionController::TestCase
88
150
  @controller.stubs(:restrict_to_only_tall_trees?).returns(false)
89
151
  Tree.expects(:only_tall).never
90
152
  Tree.expects(:all).returns([mock_tree])
91
- get :index, :only_tall => 'true'
92
- assert_equal([mock_tree], assigns(:trees))
153
+
154
+ get :index, params: { only_tall: 'true' }
155
+
156
+ assert_equal([mock_tree], assigns(:@trees))
93
157
  assert_equal({ }, current_scopes)
94
158
  end
95
159
 
@@ -97,85 +161,149 @@ class HasScopeTest < ActionController::TestCase
97
161
  @controller.stubs(:show_all_colors?).returns(true)
98
162
  Tree.expects(:color).never
99
163
  Tree.expects(:all).returns([mock_tree])
100
- get :index, :color => 'blue'
101
- assert_equal([mock_tree], assigns(:trees))
164
+
165
+ get :index, params: { color: 'blue' }
166
+
167
+ assert_equal([mock_tree], assigns(:@trees))
102
168
  assert_equal({ }, current_scopes)
103
169
  end
104
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
+
105
193
  def test_scope_is_called_except_on_index
106
- Tree.expects(:shadown_range).with().never
194
+ Tree.expects(:shadown_range).never
107
195
  Tree.expects(:all).returns([mock_tree])
108
- get :index, :shadown_range => 20
109
- assert_equal([mock_tree], assigns(:trees))
196
+
197
+ get :index, params: { shadown_range: 20 }
198
+
199
+ assert_equal([mock_tree], assigns(:@trees))
110
200
  assert_equal({ }, current_scopes)
111
201
  end
112
202
 
113
203
  def test_scope_is_called_with_arguments
114
204
  Tree.expects(:color).with('blue').returns(Tree).in_sequence
115
205
  Tree.expects(:all).returns([mock_tree]).in_sequence
116
- get :index, :color => 'blue'
117
- assert_equal([mock_tree], assigns(:trees))
118
- assert_equal({ :color => 'blue' }, current_scopes)
206
+
207
+ get :index, params: { color: 'blue' }
208
+
209
+ assert_equal([mock_tree], assigns(:@trees))
210
+ assert_equal({ color: 'blue' }, current_scopes)
119
211
  end
120
212
 
121
213
  def test_scope_is_not_called_if_blank
122
214
  Tree.expects(:color).never
123
215
  Tree.expects(:all).returns([mock_tree]).in_sequence
124
- get :index, :color => ''
125
- assert_equal([mock_tree], assigns(:trees))
216
+
217
+ get :index, params: { color: '' }
218
+
219
+ assert_equal([mock_tree], assigns(:@trees))
126
220
  assert_equal({ }, current_scopes)
127
221
  end
128
222
 
129
223
  def test_scope_is_called_when_blank_if_allow_blank_is_given
130
224
  Tree.expects(:root_type).with('').returns(Tree)
131
225
  Tree.expects(:all).returns([mock_tree]).in_sequence
132
- get :index, :root => ''
133
- assert_equal([mock_tree], assigns(:trees))
134
- assert_equal({ :root => '' }, current_scopes)
226
+
227
+ get :index, params: { root: '' }
228
+
229
+ assert_equal([mock_tree], assigns(:@trees))
230
+ assert_equal({ root: '' }, current_scopes)
135
231
  end
136
232
 
137
233
  def test_multiple_scopes_are_called
138
234
  Tree.expects(:only_tall).with().returns(Tree)
139
235
  Tree.expects(:color).with('blue').returns(Tree)
140
236
  Tree.expects(:all).returns([mock_tree])
141
- get :index, :color => 'blue', :only_tall => 'true'
142
- assert_equal([mock_tree], assigns(:trees))
143
- assert_equal({ :color => 'blue', :only_tall => true }, current_scopes)
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)
144
242
  end
145
243
 
146
244
  def test_scope_of_type_hash
147
245
  hash = { "page" => "1", "per_page" => "10" }
148
246
  Tree.expects(:paginate).with(hash).returns(Tree)
149
247
  Tree.expects(:all).returns([mock_tree])
150
- get :index, :paginate => hash
151
- assert_equal([mock_tree], assigns(:trees))
152
- assert_equal({ :paginate => hash }, current_scopes)
248
+
249
+ get :index, params: { paginate: hash }
250
+
251
+ assert_equal([mock_tree], assigns(:@trees))
252
+ assert_equal({ paginate: hash }, current_scopes)
153
253
  end
154
254
 
155
255
  def test_scope_of_type_hash_with_using
156
256
  hash = { "page" => "1", "per_page" => "10" }
157
257
  Tree.expects(:args_paginate).with("1", "10").returns(Tree)
158
258
  Tree.expects(:all).returns([mock_tree])
159
- get :index, :args_paginate => hash
160
- assert_equal([mock_tree], assigns(:trees))
161
- assert_equal({ :args_paginate => hash }, current_scopes)
259
+
260
+ get :index, params: { args_paginate: hash }
261
+
262
+ assert_equal([mock_tree], assigns(:@trees))
263
+ assert_equal({ args_paginate: hash }, current_scopes)
162
264
  end
163
265
 
164
266
  def test_hash_with_blank_values_is_ignored
165
267
  hash = { "page" => "", "per_page" => "" }
166
268
  Tree.expects(:paginate).never
167
269
  Tree.expects(:all).returns([mock_tree])
168
- get :index, :paginate => hash
169
- assert_equal([mock_tree], assigns(:trees))
270
+
271
+ get :index, params: { paginate: hash }
272
+
273
+ assert_equal([mock_tree], assigns(:@trees))
170
274
  assert_equal({ }, current_scopes)
171
275
  end
172
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
+
173
299
  def test_nested_hash_with_blank_values_is_ignored
174
- hash = { "parent" => {"children" => ""} }
300
+ hash = { "parent" => { "children" => "" } }
175
301
  Tree.expects(:paginate).never
176
302
  Tree.expects(:all).returns([mock_tree])
177
- get :index, :paginate => hash
178
- assert_equal([mock_tree], assigns(:trees))
303
+
304
+ get :index, params: { paginate: hash }
305
+
306
+ assert_equal([mock_tree], assigns(:@trees))
179
307
  assert_equal({ }, current_scopes)
180
308
  end
181
309
 
@@ -183,8 +311,10 @@ class HasScopeTest < ActionController::TestCase
183
311
  hash = { "parent" => [""] }
184
312
  Tree.expects(:paginate).never
185
313
  Tree.expects(:all).returns([mock_tree])
186
- get :index, :paginate => hash
187
- assert_equal([mock_tree], assigns(:trees))
314
+
315
+ get :index, params: { paginate: hash }
316
+
317
+ assert_equal([mock_tree], assigns(:@trees))
188
318
  assert_equal({ }, current_scopes)
189
319
  end
190
320
 
@@ -192,89 +322,170 @@ class HasScopeTest < ActionController::TestCase
192
322
  array = %w(book kitchen sport)
193
323
  Tree.expects(:categories).with(array).returns(Tree)
194
324
  Tree.expects(:all).returns([mock_tree])
195
- get :index, :categories => array
196
- assert_equal([mock_tree], assigns(:trees))
197
- assert_equal({ :categories => array }, current_scopes)
325
+
326
+ get :index, params: { categories: array }
327
+
328
+ assert_equal([mock_tree], assigns(:@trees))
329
+ assert_equal({ categories: array }, current_scopes)
198
330
  end
199
331
 
200
332
  def test_array_of_blank_values_is_ignored
201
333
  Tree.expects(:categories).never
202
334
  Tree.expects(:all).returns([mock_tree])
203
- get :index, :categories => [""]
204
- assert_equal([mock_tree], assigns(:trees))
335
+
336
+ get :index, params: { categories: [""] }
337
+
338
+ assert_equal([mock_tree], assigns(:@trees))
205
339
  assert_equal({ }, current_scopes)
206
340
  end
207
341
 
208
342
  def test_scope_of_invalid_type_silently_fails
209
343
  Tree.expects(:all).returns([mock_tree])
210
- get :index, :paginate => "1"
211
- assert_equal([mock_tree], assigns(:trees))
212
- assert_equal({}, current_scopes)
344
+
345
+ get :index, params: { paginate: "1" }
346
+
347
+ assert_equal([mock_tree], assigns(:@trees))
348
+ assert_equal({ }, current_scopes)
213
349
  end
214
350
 
215
351
  def test_scope_is_called_with_default_value
216
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
217
356
  Tree.expects(:find).with('42').returns(mock_tree).in_sequence
218
- get :edit, :id => '42'
219
- assert_equal(mock_tree, assigns(:tree))
220
- assert_equal({ :shadown_range => 10 }, current_scopes)
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)
221
367
  end
222
368
 
223
369
  def test_default_scope_value_can_be_overwritten
224
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
225
375
  Tree.expects(:find).with('42').returns(mock_tree).in_sequence
226
- get :edit, :id => '42', :shadown_range => '20'
227
- assert_equal(mock_tree, assigns(:tree))
228
- assert_equal({ :shadown_range => '20' }, current_scopes)
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)
229
392
  end
230
393
 
231
394
  def test_scope_with_different_key
232
395
  Tree.expects(:root_type).with('outside').returns(Tree).in_sequence
233
396
  Tree.expects(:find).with('42').returns(mock_tree).in_sequence
234
- get :show, :id => '42', :root => 'outside'
235
- assert_equal(mock_tree, assigns(:tree))
236
- assert_equal({ :root => 'outside' }, current_scopes)
397
+
398
+ get :show, params: { id: '42', root: 'outside' }
399
+
400
+ assert_equal(mock_tree, assigns(:@tree))
401
+ assert_equal({ root: 'outside' }, current_scopes)
237
402
  end
238
403
 
239
- def test_scope_with_default_value_as_proc
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
240
416
  session[:height] = 100
241
417
  Tree.expects(:calculate_height).with(100).returns(Tree).in_sequence
242
418
  Tree.expects(:new).returns(mock_tree).in_sequence
419
+
243
420
  get :new
244
- assert_equal(mock_tree, assigns(:tree))
245
- assert_equal({ :calculate_height => 100 }, current_scopes)
246
- end
247
-
248
- def test_scope_with_boolean_block
249
- Tree.expects(:only_really_short!).with(@controller.object_id).returns(Tree)
250
- Tree.expects(:all).returns([mock_tree])
251
- get :index, :only_short => 'true'
252
- assert_equal([mock_tree], assigns(:trees))
253
- assert_equal({ :only_short => true }, current_scopes)
254
- end
255
-
256
- def test_scope_with_other_block_types
257
- Tree.expects(:by_given_category).with(@controller.object_id, 'for_id').returns(Tree)
258
- Tree.expects(:all).returns([mock_tree])
259
- get :index, :by_category => 'for'
260
- assert_equal([mock_tree], assigns(:trees))
261
- assert_equal({ :by_category => 'for' }, current_scopes)
262
- end
263
-
264
- def test_overwritten_scope
265
- assert_nil(TreesController.scopes_configuration[:categories][:if])
266
- assert_equal(:categories?, BonsaisController.scopes_configuration[:categories][:if])
267
- end
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
268
475
 
269
476
  protected
270
477
 
271
- def mock_tree(stubs={})
478
+ def mock_tree(stubs = {})
272
479
  @mock_tree ||= mock(stubs)
273
480
  end
274
481
 
275
482
  def current_scopes
276
483
  @controller.send :current_scopes
277
484
  end
485
+
486
+ def assigns(ivar)
487
+ @controller.instance_variable_get(ivar)
488
+ end
278
489
  end
279
490
 
280
491
  class TreeHugger
@@ -283,16 +494,14 @@ class TreeHugger
283
494
  has_scope :color
284
495
 
285
496
  def by_color
286
- apply_scopes(Tree, :color => 'blue')
497
+ apply_scopes(Tree, color: 'blue')
287
498
  end
288
-
289
499
  end
290
500
 
291
501
  class HasScopeOutsideControllerTest < ActiveSupport::TestCase
292
-
293
502
  def test_has_scope_usable_outside_controller
294
503
  Tree.expects(:color).with('blue')
504
+
295
505
  TreeHugger.new.by_color
296
506
  end
297
-
298
507
  end