require 'spec_helper' describe ClassAction::Action do let(:controller) { double(:controller, :view_assigns => {}, :response_body => nil) } let(:action_class) { Class.new(ClassAction::Action) } let(:action) { action_class.new(controller) } it "should by default be available" do expect(action).to be_available end describe '.helpers && .helper_method' do it "should create an empty module upon inheritance" do expect(action_class.helpers).to be_a(Module) end it "should define the helper method in the action's helpers module, which should call the method on the controller action" do action_class.class_eval do def helper1 'HELPER RESULT' end helper_method :helper1 end helpers = action_class.helpers klass = Class.new do include helpers attr_reader :controller def initialize(controller) @controller = controller end end obj = klass.new(controller) expect(obj).to respond_to(:helper1) expect(controller).to receive(:class_action).and_return(action) expect(obj.helper1).to eql('HELPER RESULT') end end describe '.controller_method' do before { allow(controller).to receive(:load_post) } before { action_class.class_eval { controller_method :load_post } } it "should create a protected method :load_post" do expect(action.protected_methods).to include(:load_post) end it "should create a proxy to the controller" do result = double(:result) expect(controller).to receive(:load_post).and_return(result) expect(action.send(:load_post)).to be(result) end it "should copy assigns to the controller before executing the controller method, and copy them back afterwards" do # Simulate an instance variable. var = 1 allow(controller).to receive(:view_assigns) do {'var' => var} end allow(controller).to receive(:instance_variable_set).with(:@var, an_instance_of(Fixnum)) do |_, num| var = num end expect(controller).to receive(:increase_var) do var += 1 end action_class.class_eval do controller_method :increase_var def execute @var = 2 increase_var end end # Even though it's set to 1 initially, it is set to 2 by copying # the assigns to the controller, and subsequently increased by 1 # to end up as 3 - both the controller and the action's versions. action._execute expect(var).to eql(3) expect(action.instance_variable_get('@var')).to eql(3) end end describe '.action_methods' do it "should include (in order) - only the public defined action methods in the action class" do action_class.class_eval do def method1; end def method2; end protected def method3; end private def method4; end end expect(action_class.action_methods).to eql([ :method1, :method2 ]) end end describe '#_execute' do it "should raise an exception if the action is not available" do expect(action).to receive(:available?).and_return(false) expect{ action._execute }.to raise_error(ClassAction::ActionNotAvailable) end it "should execute all action methods in the action, and call #copy_assigns_to_controller finally" do called = [] expect(action_class).to receive(:action_methods).and_return([:method1, :method2]) expect(action).to receive(:method1) { called << :method1 } expect(action).to receive(:method2) { called << :method2 } action._execute expect(called).to eql([:method1, :method2]) end it "should stop executing when a response body is set" do called = []; response_body = nil allow(controller).to receive(:response_body) { response_body } expect(action_class).to receive(:action_methods).and_return([:method1, :method2]) expect(action).to receive(:method1) { called << :method1; response_body = '' } expect(action).not_to receive(:method2) action._execute expect(called).to eql([:method1]) end it "should call _respond at the end" do called = [] expect(action_class).to receive(:action_methods).and_return([:method1, :method2]) expect(action).to receive(:method1) { called << :method1 } expect(action).to receive(:method2) { called << :method2 } expect(action).to receive(:_respond) { called << :_respond } action._execute expect(called).to eql([:method1, :method2, :_respond]) end it "should not call _respond if a response body is set" do allow(controller).to receive(:response_body).and_return('') expect(action).not_to receive(:_respond) action._execute end end describe '#_respond' do # Note - as _respond is a private method, we will call _execute to test # this method. _execute does not perform other actions if no public methods # are defined. it "should always copy assignment variables back to the controller" do action_class.class_eval do def set_ivar @my_var = :test end end expect(controller).to receive(:instance_variable_set).with(:@my_var, :test) action._execute end context "with no respond with or responders" do it "should not call a respond method, but copy all instance variables into the controller at the end" do expect(controller).not_to receive(:respond_with) expect(controller).not_to receive(:respond_to) action._execute end end context "having set a respond_with" do let(:response) { double(:response) } before do action_class.class_eval do respond_with :response end expect(action).to receive(:response).and_return(response) end it "should call the respond_with method and use it in the response" do expect(controller).to receive(:respond_with).with(response) do |&blk| expect(blk).to be_nil end action._execute end it "should use the _respond_block if it is set" do block = proc{} allow(action).to receive(:_respond_block).and_return(block) expect(controller).to receive(:respond_with).with(response) do |&blk| expect(blk).to be(block) end action._execute end end context "having set _respond_block" do it "should use the _respond_block" do block = proc{} allow(action).to receive(:_respond_block).and_return(block) expect(controller).to receive(:respond_to) do |&blk| expect(blk).to be(block) end action._execute end end end describe 'responders & _respond_block' do # Private method, but specced individually to make spec terser. let(:respond_block) { action.send(:_respond_block) } it "should create a block using the given responders, which is executed on the action" do called = nil; receiver = nil json_block = proc { receiver = self; called = :json } html_block = proc { receiver = self; called = :html } any_block = proc { receiver = self; called = :any } action_class.class_eval do respond_to :json, &json_block respond_to :html, &html_block respond_to_any &any_block end # Simulate ActionController's format collector. collector = Class.new{ attr_reader :json_block, :html_block, :any_block }.new def collector.json(&block) @json_block = block end def collector.html(&block) @html_block = block end def collector.any(&block) @any_block = block end respond_block.call collector collector.json_block.call expect(receiver).to be(action); expect(called).to be(:json) collector.html_block.call expect(receiver).to be(action); expect(called).to be(:html) collector.any_block.call expect(receiver).to be(action); expect(called).to be(:any) end end end