class-action 0.0.1 → 1.1.0
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.
- 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
|