class-action 0.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +33 -16
- data/.travis.yml +6 -0
- data/CHANGELOG.md +31 -0
- data/Gemfile.lock +62 -0
- data/README.md +42 -11
- data/class-action.gemspec +1 -0
- data/lib/class-action/rspec.rb +2 -0
- data/lib/class_action/action.rb +113 -63
- data/lib/class_action/rspec/class_action_example_group.rb +57 -0
- data/lib/class_action/rspec/have_class_action_matcher.rb +67 -0
- data/lib/class_action/rspec/respond_to_format_matcher.rb +72 -0
- data/lib/class_action/rspec/respond_with_matcher.rb +60 -0
- data/lib/class_action/rspec.rb +3 -0
- data/lib/class_action/version.rb +1 -1
- data/lib/class_action.rb +12 -8
- data/spec/class_action/action_spec.rb +202 -29
- data/spec/class_action/rspec/have_class_action_matcher_spec.rb +69 -0
- data/spec/class_action/rspec/respond_to_format_matcher_spec.rb +76 -0
- data/spec/class_action/rspec/respond_with_matcher_spec.rb +84 -0
- data/spec/class_action_spec.rb +8 -1
- data/spec/spec_helper.rb +6 -0
- metadata +32 -7
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/ClassAction.sublime-project +0 -10
- data/ClassAction.sublime-workspace +0 -1873
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2d7c4f6566ad04e2d10d0cfcc3e107302afb367d
|
4
|
+
data.tar.gz: 1fc331ef010a59d7723ca3faa206dd72b4322b17
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2ae15f473e25cdf638d5aaa51daf9239f61d4ee157544ce55d5184019405795584a8538eb693d0377730117dca662de16fcfdd96e3cbb96e3b3ebcc7c434f83b
|
7
|
+
data.tar.gz: 16c0a85b1a3798f913171cecc68830cb5f046d6689a33ddeb316458b85d9168ea019d174fcc2049c6a6358ff3684b722f2c398824d6b4391d59cd9014e6af00b
|
data/.gitignore
CHANGED
@@ -1,17 +1,34 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
2
|
+
#
|
3
|
+
# If you find yourself ignoring temporary files generated by your text editor
|
4
|
+
# or operating system, you probably want to add a global ignore instead:
|
5
|
+
# git config --global core.excludesfile ~/.gitignore_global
|
6
|
+
|
7
|
+
# Ignore .DS_Store
|
8
|
+
.DS_Store
|
9
|
+
|
10
|
+
# Ignore the output files.
|
11
|
+
/pkg
|
12
|
+
|
13
|
+
# Ignore bundler and database config and precompiled assets
|
14
|
+
/.bundle
|
15
|
+
/.env
|
16
|
+
|
17
|
+
# RVM files
|
18
|
+
.rvmrc
|
19
|
+
.ruby-version
|
20
|
+
.ruby-gemset
|
21
|
+
|
22
|
+
# Ignore all logfiles and tempfiles.
|
23
|
+
/tmp
|
24
|
+
|
25
|
+
# Ignore TextMate projects
|
26
|
+
*.tmproj
|
27
|
+
*.sublime-project
|
28
|
+
*.sublime-workspace
|
29
|
+
|
30
|
+
# Documentation files and other stuff
|
5
31
|
.yardoc
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
coverage
|
10
|
-
doc/
|
11
|
-
lib/bundler/man
|
12
|
-
pkg
|
13
|
-
rdoc
|
14
|
-
spec/reports
|
15
|
-
test/tmp
|
16
|
-
test/version_tmp
|
17
|
-
tmp
|
32
|
+
/doc
|
33
|
+
/nbproject
|
34
|
+
/coverage
|
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
## 1.1.0 ##
|
2
|
+
|
3
|
+
* Early class action resolution
|
4
|
+
|
5
|
+
- Allow access to `class_action` method from before-filters.
|
6
|
+
|
7
|
+
* Add helper methods onto controller instance as well
|
8
|
+
|
9
|
+
## 1.0.0.rc1 ##
|
10
|
+
|
11
|
+
* Automatic controller method inferral
|
12
|
+
|
13
|
+
- No need to write `controller_method`
|
14
|
+
|
15
|
+
* Instance variable references allowed in `respond_with`
|
16
|
+
|
17
|
+
## 0.0.2 ##
|
18
|
+
|
19
|
+
* Extension of responses
|
20
|
+
|
21
|
+
- Differentiation of responses for XHR requests
|
22
|
+
- Differentiation of responses based on state
|
23
|
+
|
24
|
+
## 0.0.1 ##
|
25
|
+
|
26
|
+
* Initial development
|
27
|
+
|
28
|
+
- Support for execution
|
29
|
+
- Rudimentary response support
|
30
|
+
|
31
|
+
*Joost Lubach*
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
class-action (1.1.0)
|
5
|
+
activesupport (~> 3.2)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
actionpack (3.2.14)
|
11
|
+
activemodel (= 3.2.14)
|
12
|
+
activesupport (= 3.2.14)
|
13
|
+
builder (~> 3.0.0)
|
14
|
+
erubis (~> 2.7.0)
|
15
|
+
journey (~> 1.0.4)
|
16
|
+
rack (~> 1.4.5)
|
17
|
+
rack-cache (~> 1.2)
|
18
|
+
rack-test (~> 0.6.1)
|
19
|
+
sprockets (~> 2.2.1)
|
20
|
+
activemodel (3.2.14)
|
21
|
+
activesupport (= 3.2.14)
|
22
|
+
builder (~> 3.0.0)
|
23
|
+
activesupport (3.2.14)
|
24
|
+
i18n (~> 0.6, >= 0.6.4)
|
25
|
+
multi_json (~> 1.0)
|
26
|
+
builder (3.0.4)
|
27
|
+
diff-lcs (1.2.5)
|
28
|
+
erubis (2.7.0)
|
29
|
+
hike (1.2.3)
|
30
|
+
i18n (0.6.9)
|
31
|
+
journey (1.0.4)
|
32
|
+
multi_json (1.8.2)
|
33
|
+
rack (1.4.5)
|
34
|
+
rack-cache (1.2)
|
35
|
+
rack (>= 0.4)
|
36
|
+
rack-test (0.6.2)
|
37
|
+
rack (>= 1.0)
|
38
|
+
rake (10.1.0)
|
39
|
+
rspec (2.14.1)
|
40
|
+
rspec-core (~> 2.14.0)
|
41
|
+
rspec-expectations (~> 2.14.0)
|
42
|
+
rspec-mocks (~> 2.14.0)
|
43
|
+
rspec-core (2.14.7)
|
44
|
+
rspec-expectations (2.14.4)
|
45
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
46
|
+
rspec-mocks (2.14.4)
|
47
|
+
sprockets (2.2.2)
|
48
|
+
hike (~> 1.2)
|
49
|
+
multi_json (~> 1.0)
|
50
|
+
rack (~> 1.0)
|
51
|
+
tilt (~> 1.1, != 1.3.0)
|
52
|
+
tilt (1.4.1)
|
53
|
+
|
54
|
+
PLATFORMS
|
55
|
+
ruby
|
56
|
+
|
57
|
+
DEPENDENCIES
|
58
|
+
actionpack (~> 3.2)
|
59
|
+
bundler (~> 1.3)
|
60
|
+
class-action!
|
61
|
+
rake
|
62
|
+
rspec (~> 2.14)
|
data/README.md
CHANGED
@@ -39,16 +39,11 @@ In your controller, make sure you have included `ClassAction`, and declare which
|
|
39
39
|
|
40
40
|
Then, create your `show` action class (the default is to name this class `PostsController::Show`, but you may customize this).
|
41
41
|
|
42
|
-
All *public* methods are executed in order when the action is run. Any support methods you need, you will need to make protected.
|
43
|
-
|
44
|
-
Some default controller methods (`params`, `request`, `render`, `redirect_to`, `respond_to` and `respond_with`) are available at all times.
|
42
|
+
All *public* methods are executed in order when the action is run. Any support methods you need, you will need to make protected. Also, all controller methods are available in the action.
|
45
43
|
|
46
44
|
class PostController
|
47
45
|
class Show < ClassAction::Action
|
48
46
|
|
49
|
-
# We need this method from the controller.
|
50
|
-
controller_method :current_user
|
51
|
-
|
52
47
|
def prepare
|
53
48
|
load_post
|
54
49
|
end
|
@@ -124,9 +119,7 @@ This employs the use of `ActionController#respond_to`. Additionally, there is su
|
|
124
119
|
|
125
120
|
class Show < ClassAction::Action
|
126
121
|
|
127
|
-
|
128
|
-
|
129
|
-
respond_with :post
|
122
|
+
respond_with :@post
|
130
123
|
respond_to :html, :json
|
131
124
|
|
132
125
|
respond_to :text do
|
@@ -142,7 +135,7 @@ is roughly equivalent to:
|
|
142
135
|
respond_to :html, :json, :text, :only => [ :show ]
|
143
136
|
|
144
137
|
def show
|
145
|
-
respond_with post do |format|
|
138
|
+
respond_with @post do |format|
|
146
139
|
format.text do
|
147
140
|
render :text => @post.to_yaml
|
148
141
|
end
|
@@ -151,14 +144,52 @@ is roughly equivalent to:
|
|
151
144
|
|
152
145
|
end
|
153
146
|
|
147
|
+
Note that the value you pass to `respond_with` may be a simple symbol (e.g. `:post`) for a method or a reference to an instance variable (e.g. `:@post`).
|
148
|
+
|
154
149
|
In other words, using `respond_with` in conjunction with `respond_to` allows you to:
|
155
150
|
|
156
|
-
1. Specify which method to use to obtain the response object (the first argument to `ActionController#respond_with`). Note that this method must exist on the action
|
151
|
+
1. Specify which method to use to obtain the response object (the first argument to `ActionController#respond_with`). Note that this method must exist on the action or controller.
|
157
152
|
2. Specify the formats that this action responds to. `ClassAction` will make sure that the controller mime types are modified accordingly.
|
158
153
|
3. Create a custom responder block in one breath.
|
159
154
|
|
160
155
|
The only caveat is that you have to specify all your controller-level `respond_to` declarations *before* defining your actions using `class_action`, or you might override the `respond_to` array of your controller.
|
161
156
|
|
157
|
+
### State based responses
|
158
|
+
|
159
|
+
In some cases you may want a certain response method (`respond_with`) or responder block (`respond_to`) to be only available in a certain case. For example, in some update action, you may want a different response based on whether the object was saved successfully.
|
160
|
+
|
161
|
+
Limiting a response method or reponder block this way is possible through the `on:` option in the methods `respond_with` and `respond_to`. The value of this option should correspond to a question-mark method on your action (or controller).
|
162
|
+
|
163
|
+
For example:
|
164
|
+
|
165
|
+
class Update < ClassAction::Action
|
166
|
+
|
167
|
+
respond_with :@post
|
168
|
+
respond_to :html, on: :failure do
|
169
|
+
render :edit, :status => :unprocessable_entity
|
170
|
+
end
|
171
|
+
|
172
|
+
protected
|
173
|
+
|
174
|
+
def success?
|
175
|
+
@post.errors.blank?
|
176
|
+
end
|
177
|
+
def failure?
|
178
|
+
@post.errors.present?
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
|
183
|
+
This will effectively perform the following response logic on the controller:
|
184
|
+
|
185
|
+
if @post.errors.blank?
|
186
|
+
respond_with @post
|
187
|
+
elsif @post.errors.present?
|
188
|
+
respond_with @post do |format|
|
189
|
+
format.html { render :edit, :status => :unprocessable_entity }
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
162
193
|
## Contributing
|
163
194
|
|
164
195
|
1. Fork it
|
data/class-action.gemspec
CHANGED
@@ -23,5 +23,6 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.add_development_dependency "bundler", "~> 1.3"
|
24
24
|
spec.add_development_dependency "rake"
|
25
25
|
spec.add_development_dependency "rspec", "~> 2.14"
|
26
|
+
spec.add_development_dependency "simplecov", "~> 2.14"
|
26
27
|
spec.add_development_dependency "actionpack", "~> 3.2"
|
27
28
|
end
|
data/lib/class_action/action.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'active_support/core_ext/object/try'
|
2
|
+
|
1
3
|
module ClassAction
|
2
4
|
|
3
5
|
# Base class for controller actions.
|
@@ -8,12 +10,15 @@ module ClassAction
|
|
8
10
|
|
9
11
|
def initialize(controller)
|
10
12
|
@_controller = controller
|
13
|
+
@_controller.singleton_class.send :include, self.class.helpers
|
11
14
|
end
|
12
15
|
|
13
16
|
######
|
14
17
|
# Attributes
|
15
18
|
|
16
|
-
|
19
|
+
def controller
|
20
|
+
@_controller
|
21
|
+
end
|
17
22
|
|
18
23
|
def available?
|
19
24
|
true
|
@@ -26,68 +31,55 @@ module ClassAction
|
|
26
31
|
class << self
|
27
32
|
|
28
33
|
# Exposes the given controller methods into the action.
|
29
|
-
def
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
controller.send :#{method}, *args, &block
|
40
|
-
ensure
|
41
|
-
#{assigns_copy_from}
|
42
|
-
end
|
43
|
-
protected :#{method}
|
44
|
-
RUBY
|
45
|
-
end
|
34
|
+
def _controller_method(method)
|
35
|
+
class_eval <<-RUBY, __FILE__, __LINE__+1
|
36
|
+
def #{method}(*args, &block)
|
37
|
+
copy_assigns_to_controller
|
38
|
+
controller.send :#{method}, *args, &block
|
39
|
+
ensure
|
40
|
+
copy_assigns_from_controller
|
41
|
+
end
|
42
|
+
protected :#{method}
|
43
|
+
RUBY
|
46
44
|
end
|
47
45
|
|
48
|
-
|
49
|
-
methods = public_instance_methods
|
50
|
-
methods -= [ :_execute ]
|
51
|
-
methods -= Object.public_instance_methods
|
52
|
-
methods
|
53
|
-
end
|
46
|
+
end
|
54
47
|
|
48
|
+
def respond_to?(method, include_private = false)
|
49
|
+
super || (include_private && controller.respond_to?(method, true))
|
55
50
|
end
|
56
51
|
|
57
|
-
|
58
|
-
|
52
|
+
def method_missing(method, *args, &block)
|
53
|
+
if controller.respond_to?(method, true)
|
54
|
+
self.class._controller_method method
|
55
|
+
send method, *args, &block
|
56
|
+
else
|
57
|
+
super
|
58
|
+
end
|
59
|
+
end
|
60
|
+
private :method_missing
|
59
61
|
|
60
62
|
######
|
61
|
-
#
|
63
|
+
# Execution
|
62
64
|
|
63
65
|
class << self
|
64
66
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
def helper_method(*methods)
|
72
|
-
methods.each do |method|
|
73
|
-
helpers.class_eval <<-RUBY, __FILE__, __LINE__+1
|
74
|
-
def #{method}(*args, &block)
|
75
|
-
controller.class_action.send(:#{method}, *args, &block)
|
76
|
-
end
|
77
|
-
RUBY
|
78
|
-
end
|
67
|
+
def _action_methods
|
68
|
+
methods = public_instance_methods
|
69
|
+
methods -= [ :_execute ]
|
70
|
+
methods -= Object.public_instance_methods
|
71
|
+
methods
|
79
72
|
end
|
80
73
|
|
81
74
|
end
|
82
75
|
|
83
|
-
######
|
84
|
-
# Execution
|
85
|
-
|
86
76
|
def _execute
|
87
77
|
raise ActionNotAvailable unless available?
|
88
78
|
|
89
79
|
# Execute the action by running all public methods in order.
|
90
|
-
self.class.
|
80
|
+
self.class._action_methods.each do |method|
|
81
|
+
next if self.method(method).arity != 0
|
82
|
+
|
91
83
|
send method
|
92
84
|
|
93
85
|
# Break execution of the action when some response body is set.
|
@@ -104,8 +96,17 @@ module ClassAction
|
|
104
96
|
def _respond
|
105
97
|
copy_assigns_to_controller
|
106
98
|
|
107
|
-
|
108
|
-
|
99
|
+
response = self.class._responses.find do |on, response|
|
100
|
+
!on || send(:"#{on}?")
|
101
|
+
end.try(:last)
|
102
|
+
|
103
|
+
if response
|
104
|
+
response_object = if response =~ /^@/
|
105
|
+
instance_variable_get(response)
|
106
|
+
else
|
107
|
+
send(response)
|
108
|
+
end
|
109
|
+
|
109
110
|
controller.respond_with response_object, &_respond_block
|
110
111
|
elsif _respond_block
|
111
112
|
controller.respond_to &_respond_block
|
@@ -113,46 +114,95 @@ module ClassAction
|
|
113
114
|
end
|
114
115
|
|
115
116
|
def _respond_block
|
116
|
-
responders =
|
117
|
-
|
117
|
+
responders = {}
|
118
|
+
self.class._responders.each do |(format, on), block|
|
119
|
+
# Select only those responders that have a block, and for which no precondition is set, or
|
120
|
+
# one that matches the current action state.
|
121
|
+
responders[format] ||= block if block && (!on || send(:"#{on}?"))
|
122
|
+
end
|
123
|
+
return if responders.empty?
|
118
124
|
|
119
125
|
action = self
|
120
126
|
proc do |collector|
|
121
127
|
responders.each do |format, block|
|
122
|
-
next unless block
|
123
128
|
collector.send(format) do
|
124
129
|
action.instance_exec &block
|
130
|
+
copy_assigns_to_controller
|
125
131
|
end
|
126
132
|
end
|
127
133
|
end
|
128
134
|
end
|
129
135
|
|
136
|
+
class << self
|
130
137
|
|
131
|
-
|
132
|
-
|
138
|
+
######
|
139
|
+
# Helpers
|
133
140
|
|
134
|
-
|
141
|
+
attr_accessor :helpers
|
142
|
+
def helpers
|
143
|
+
@helpers ||= Module.new.tap do |helpers|
|
144
|
+
helpers.send :include, superclass.helpers if superclass.respond_to?(:helpers)
|
145
|
+
end
|
146
|
+
end
|
135
147
|
|
136
|
-
|
137
|
-
|
138
|
-
|
148
|
+
def helper_method(*methods)
|
149
|
+
methods.each do |method|
|
150
|
+
helpers.class_eval <<-RUBY, __FILE__, __LINE__+1
|
151
|
+
def #{method}(*args, &block)
|
152
|
+
controller = if respond_to?(:class_action)
|
153
|
+
self
|
154
|
+
else
|
155
|
+
self.controller
|
156
|
+
end
|
157
|
+
controller.class_action.send(:#{method}, *args, &block)
|
158
|
+
end
|
159
|
+
RUBY
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
######
|
164
|
+
# Responders
|
165
|
+
|
166
|
+
attr_reader :_responders, :_responses
|
167
|
+
|
168
|
+
def _responses
|
169
|
+
@_responses ||= {}.tap do |responses|
|
170
|
+
responses.reverse_merge! superclass._responses if superclass.respond_to?(:_responses)
|
171
|
+
end
|
172
|
+
|
173
|
+
# Keep the hash in such an order that the 'nil' condition is always *last*.
|
174
|
+
# { :ok => 1, nil => 2, :invalid => 3 } => { :ok => 1, :invalid => 3, nil => 2 }
|
175
|
+
@_responses = Hash[ *@_responses.sort_by { |on, _method| on.nil? ? 1 : 0 }.flatten ]
|
176
|
+
end
|
177
|
+
|
178
|
+
def _responders
|
179
|
+
@_responders ||= {}.tap do |responders|
|
180
|
+
responders.reverse_merge! superclass._responders if superclass.respond_to?(:_responders)
|
181
|
+
end
|
182
|
+
|
183
|
+
# Keep the hash in such an order that the 'nil' conditions are always *last*.
|
184
|
+
# { [ (:html, nil) => 1, (:json, nil) => 2, (:html, :ok) => 3 } => { (:html, :ok) => 3, (:html, nil) => 1, (:json, nil) => 2 }
|
185
|
+
@_responders = Hash[ *@_responders.sort_by { |(format, on), _method| on.nil? ? 1 : 0 }.inject([]) { |arr, (key, value)| arr << key << value } ]
|
139
186
|
end
|
140
187
|
|
141
|
-
|
142
|
-
|
188
|
+
# Defines a method that returns the response. Specify an optional precondition in the `on` parameter.
|
189
|
+
def respond_with(method, on: nil)
|
190
|
+
_responses[on.try(:to_sym)] = method
|
143
191
|
end
|
144
192
|
|
145
|
-
|
193
|
+
# Defines a response block for the given format(s). Specify an optional precondition in the `on` parameter.
|
194
|
+
def respond_to(*formats, on: nil, &block)
|
146
195
|
formats.each do |format|
|
147
|
-
|
196
|
+
_responders[ [format.to_sym, on.try(:to_sym)] ] = block
|
148
197
|
end
|
149
198
|
end
|
150
199
|
|
151
|
-
|
152
|
-
|
200
|
+
# Defines a response block for any remaining format. Specify an optional precondition in the `on` parameter.
|
201
|
+
def respond_to_any(on: nil, &block)
|
202
|
+
respond_to :any, on: on, &block
|
153
203
|
end
|
154
204
|
|
155
|
-
|
205
|
+
end
|
156
206
|
|
157
207
|
######
|
158
208
|
# Assigns
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module ClassAction
|
2
|
+
module RSpec
|
3
|
+
|
4
|
+
# Adds support for speccing Class Actions. Sets up the example as
|
5
|
+
module ClassActionExampleGroup
|
6
|
+
def self.included(target)
|
7
|
+
target.send :include, ::RSpec::Rails::ControllerExampleGroup
|
8
|
+
target.extend ClassMethods
|
9
|
+
target.send :include, InstanceMethods
|
10
|
+
|
11
|
+
target.class_eval do
|
12
|
+
# I don't know why ControllerExampleGroup overrides this.
|
13
|
+
metadata[:type] = :class_action
|
14
|
+
|
15
|
+
subject { action }
|
16
|
+
before do
|
17
|
+
# This is required for response testing, as we won't use
|
18
|
+
# ActionController::TestCase#process
|
19
|
+
@controller.instance_variable_set '@_response', @response
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module ClassMethods
|
25
|
+
def action_class
|
26
|
+
described_class
|
27
|
+
end
|
28
|
+
def controller_class
|
29
|
+
# Controller::Action => Controller
|
30
|
+
described_class.name.sub(/(.*)::.*$/, '\1').constantize
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module InstanceMethods
|
35
|
+
def action
|
36
|
+
@action ||= self.class.action_class.new(@controller)
|
37
|
+
end
|
38
|
+
|
39
|
+
def assigns(*)
|
40
|
+
action.send :copy_assigns_to_controller
|
41
|
+
super
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def assigns
|
46
|
+
@action.send :copy_assigns_to_controller
|
47
|
+
super
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
RSpec.configure do |c|
|
56
|
+
c.include ClassAction::RSpec::ClassActionExampleGroup, type: :class_action
|
57
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module ClassAction
|
2
|
+
module RSpec
|
3
|
+
|
4
|
+
class HaveClassActionMatcher
|
5
|
+
|
6
|
+
def initialize(action_name)
|
7
|
+
@action_name = action_name.to_s
|
8
|
+
end
|
9
|
+
|
10
|
+
def using_class(klass)
|
11
|
+
@klass = klass
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def matches?(controller)
|
16
|
+
@controller = controller
|
17
|
+
@reason = :unsupported and return false unless controller.respond_to?(:_class_action, true)
|
18
|
+
@reason = :not_an_action and return false unless controller.respond_to?(@action_name)
|
19
|
+
@reason = :not_a_class_action and return false unless controller.respond_to?(:"_#{@action_name}_action_class", true)
|
20
|
+
|
21
|
+
if @klass
|
22
|
+
@found_class = controller.send(:"_#{@action_name}_action_class").class
|
23
|
+
@reason = :incorrect_class and return false if @found_class != @klass
|
24
|
+
end
|
25
|
+
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
def description
|
30
|
+
if @klass
|
31
|
+
"have class action :#{@action_name} using class #{@klass}"
|
32
|
+
else
|
33
|
+
"have class action :#{@action_name}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def failure_message_for_should
|
38
|
+
case @reason
|
39
|
+
when :unsupported
|
40
|
+
"expected controller of class #{@controller.class} to have class action :#{@action_name}, but it does not support class actions"
|
41
|
+
when :incorrect_class
|
42
|
+
"expected action #{@controller.class}##{@action_name} to use class #{@klass}, but it used #{@found_class}"
|
43
|
+
when :not_a_class_action
|
44
|
+
"expected action #{@controller.class}##{@action_name} to be a class action"
|
45
|
+
else
|
46
|
+
"expected controller of class #{@controller.class} to have class action :#{@action_name}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def failure_message_for_should_not
|
51
|
+
if @klass
|
52
|
+
"expected #{@controller.class}##{@action_name} not to be a class action using class #{@klass}"
|
53
|
+
else
|
54
|
+
"expected #{@controller.class}##{@action_name} not to be a class action"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
RSpec::Matchers.module_eval do
|
64
|
+
def have_class_action(action_name)
|
65
|
+
ClassAction::RSpec::HaveClassActionMatcher.new(action_name)
|
66
|
+
end
|
67
|
+
end
|