has_scope 0.2 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -40,8 +40,56 @@ 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
- <tt>has_scope</tt> support several options (:only, :except, :as, :if, :unless, :default
44
- and :type), please check the documentation for a detailed description.
43
+ == Installation
44
+
45
+ HasScope is available as gem on Gemcutter, so just run the following:
46
+
47
+ sudo gem install has_scope
48
+
49
+ If you want it as plugin, just do:
50
+
51
+ script/plugin install git://github.com/plataformatec/has_scope.git
52
+
53
+ == Options
54
+
55
+ HasScope support several options:
56
+
57
+ * <tt>:type</tt> - Checks the type of the parameter sent. If set to :boolean
58
+ it just calls the named scope, without any argument. By default,
59
+ it does not allow hashes or arrays to be given, except if type
60
+ :hash or :array are set.
61
+
62
+ * <tt>:only</tt> - In which actions the scope is applied.
63
+
64
+ * <tt>:except</tt> - In which actions the scope is not applied.
65
+
66
+ * <tt>:as</tt> - The key in the params hash expected to find the scope.
67
+ Defaults to the scope name.
68
+
69
+ * <tt>:if</tt> - Specifies a method, proc or string to call to determine
70
+ if the scope should apply
71
+
72
+ * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
73
+ if the scope should NOT apply.
74
+
75
+ * <tt>:default</tt> - Default value for the scope. Whenever supplied the scope
76
+ is always called.
77
+
78
+ * <tt>:allow_blank</tt> - Blank values are not sent to scopes by default. Set to true to overwrite.
79
+
80
+ == Block usage
81
+
82
+ has_scope also accepts a block. The controller, current scope and value are yielded
83
+ to the block so the user can apply the scope on its own. This is useful in case we
84
+ need to manipulate the given value:
85
+
86
+ has_scope :category do |controller, scope, value|
87
+ value != "all" ? scope.by_category(value) : scope
88
+ end
89
+
90
+ has_scope :not_voted_by_me, :type => :boolean do |controller, scope|
91
+ scope.not_voted_by(controller.current_user.id)
92
+ end
45
93
 
46
94
  == Bugs and Feedback
47
95
 
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.2"
30
+ s.version = "0.3"
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/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'has_scope'
data/lib/has_scope.rb CHANGED
@@ -4,6 +4,7 @@ module HasScope
4
4
  ALLOWED_TYPES = {
5
5
  :array => [ Array ],
6
6
  :hash => [ Hash ],
7
+ :boolean => [ Object ],
7
8
  :default => [ String, Numeric ]
8
9
  }
9
10
 
@@ -43,7 +44,23 @@ module HasScope
43
44
  # * <tt>:default</tt> - Default value for the scope. Whenever supplied the scope
44
45
  # is always called.
45
46
  #
46
- def has_scope(*scopes)
47
+ # * <tt>:allow_blank</tt> - Blank values are not sent to scopes by default. Set to true to overwrite.
48
+ #
49
+ # == Block usage
50
+ #
51
+ # has_scope also accepts a block. The controller, current scope and value are yielded
52
+ # to the block so the user can apply the scope on its own. This is useful in case we
53
+ # need to manipulate the given value:
54
+ #
55
+ # has_scope :category do |controller, scope, value|
56
+ # value != "all" ? scope.by_category(value) : scope
57
+ # end
58
+ #
59
+ # has_scope :not_voted_by_me, :type => :boolean do |controller, scope|
60
+ # scope.not_voted_by(controller.current_user.id)
61
+ # end
62
+ #
63
+ def has_scope(*scopes, &block)
47
64
  options = scopes.extract_options!
48
65
  options.symbolize_keys!
49
66
 
@@ -51,13 +68,13 @@ module HasScope
51
68
  options[:type] ||= :boolean
52
69
  ActiveSupport::Deprecation.warn(":boolean => true is deprecated, use :type => :boolean instead", caller)
53
70
  end
54
- options.assert_valid_keys(:type, :only, :except, :if, :unless, :default, :as)
71
+ options.assert_valid_keys(:type, :only, :except, :if, :unless, :default, :as, :allow_blank)
55
72
 
56
73
  options[:only] = Array(options[:only])
57
74
  options[:except] = Array(options[:except])
58
75
 
59
76
  scopes.each do |scope|
60
- self.scopes_configuration[scope] ||= { :as => scope, :type => :default }
77
+ self.scopes_configuration[scope] ||= { :as => scope, :type => :default, :block => block }
61
78
  self.scopes_configuration[scope].merge!(options)
62
79
  end
63
80
  end
@@ -88,22 +105,34 @@ module HasScope
88
105
  value = value.call(self) if value.is_a?(Proc)
89
106
  end
90
107
 
91
- target = apply_scope_by_type(options[:type], key, scope, value, target) if call_scope
108
+ if call_scope && (value.present? || options[:allow_blank])
109
+ set_current_scope(options[:type], key, value)
110
+ target = apply_scope_by_type(options[:type], scope, target, current_scopes[key], options[:block])
111
+ end
92
112
  end
93
113
 
94
114
  target
95
115
  end
96
116
 
97
- # Apply the scope taking into account its type.
98
- def apply_scope_by_type(type, key, scope, value, target) #:nodoc:
117
+ # Set the real value for the current scope if type check.
118
+ def set_current_scope(type, key, value) #:nodoc:
99
119
  if type == :boolean
100
120
  current_scopes[key] = TRUE_VALUES.include?(value)
