has_scope 0.1 → 0.2
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.
- data/README.rdoc +4 -3
- data/Rakefile +1 -1
- data/lib/has_scope.rb +39 -23
- data/test/has_scope_test.rb +33 -1
- metadata +2 -2
data/README.rdoc
CHANGED
@@ -11,14 +11,14 @@ Imagine the following model called graduations:
|
|
11
11
|
You can use those named scopes as filters by declaring them on your controller:
|
12
12
|
|
13
13
|
class GraduationsController < ApplicationController
|
14
|
-
has_scope :featured, :
|
14
|
+
has_scope :featured, :type => :boolean
|
15
15
|
has_scope :by_degree
|
16
16
|
end
|
17
17
|
|
18
18
|
Now, if you want to apply them to an specific resource, you just need to call <tt>apply_scopes</tt>:
|
19
19
|
|
20
20
|
class GraduationsController < ApplicationController
|
21
|
-
has_scope :featured, :
|
21
|
+
has_scope :featured, :type => :boolean
|
22
22
|
has_scope :by_degree
|
23
23
|
|
24
24
|
def index
|
@@ -40,7 +40,8 @@ Then for each request:
|
|
40
40
|
You can retrieve all the scopes applied in one action with <tt>current_scopes</tt> method.
|
41
41
|
In the last case, it would return: { :featured => true, :by_degree => "phd" }.
|
42
42
|
|
43
|
-
|
43
|
+
<tt>has_scope</tt> support several options (:only, :except, :as, :if, :unless, :default
|
44
|
+
and :type), please check the documentation for a detailed description.
|
44
45
|
|
45
46
|
== Bugs and Feedback
|
46
47
|
|
data/Rakefile
CHANGED
@@ -27,7 +27,7 @@ begin
|
|
27
27
|
require 'jeweler'
|
28
28
|
Jeweler::Tasks.new do |s|
|
29
29
|
s.name = "has_scope"
|
30
|
-
s.version = "0.
|
30
|
+
s.version = "0.2"
|
31
31
|
s.summary = "Maps controller filters to your resource scopes"
|
32
32
|
s.email = "contact@plataformatec.com.br"
|
33
33
|
s.homepage = "http://github.com/plataformatec/has_scope"
|
data/lib/has_scope.rb
CHANGED
@@ -1,12 +1,18 @@
|
|
1
1
|
module HasScope
|
2
2
|
TRUE_VALUES = ["true", true, "1", 1]
|
3
3
|
|
4
|
+
ALLOWED_TYPES = {
|
5
|
+
:array => [ Array ],
|
6
|
+
:hash => [ Hash ],
|
7
|
+
:default => [ String, Numeric ]
|
8
|
+
}
|
9
|
+
|
4
10
|
def self.included(base)
|
5
11
|
base.class_eval do
|
6
12
|
extend ClassMethods
|
7
13
|
helper_method :current_scopes
|
8
14
|
|
9
|
-
|
15
|
+
class_inheritable_hash :scopes_configuration, :instance_writer => false
|
10
16
|
self.scopes_configuration ||= {}
|
11
17
|
end
|
12
18
|
end
|
@@ -16,8 +22,10 @@ module HasScope
|
|
16
22
|
#
|
17
23
|
# == Options
|
18
24
|
#
|
19
|
-
# * <tt>:
|
20
|
-
#
|
25
|
+
# * <tt>:type</tt> - Checks the type of the parameter sent. If set to :boolean
|
26
|
+
# it just calls the named scope, without any argument. By default,
|
27
|
+
# it does not allow hashes or arrays to be given, except if type
|
28
|
+
# :hash or :array are set.
|
21
29
|
#
|
22
30
|
# * <tt>:only</tt> - In which actions the scope is applied. By default is :all.
|
23
31
|
#
|
@@ -33,22 +41,24 @@ module HasScope
|
|
33
41
|
# if the scope should NOT apply.
|
34
42
|
#
|
35
43
|
# * <tt>:default</tt> - Default value for the scope. Whenever supplied the scope
|
36
|
-
# is always called.
|
44
|
+
# is always called.
|
37
45
|
#
|
38
46
|
def has_scope(*scopes)
|
39
47
|
options = scopes.extract_options!
|
40
48
|
options.symbolize_keys!
|
41
|
-
|
49
|
+
|
50
|
+
if options.delete(:boolean)
|
51
|
+
options[:type] ||= :boolean
|
52
|
+
ActiveSupport::Deprecation.warn(":boolean => true is deprecated, use :type => :boolean instead", caller)
|
53
|
+
end
|
54
|
+
options.assert_valid_keys(:type, :only, :except, :if, :unless, :default, :as)
|
55
|
+
|
56
|
+
options[:only] = Array(options[:only])
|
57
|
+
options[:except] = Array(options[:except])
|
42
58
|
|
43
59
|
scopes.each do |scope|
|
44
|
-
self.scopes_configuration[scope]
|
45
|
-
self.scopes_configuration[scope]
|
46
|
-
self.scopes_configuration[scope][:only] = Array(options[:only])
|
47
|
-
self.scopes_configuration[scope][:except] = Array(options[:except])
|
48
|
-
|
49
|
-
[:if, :unless, :boolean, :default].each do |opt|
|
50
|
-
self.scopes_configuration[scope][opt] = options[opt] if options.key?(opt)
|
51
|
-
end
|
60
|
+
self.scopes_configuration[scope] ||= { :as => scope, :type => :default }
|
61
|
+
self.scopes_configuration[scope].merge!(options)
|
52
62
|
end
|
53
63
|
end
|
54
64
|
end
|
@@ -66,7 +76,7 @@ module HasScope
|
|
66
76
|
# end
|
67
77
|
# end
|
68
78
|
#
|
69
|
-
def apply_scopes(
|
79
|
+
def apply_scopes(target)
|
70
80
|
self.scopes_configuration.each do |scope, options|
|
71
81
|
next unless apply_scope_to_action?(options)
|
72
82
|
key = options[:as]
|
@@ -78,17 +88,23 @@ module HasScope
|
|
78
88
|
value = value.call(self) if value.is_a?(Proc)
|
79
89
|
end
|
80
90
|
|
81
|
-
if call_scope
|
82
|
-
if options[:boolean]
|
83
|
-
target_object = target_object.send(scope) if current_scopes[key] = TRUE_VALUES.include?(value)
|
84
|
-
else
|
85
|
-
current_scopes[key] = value
|
86
|
-
target_object = target_object.send(scope, value)
|
87
|
-
end
|
88
|
-
end
|
91
|
+
target = apply_scope_by_type(options[:type], key, scope, value, target) if call_scope
|
89
92
|
end
|
90
93
|
|
91
|
-
|
94
|
+
target
|
95
|
+
end
|
96
|
+
|
97
|
+
# Apply the scope taking into account its type.
|
98
|
+
def apply_scope_by_type(type, key, scope, value, target) #:nodoc:
|
99
|
+
if type == :boolean
|
100
|
+
current_scopes[key] = TRUE_VALUES.include?(value)
|
101
|
+
current_scopes[key] ? target.send(scope) : target
|
102
|
+
elsif ALLOWED_TYPES[type].none?{ |klass| value.is_a?(klass) }
|
103
|
+
raise "Expected type :#{type} in params[:#{key}], got :#{value.class}"
|
104
|
+
else
|
105
|
+
current_scopes[key] = value
|
106
|
+
target.send(scope, value)
|
107
|
+
end
|
92
108
|
end
|
93
109
|
|
94
110
|
# Given an options with :only and :except arrays, check if the scope
|
data/test/has_scope_test.rb
CHANGED
@@ -5,10 +5,12 @@ end
|
|
5
5
|
|
6
6
|
class TreesController < ApplicationController
|
7
7
|
has_scope :color, :unless => :show_all_colors?
|
8
|
-
has_scope :only_tall, :
|
8
|
+
has_scope :only_tall, :type => :boolean, :only => :index, :if => :restrict_to_only_tall_trees?
|
9
9
|
has_scope :shadown_range, :default => 10, :except => [ :index, :show, :destroy, :new ]
|
10
10
|
has_scope :root_type, :as => :root
|
11
11
|
has_scope :calculate_height, :default => proc {|c| c.session[:height] || 20 }, :only => :new
|
12
|
+
has_scope :paginate, :type => :hash
|
13
|
+
has_scope :categories, :type => :array
|
12
14
|
|
13
15
|
def index
|
14
16
|
@trees = apply_scopes(Tree).all
|
@@ -107,6 +109,36 @@ class HasScopeTest < ActionController::TestCase
|
|
107
109
|
assert_equal({ :color => 'blue', :only_tall => true }, current_scopes)
|
108
110
|
end
|
109
111
|
|
112
|
+
def test_scope_of_type_hash
|
113
|
+
hash = { "page" => "1", "per_page" => "1" }
|
114
|
+
Tree.expects(:paginate).with(hash).returns(Tree)
|
115
|
+
Tree.expects(:all).returns([mock_tree])
|
116
|
+
get :index, :paginate => hash
|
117
|
+
assert_equal([mock_tree], assigns(:trees))
|
118
|
+
assert_equal({ :paginate => hash }, current_scopes)
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_scope_of_type_array
|
122
|
+
array = %w(book kitchen sport)
|
123
|
+
Tree.expects(:categories).with(array).returns(Tree)
|
124
|
+
Tree.expects(:all).returns([mock_tree])
|
125
|
+
get :index, :categories => array
|
126
|
+
assert_equal([mock_tree], assigns(:trees))
|
127
|
+
assert_equal({ :categories => array }, current_scopes)
|
128
|
+
end
|
129
|
+
|
130
|
+
def test_invalid_type_hash_for_default_type_scope
|
131
|
+
assert_raise RuntimeError do
|
132
|
+
get :index, :color => { :blue => :red }
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_invalid_type_string_for_hash_type_scope
|
137
|
+
assert_raise RuntimeError do
|
138
|
+
get :index, :paginate => "1"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
110
142
|
def test_scope_is_called_with_default_value
|
111
143
|
Tree.expects(:shadown_range).with(10).returns(Tree).in_sequence
|
112
144
|
Tree.expects(:find).with('42').returns(mock_tree).in_sequence
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: has_scope
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: "0.
|
4
|
+
version: "0.2"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- "Jos\xC3\xA9 Valim"
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-12-
|
12
|
+
date: 2009-12-22 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|