Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 206 lines (181 sloc) 6.662 kb
f6fb5fd @josevalim First commit, ported has_scope from InheritedResources.
josevalim authored
1 module HasScope
2 TRUE_VALUES = ["true", true, "1", 1]
ccf5b4f @josevalim Updated README and docs.
josevalim authored
3
beaa019 @carlosantoniodasilva Keep the constant rather than a new accessor, update changelog and de…
carlosantoniodasilva authored
4 ALLOWED_TYPES = {
5 :array => [[ Array ]],
6 :hash => [[ Hash ]],
7 :boolean => [[ Object ], -> v { TRUE_VALUES.include?(v) }],
8 :default => [[ String, Numeric ]],
be17718 @josevalim Add strong type check to ensure that filters do not accept hashes (wh…
josevalim authored
9 }
10
f6fb5fd @josevalim First commit, ported has_scope from InheritedResources.
josevalim authored
11 def self.included(base)
12 base.class_eval do
13 extend ClassMethods
4861079 @jackdempsey use class_attribute now instead of class_inheritable_hash
jackdempsey authored
14 class_attribute :scopes_configuration, :instance_writer => false
1c58143 @carlosantoniodasilva Initialize scopes configuration to avoid guard method
carlosantoniodasilva authored
15 self.scopes_configuration = {}
f6fb5fd @josevalim First commit, ported has_scope from InheritedResources.
josevalim authored
16 end
17 end
18
19 module ClassMethods
20 # Detects params from url and apply as scopes to your classes.
21 #
22 # == Options
23 #
be17718 @josevalim Add strong type check to ensure that filters do not accept hashes (wh…
josevalim authored
24 # * <tt>:type</tt> - Checks the type of the parameter sent. If set to :boolean
25 # it just calls the named scope, without any argument. By default,
26 # it does not allow hashes or arrays to be given, except if type
27 # :hash or :array are set.
f6fb5fd @josevalim First commit, ported has_scope from InheritedResources.
josevalim authored
28 #
29 # * <tt>:only</tt> - In which actions the scope is applied. By default is :all.
30 #
31 # * <tt>:except</tt> - In which actions the scope is not applied. By default is :none.
32 #
33 # * <tt>:as</tt> - The key in the params hash expected to find the scope.
34 # Defaults to the scope name.
35 #
249a8c8 @josevalim Add support to :using and release a new gem.
josevalim authored
36 # * <tt>:using</tt> - If type is a hash, you can provide :using to convert the hash to
37 # a named scope call with several arguments.
38 #
f6fb5fd @josevalim First commit, ported has_scope from InheritedResources.
josevalim authored
39 # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
40 # if the scope should apply
41 #
42 # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
43 # if the scope should NOT apply.
44 #
45 # * <tt>:default</tt> - Default value for the scope. Whenever supplied the scope
be17718 @josevalim Add strong type check to ensure that filters do not accept hashes (wh…
josevalim authored
46 # is always called.
f6fb5fd @josevalim First commit, ported has_scope from InheritedResources.
josevalim authored
47 #
b11b092 @josevalim Add support to :allow_blank and blocks and release 0.3.
josevalim authored
48 # * <tt>:allow_blank</tt> - Blank values are not sent to scopes by default. Set to true to overwrite.
49 #
50 # == Block usage
51 #
52 # has_scope also accepts a block. The controller, current scope and value are yielded
53 # to the block so the user can apply the scope on its own. This is useful in case we
54 # need to manipulate the given value:
55 #
56 # has_scope :category do |controller, scope, value|
57 # value != "all" ? scope.by_category(value) : scope
58 # end
59 #
60 # has_scope :not_voted_by_me, :type => :boolean do |controller, scope|
46b35ea @carlosantoniodasilva Add Gemfile, cleanup test helper. Tests green on Rails 3
carlosantoniodasilva authored
61 # scope.not_voted_by(controller.current_user.id)
b11b092 @josevalim Add support to :allow_blank and blocks and release 0.3.
josevalim authored
62 # end
63 #
64 def has_scope(*scopes, &block)
f6fb5fd @josevalim First commit, ported has_scope from InheritedResources.
josevalim authored
65 options = scopes.extract_options!
66 options.symbolize_keys!
2a2a28d @lucasmazza Add the `:in` option.
lucasmazza authored
67 options.assert_valid_keys(:type, :only, :except, :if, :unless, :default, :as, :using, :allow_blank, :in)
68
69 if options.key?(:in)
70 options[:as] = options[:in]
71 options[:using] = scopes
72 end
249a8c8 @josevalim Add support to :using and release a new gem.
josevalim authored
73
74 if options.key?(:using)
75 if options.key?(:type) && options[:type] != :hash
76 raise "You cannot use :using with another :type different than :hash"
77 else
78 options[:type] = :hash
79 end
80
81 options[:using] = Array(options[:using])
82 end
f6fb5fd @josevalim First commit, ported has_scope from InheritedResources.
josevalim authored
83
be17718 @josevalim Add strong type check to ensure that filters do not accept hashes (wh…
josevalim authored
84 options[:only] = Array(options[:only])
85 options[:except] = Array(options[:except])
2f23811 @josevalim Check if scopes configuration is not nil.
josevalim authored
86
1c58143 @carlosantoniodasilva Initialize scopes configuration to avoid guard method
carlosantoniodasilva authored
87 self.scopes_configuration = scopes_configuration.dup
be17718 @josevalim Add strong type check to ensure that filters do not accept hashes (wh…
josevalim authored
88
89 scopes.each do |scope|
1c58143 @carlosantoniodasilva Initialize scopes configuration to avoid guard method
carlosantoniodasilva authored
90 scopes_configuration[scope] ||= { :as => scope, :type => :default, :block => block }
91 scopes_configuration[scope] = self.scopes_configuration[scope].merge(options)
f6fb5fd @josevalim First commit, ported has_scope from InheritedResources.
josevalim authored
92 end
93 end
94 end
95
96 protected
97
98 # Receives an object where scopes will be applied to.
99 #
100 # class GraduationsController < InheritedResources::Base
249a8c8 @josevalim Add support to :using and release a new gem.
josevalim authored
101 # has_scope :featured, :type => true, :only => :index
f6fb5fd @josevalim First commit, ported has_scope from InheritedResources.
josevalim authored
102 # has_scope :by_degree, :only => :index
103 #
104 # def index
105 # @graduations = apply_scopes(Graduation).all
106 # end
107 # end
108 #
249a8c8 @josevalim Add support to :using and release a new gem.
josevalim authored
109 def apply_scopes(target, hash=params)
1c58143 @carlosantoniodasilva Initialize scopes configuration to avoid guard method
carlosantoniodasilva authored
110 scopes_configuration.each do |scope, options|
f6fb5fd @josevalim First commit, ported has_scope from InheritedResources.
josevalim authored
111 next unless apply_scope_to_action?(options)
112 key = options[:as]
113
249a8c8 @josevalim Add support to :using and release a new gem.
josevalim authored
114 if hash.key?(key)
115 value, call_scope = hash[key], true
f6fb5fd @josevalim First commit, ported has_scope from InheritedResources.
josevalim authored
116 elsif options.key?(:default)
117 value, call_scope = options[:default], true
f4fb317 default value as a proc without argument
Krzysztof Herod authored
118 if value.is_a?(Proc)
119 value = value.arity == 0 ? value.call : value.call(self)
120 end
f6fb5fd @josevalim First commit, ported has_scope from InheritedResources.
josevalim authored
121 end
122
249a8c8 @josevalim Add support to :using and release a new gem.
josevalim authored
123 value = parse_value(options[:type], key, value)
3fa4892 @tfwright Screen blank array and hash params
tfwright authored
124 value = normalize_blanks(value)
249a8c8 @josevalim Add support to :using and release a new gem.
josevalim authored
125
b11b092 @josevalim Add support to :allow_blank and blocks and release 0.3.
josevalim authored
126 if call_scope && (value.present? || options[:allow_blank])
249a8c8 @josevalim Add support to :using and release a new gem.
josevalim authored
127 current_scopes[key] = value
128 target = call_scope_by_type(options[:type], scope, target, value, options)
b11b092 @josevalim Add support to :allow_blank and blocks and release 0.3.
josevalim authored
129 end
f6fb5fd @josevalim First commit, ported has_scope from InheritedResources.
josevalim authored
130 end
131
be17718 @josevalim Add strong type check to ensure that filters do not accept hashes (wh…
josevalim authored
132 target
133 end
134
b11b092 @josevalim Add support to :allow_blank and blocks and release 0.3.
josevalim authored
135 # Set the real value for the current scope if type check.
249a8c8 @josevalim Add support to :using and release a new gem.
josevalim authored
136 def parse_value(type, key, value) #:nodoc:
beaa019 @carlosantoniodasilva Keep the constant rather than a new accessor, update changelog and de…
carlosantoniodasilva authored
137 klasses, parser = ALLOWED_TYPES[type]
138 if klasses.any? { |klass| value.is_a?(klass) }
522097a @dim Allow custom types and parsers
dim authored
139 parser ? parser.call(value) : value
b11b092 @josevalim Add support to :allow_blank and blocks and release 0.3.
josevalim authored
140 end
141 end
142
3fa4892 @tfwright Screen blank array and hash params
tfwright authored
143 # Screens pseudo-blank params.
144 def normalize_blanks(value) #:nodoc:
d94cd52 @carlosantoniodasilva Refactor is_a? conditionals with case
carlosantoniodasilva authored
145 case value
146 when Array
3fa4892 @tfwright Screen blank array and hash params
tfwright authored
147 value.select { |v| v.present? }
d94cd52 @carlosantoniodasilva Refactor is_a? conditionals with case
carlosantoniodasilva authored
148 when Hash
3fa4892 @tfwright Screen blank array and hash params
tfwright authored
149 value.select { |k, v| normalize_blanks(v).present? }.with_indifferent_access
150 else
151 value
152 end
153 end
154
249a8c8 @josevalim Add support to :using and release a new gem.
josevalim authored
155 # Call the scope taking into account its type.
156 def call_scope_by_type(type, scope, target, value, options) #:nodoc:
157 block = options[:block]
158
f0e6a9a @printercu Boolean scopes with `allow_blank: true` are called with values
printercu authored
159 if type == :boolean && !options[:allow_blank]
249a8c8 @josevalim Add support to :using and release a new gem.
josevalim authored
160 block ? block.call(self, target) : target.send(scope)
161 elsif value && options.key?(:using)
162 value = value.values_at(*options[:using])
163 block ? block.call(self, target, value) : target.send(scope, *value)
b11b092 @josevalim Add support to :allow_blank and blocks and release 0.3.
josevalim authored
164 else
165 block ? block.call(self, target, value) : target.send(scope, value)
be17718 @josevalim Add strong type check to ensure that filters do not accept hashes (wh…
josevalim authored
166 end
f6fb5fd @josevalim First commit, ported has_scope from InheritedResources.
josevalim authored
167 end
168
169 # Given an options with :only and :except arrays, check if the scope
170 # can be performed in the current action.
171 def apply_scope_to_action?(options) #:nodoc:
172 return false unless applicable?(options[:if], true) && applicable?(options[:unless], false)
173
174 if options[:only].empty?
175 options[:except].empty? || !options[:except].include?(action_name.to_sym)
176 else
177 options[:only].include?(action_name.to_sym)
178 end
179 end
180
181 # Evaluates the scope options :if or :unless. Returns true if the proc
182 # method, or string evals to the expected value.
183 def applicable?(string_proc_or_symbol, expected) #:nodoc:
184 case string_proc_or_symbol
d51dd66 @carlosantoniodasilva Reindent case statement
carlosantoniodasilva authored
185 when String
186 eval(string_proc_or_symbol) == expected
187 when Proc
188 string_proc_or_symbol.call(self) == expected
189 when Symbol
190 send(string_proc_or_symbol) == expected
191 else
192 true
f6fb5fd @josevalim First commit, ported has_scope from InheritedResources.
josevalim authored
193 end
194 end
195
196 # Returns the scopes used in this action.
197 def current_scopes
198 @current_scopes ||= {}
199 end
200 end
201
618fab1 @kreeger Updating ActiveSupport patching to use #on_load.
kreeger authored
202 ActiveSupport.on_load :action_controller do
9c6d630 @iain Apply helper_method only on ActionController::Base
iain authored
203 include HasScope
204 helper_method :current_scopes
205 end
Something went wrong with that request. Please try again.