has_scope 0.2 → 0.3

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