class-action 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,264 @@
1
+ require 'spec_helper'
2
+
3
+ describe ClassAction::Action do
4
+
5
+ let(:controller) { double(:controller, :view_assigns => {}, :response_body => nil) }
6
+ let(:action_class) { Class.new(ClassAction::Action) }
7
+ let(:action) { action_class.new(controller) }
8
+
9
+ it "should by default be available" do
10
+ expect(action).to be_available
11
+ end
12
+
13
+ describe '.helpers && .helper_method' do
14
+ it "should create an empty module upon inheritance" do
15
+ expect(action_class.helpers).to be_a(Module)
16
+ end
17
+
18
+ it "should define the helper method in the action's helpers module, which should call the method on the controller action" do
19
+ action_class.class_eval do
20
+ def helper1
21
+ 'HELPER RESULT'
22
+ end
23
+ helper_method :helper1
24
+ end
25
+
26
+ helpers = action_class.helpers
27
+ klass = Class.new do
28
+ include helpers
29
+ attr_reader :controller
30
+ def initialize(controller)
31
+ @controller = controller
32
+ end
33
+ end
34
+
35
+ obj = klass.new(controller)
36
+
37
+ expect(obj).to respond_to(:helper1)
38
+ expect(controller).to receive(:class_action).and_return(action)
39
+ expect(obj.helper1).to eql('HELPER RESULT')
40
+ end
41
+ end
42
+
43
+ describe '.controller_method' do
44
+ before { allow(controller).to receive(:load_post) }
45
+ before { action_class.class_eval { controller_method :load_post } }
46
+
47
+ it "should create a protected method :load_post" do
48
+ expect(action.protected_methods).to include(:load_post)
49
+ end
50
+
51
+ it "should create a proxy to the controller" do
52
+ result = double(:result)
53
+ expect(controller).to receive(:load_post).and_return(result)
54
+ expect(action.send(:load_post)).to be(result)
55
+ end
56
+
57
+ it "should copy assigns to the controller before executing the controller method, and copy them back afterwards" do
58
+ # Simulate an instance variable.
59
+ var = 1
60
+ allow(controller).to receive(:view_assigns) do
61
+ {'var' => var}
62
+ end
63
+ allow(controller).to receive(:instance_variable_set).with(:@var, an_instance_of(Fixnum)) do |_, num|
64
+ var = num
65
+ end
66
+ expect(controller).to receive(:increase_var) do
67
+ var += 1
68
+ end
69
+
70
+ action_class.class_eval do
71
+ controller_method :increase_var
72
+ def execute
73
+ @var = 2
74
+ increase_var
75
+ end
76
+ end
77
+
78
+ # Even though it's set to 1 initially, it is set to 2 by copying
79
+ # the assigns to the controller, and subsequently increased by 1
80
+ # to end up as 3 - both the controller and the action's versions.
81
+
82
+ action._execute
83
+ expect(var).to eql(3)
84
+ expect(action.instance_variable_get('@var')).to eql(3)
85
+ end
86
+ end
87
+
88
+ describe '.action_methods' do
89
+ it "should include (in order) - only the public defined action methods in the action class" do
90
+ action_class.class_eval do
91
+ def method1; end
92
+ def method2; end
93
+
94
+ protected
95
+ def method3; end
96
+
97
+ private
98
+ def method4; end
99
+ end
100
+
101
+ expect(action_class.action_methods).to eql([ :method1, :method2 ])
102
+ end
103
+ end
104
+
105
+ describe '#_execute' do
106
+ it "should raise an exception if the action is not available" do
107
+ expect(action).to receive(:available?).and_return(false)
108
+ expect{ action._execute }.to raise_error(ClassAction::ActionNotAvailable)
109
+ end
110
+
111
+ it "should execute all action methods in the action, and call #copy_assigns_to_controller finally" do
112
+ called = []
113
+
114
+ expect(action_class).to receive(:action_methods).and_return([:method1, :method2])
115
+ expect(action).to receive(:method1) { called << :method1 }
116
+ expect(action).to receive(:method2) { called << :method2 }
117
+
118
+ action._execute
119
+ expect(called).to eql([:method1, :method2])
120
+ end
121
+
122
+ it "should stop executing when a response body is set" do
123
+ called = []; response_body = nil
124
+
125
+ allow(controller).to receive(:response_body) { response_body }
126
+ expect(action_class).to receive(:action_methods).and_return([:method1, :method2])
127
+ expect(action).to receive(:method1) { called << :method1; response_body = '<html></html>' }
128
+ expect(action).not_to receive(:method2)
129
+
130
+ action._execute
131
+ expect(called).to eql([:method1])
132
+ end
133
+
134
+ it "should call _respond at the end" do
135
+ called = []
136
+
137
+ expect(action_class).to receive(:action_methods).and_return([:method1, :method2])
138
+ expect(action).to receive(:method1) { called << :method1 }
139
+ expect(action).to receive(:method2) { called << :method2 }
140
+ expect(action).to receive(:_respond) { called << :_respond }
141
+
142
+ action._execute
143
+ expect(called).to eql([:method1, :method2, :_respond])
144
+ end
145
+
146
+ it "should not call _respond if a response body is set" do
147
+ allow(controller).to receive(:response_body).and_return('<html></html>')
148
+ expect(action).not_to receive(:_respond)
149
+ action._execute
150
+ end
151
+
152
+ end
153
+
154
+ describe '#_respond' do
155
+
156
+ # Note - as _respond is a private method, we will call _execute to test
157
+ # this method. _execute does not perform other actions if no public methods
158
+ # are defined.
159
+
160
+ it "should always copy assignment variables back to the controller" do
161
+ action_class.class_eval do
162
+ def set_ivar
163
+ @my_var = :test
164
+ end
165
+ end
166
+
167
+ expect(controller).to receive(:instance_variable_set).with(:@my_var, :test)
168
+ action._execute
169
+ end
170
+
171
+ context "with no respond with or responders" do
172
+ it "should not call a respond method, but copy all instance variables into the controller at the end" do
173
+ expect(controller).not_to receive(:respond_with)
174
+ expect(controller).not_to receive(:respond_to)
175
+ action._execute
176
+ end
177
+ end
178
+
179
+ context "having set a respond_with" do
180
+ let(:response) { double(:response) }
181
+ before do
182
+ action_class.class_eval do
183
+ respond_with :response
184
+ end
185
+ expect(action).to receive(:response).and_return(response)
186
+ end
187
+
188
+ it "should call the respond_with method and use it in the response" do
189
+ expect(controller).to receive(:respond_with).with(response) do |&blk|
190
+ expect(blk).to be_nil
191
+ end
192
+
193
+ action._execute
194
+ end
195
+
196
+ it "should use the _respond_block if it is set" do
197
+ block = proc{}
198
+ allow(action).to receive(:_respond_block).and_return(block)
199
+
200
+ expect(controller).to receive(:respond_with).with(response) do |&blk|
201
+ expect(blk).to be(block)
202
+ end
203
+
204
+ action._execute
205
+ end
206
+
207
+ end
208
+
209
+ context "having set _respond_block" do
210
+
211
+ it "should use the _respond_block" do
212
+ block = proc{}
213
+ allow(action).to receive(:_respond_block).and_return(block)
214
+
215
+ expect(controller).to receive(:respond_to) do |&blk|
216
+ expect(blk).to be(block)
217
+ end
218
+
219
+ action._execute
220
+ end
221
+
222
+ end
223
+
224
+ end
225
+
226
+ describe 'responders & _respond_block' do
227
+
228
+ # Private method, but specced individually to make spec terser.
229
+
230
+ let(:respond_block) { action.send(:_respond_block) }
231
+
232
+ it "should create a block using the given responders, which is executed on the action" do
233
+ called = nil; receiver = nil
234
+ json_block = proc { receiver = self; called = :json }
235
+ html_block = proc { receiver = self; called = :html }
236
+ any_block = proc { receiver = self; called = :any }
237
+
238
+ action_class.class_eval do
239
+ respond_to :json, &json_block
240
+ respond_to :html, &html_block
241
+ respond_to_any &any_block
242
+ end
243
+
244
+ # Simulate ActionController's format collector.
245
+ collector = Class.new{ attr_reader :json_block, :html_block, :any_block }.new
246
+ def collector.json(&block) @json_block = block end
247
+ def collector.html(&block) @html_block = block end
248
+ def collector.any(&block) @any_block = block end
249
+
250
+ respond_block.call collector
251
+
252
+ collector.json_block.call
253
+ expect(receiver).to be(action); expect(called).to be(:json)
254
+
255
+ collector.html_block.call
256
+ expect(receiver).to be(action); expect(called).to be(:html)
257
+
258
+ collector.any_block.call
259
+ expect(receiver).to be(action); expect(called).to be(:any)
260
+ end
261
+
262
+ end
263
+
264
+ end
@@ -0,0 +1,185 @@
1
+ require 'spec_helper'
2
+
3
+ describe ClassAction do
4
+
5
+ let(:controller) { ClassActionTestController.new }
6
+
7
+ before do
8
+ Object.send :remove_const, :ClassActionTestController if defined?(ClassActionTestController)
9
+
10
+ class ::ClassActionTestController < ActionController::Base
11
+ include ClassAction
12
+
13
+ def self.logger
14
+ @logger ||= Logger.new(STDOUT)
15
+ end
16
+
17
+ class Show < ClassAction::Action
18
+ end
19
+
20
+ class OtherShow < ClassAction::Action
21
+ end
22
+ end
23
+ end
24
+
25
+ context "adding a class action :show" do
26
+
27
+ before { ClassActionTestController.class_eval { class_action :show } }
28
+
29
+ it "should respond to method :show" do
30
+ expect(controller).to respond_to(:show)
31
+ end
32
+
33
+ it "should add helper methods from the action class to the view context class" do
34
+ helpers = Module.new do
35
+ def method_added_by_class_action
36
+ end
37
+ end
38
+
39
+ action_class = double(:helpers => helpers)
40
+ allow(controller).to receive(:class_action).and_return(double(:class => action_class))
41
+ expect(controller.view_context).to respond_to(:method_added_by_class_action)
42
+ end
43
+
44
+ context "when executing the action" do
45
+ let(:action) { action = double(:action, :_execute => nil) }
46
+ before { expect(ClassActionTestController::Show).to receive(:new).with(controller).and_return(action) }
47
+
48
+ it "should try to instantiate TestController::Show and execute it" do
49
+ expect(action).to receive(:_execute)
50
+ controller.show
51
+ end
52
+
53
+ it "should store the created action in the controller" do
54
+ controller.show
55
+ expect(controller.class_action).to be(action)
56
+ end
57
+
58
+ it "should not appear in the view assigns" do
59
+ controller.show
60
+ expect(controller.view_assigns).not_to have_key('_class_action')
61
+ end
62
+ end
63
+
64
+ context "giving another action class" do
65
+ before do
66
+ ClassActionTestController.class_eval do
67
+ class_action :show, klass: ClassActionTestController::OtherShow
68
+ end
69
+ end
70
+
71
+ it "should try to instantiate the given action class when executed" do
72
+ action = ClassActionTestController::OtherShow.new(controller)
73
+ expect(ClassActionTestController::OtherShow).to receive(:new).with(controller).and_return(action)
74
+ controller.show
75
+
76
+ expect(controller.class_action).to be(action)
77
+ end
78
+ end
79
+
80
+ end
81
+
82
+ describe "respond mime injection" do
83
+
84
+ before do
85
+ ClassActionTestController::Show.class_eval do
86
+ respond_to :html
87
+ end
88
+ end
89
+
90
+ it "should create mimes with :only => 'show' for nonexisting mimes" do
91
+ ClassActionTestController.class_eval { class_action :show }
92
+ ClassActionTestController.mimes_for_respond_to.should eql(
93
+ :html => {:only => %w[show]}
94
+ )
95
+ end
96
+
97
+ it "should append the action 'show' to existing mimes with an :only restriction" do
98
+ ClassActionTestController.class_eval do
99
+ respond_to :html, :only => [ :index ]
100
+ class_action :show
101
+ end
102
+
103
+ ClassActionTestController.mimes_for_respond_to.should eql(
104
+ :html => {:only => %w[index show]}
105
+ )
106
+ end
107
+
108
+ it "should not append the action 'show' to existing mimes with no :only restriction" do
109
+ ClassActionTestController.class_eval do
110
+ respond_to :html
111
+ class_action :show
112
+ end
113
+
114
+ ClassActionTestController.mimes_for_respond_to.should eql(
115
+ :html => {}
116
+ )
117
+ end
118
+
119
+ it "should not append the action 'show' if it was already targeted" do
120
+ ClassActionTestController.class_eval do
121
+ respond_to :html, :only => [ :show ]
122
+ class_action :show
123
+ end
124
+
125
+ ClassActionTestController.mimes_for_respond_to.should eql(
126
+ :html => { :only => %w[show] }
127
+ )
128
+ end
129
+
130
+ it "should not append the action 'show' if it was also already to the :except list (but log a warning)" do
131
+ expect(ClassActionTestController.logger).to receive(:warn).with("Warning: action show (ClassAction) responds to `html` but it does not accept this mime type")
132
+
133
+ ClassActionTestController.class_eval do
134
+ respond_to :html, :except => [ :show ]
135
+ class_action :show
136
+ end
137
+
138
+ ClassActionTestController.mimes_for_respond_to.should eql(
139
+ :html => { :except => %w[show] }
140
+ )
141
+ end
142
+
143
+ it "should exclude the action from any other mime types that may be defined" do
144
+ ClassActionTestController.class_eval do
145
+ respond_to :json
146
+ class_action :show
147
+ end
148
+
149
+ ClassActionTestController.mimes_for_respond_to.should eql(
150
+ :html => { :only => %w[show] },
151
+ :json => { :except => %w[show] }
152
+ )
153
+ end
154
+
155
+ it "should leave everything alone if the class action has no responders" do
156
+ allow(ClassActionTestController::Show).to receive(:responders).and_return({})
157
+
158
+ ClassActionTestController.class_eval do
159
+ respond_to :json
160
+ class_action :show
161
+ end
162
+
163
+ ClassActionTestController.mimes_for_respond_to.should eql(
164
+ :json => {}
165
+ )
166
+ end
167
+
168
+ it "should leave everything alone if the class action has an 'any' responder" do
169
+ ClassActionTestController::Show.class_eval do
170
+ respond_to_any
171
+ end
172
+
173
+ ClassActionTestController.class_eval do
174
+ respond_to :json
175
+ class_action :show
176
+ end
177
+
178
+ ClassActionTestController.mimes_for_respond_to.should eql(
179
+ :json => {}
180
+ )
181
+ end
182
+
183
+ end
184
+
185
+ end
@@ -0,0 +1,15 @@
1
+ require 'action_controller'
2
+ require 'class-action'
3
+ require 'rspec/autorun'
4
+
5
+ RSpec.configure do |config|
6
+
7
+ config.mock_with :rspec do |config|
8
+ config.syntax = :expect
9
+ end
10
+
11
+ end
12
+
13
+ # Requires supporting ruby files with custom matchers and macros, etc,
14
+ # in spec/support/ and its subdirectories.
15
+ Dir[File.expand_path("../support/**/*.rb", __FILE__)].each {|f| require f}
metadata ADDED
@@ -0,0 +1,135 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: class-action
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Joost Lubach
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-09-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '3.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '3.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '2.14'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '2.14'
69
+ - !ruby/object:Gem::Dependency
70
+ name: actionpack
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '3.2'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '3.2'
83
+ description: Allows you to write controller actions as classes, rather than methods.
84
+ email:
85
+ - joost@yoazt.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - .gitignore
91
+ - .ruby-gemset
92
+ - .ruby-version
93
+ - ClassAction.sublime-project
94
+ - ClassAction.sublime-workspace
95
+ - Gemfile
96
+ - LICENSE.txt
97
+ - README.md
98
+ - Rakefile
99
+ - class-action.gemspec
100
+ - lib/class-action.rb
101
+ - lib/class_action.rb
102
+ - lib/class_action/action.rb
103
+ - lib/class_action/version.rb
104
+ - spec/class_action/action_spec.rb
105
+ - spec/class_action_spec.rb
106
+ - spec/spec_helper.rb
107
+ homepage: ''
108
+ licenses:
109
+ - MIT
110
+ metadata: {}
111
+ post_install_message:
112
+ rdoc_options: []
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - '>='
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ requirements: []
126
+ rubyforge_project:
127
+ rubygems_version: 2.0.3
128
+ signing_key:
129
+ specification_version: 4
130
+ summary: Allows you to write controller actions as classes, rather than methods.
131
+ test_files:
132
+ - spec/class_action/action_spec.rb
133
+ - spec/class_action_spec.rb
134
+ - spec/spec_helper.rb
135
+ has_rdoc: