has_scope 0.6.0.rc → 0.8.0

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
- 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