101
- current_scopes[key] ? target.send(scope) : target
102
121
  elsif ALLOWED_TYPES[type].none?{ |klass| value.is_a?(klass) }
103
122
  raise "Expected type :#{type} in params[:#{key}], got :#{value.class}"
104
123
  else
105
124
  current_scopes[key] = value
106
- target.send(scope, value)
125
+ end
126
+ end
127
+
128
+ # Apply the scope taking into account its type.
129
+ def apply_scope_by_type(type, scope, target, value, block) #:nodoc:
130
+ return target if type == :boolean && value == false
131
+
132
+ if type == :boolean
133
+ block ? block.call(self, target) : target.send(scope)
134
+ else
135
+ block ? block.call(self, target, value) : target.send(scope, value)
107
136
  end
108
137
  end
109
138
 
@@ -140,4 +169,4 @@ module HasScope
140
169
  end
141
170
  end
142
171
 
143
- ApplicationController.send :include, HasScope
172
+ ActionController::Base.send :include, HasScope
@@ -6,12 +6,20 @@ end
6
6
  class TreesController < ApplicationController
7
7
  has_scope :color, :unless => :show_all_colors?
8
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, :destroy, :new ]
10
- has_scope :root_type, :as => :root
9
+ has_scope :shadown_range, :default => 10, :except => [ :index, :show, :new ]
10
+ has_scope :root_type, :as => :root, :allow_blank => true
11
11
  has_scope :calculate_height, :default => proc {|c| c.session[:height] || 20 }, :only => :new
12
12
  has_scope :paginate, :type => :hash
13
13
  has_scope :categories, :type => :array
14
14
 
15
+ has_scope :only_short, :type => :boolean do |controller, scope|
16
+ scope.only_really_short!(controller.object_id)
17
+ end
18
+
19
+ has_scope :by_category do |controller, scope, value|
20
+ scope.by_given_category(controller.object_id, value + "_id")
21
+ end
22
+
15
23
  def index
16
24
  @trees = apply_scopes(Tree).all
17
25
  end
@@ -50,7 +58,7 @@ class HasScopeTest < ActionController::TestCase
50
58
  assert_equal({ :only_tall => true }, current_scopes)
51
59
  end
52
60
 
53
- def test_boolean_scope_is_called_when_boolean_param_is_false
61
+ def test_boolean_scope_is_not_called_when_boolean_param_is_false
54
62
  Tree.expects(:only_tall).never
55
63
  Tree.expects(:all).returns([mock_tree])
56
64
  get :index, :only_tall => 'false'
@@ -100,6 +108,22 @@ class HasScopeTest < ActionController::TestCase
100
108
  assert_equal({ :color => 'blue' }, current_scopes)
101
109
  end
102
110
 
111
+ def test_scope_is_not_called_if_blank
112
+ Tree.expects(:color).never
113
+ Tree.expects(:all).returns([mock_tree]).in_sequence
114
+ get :index, :color => ''
115
+ assert_equal([mock_tree], assigns(:trees))
116
+ assert_equal({ }, current_scopes)
117
+ end
118
+
119
+ def test_scope_is_called_when_blank_if_allow_blank_is_given
120
+ Tree.expects(:root_type).with('').returns(Tree)
121
+ Tree.expects(:all).returns([mock_tree]).in_sequence
122
+ get :index, :root => ''
123
+ assert_equal([mock_tree], assigns(:trees))
124
+ assert_equal({ :root => '' }, current_scopes)
125
+ end
126
+
103
127
  def test_multiple_scopes_are_called
104
128
  Tree.expects(:only_tall).with().returns(Tree)
105
129
  Tree.expects(:color).with('blue').returns(Tree)
@@ -172,6 +196,22 @@ class HasScopeTest < ActionController::TestCase
172
196
  assert_equal({ :calculate_height => 100 }, current_scopes)
173
197
  end
174
198
 
199
+ def test_scope_with_boolean_block
200
+ Tree.expects(:only_really_short!).with(@controller.object_id).returns(Tree)
201
+ Tree.expects(:all).returns([mock_tree])
202
+ get :index, :only_short => 'true'
203
+ assert_equal([mock_tree], assigns(:trees))
204
+ assert_equal({ :only_short => true }, current_scopes)
205
+ end
206
+
207
+ def test_scope_with_other_block_types
208
+ Tree.expects(:by_given_category).with(@controller.object_id, 'for_id').returns(Tree)
209
+ Tree.expects(:all).returns([mock_tree])
210
+ get :index, :by_category => 'for'
211
+ assert_equal([mock_tree], assigns(:trees))
212
+ assert_equal({ :by_category => 'for' }, current_scopes)
213
+ end
214
+
175
215
  protected
176
216
 
177
217
  def mock_tree(stubs={})
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.2"
4
+ version: "0.3"
5
5
  platform: ruby
6
6
  authors:
7
7
  - "Jos\xC3\xA9 Valim"
@@ -22,10 +22,10 @@ extensions: []
22
22
  extra_rdoc_files:
23
23
  - README.rdoc
24
24
  files:
25
- - CHANGELOG.rdoc
26
25
  - MIT-LICENSE
27
26
  - README.rdoc
28
27
  - Rakefile
28
+ - init.rb
29
29
  - lib/has_scope.rb
30
30
  has_rdoc: true
31
31
  homepage: http://github.com/plataformatec/has_scope
data/CHANGELOG.rdoc DELETED
File without changes