require 'rspec_ext'
require "#{__FILE__.dirname}/helper"

require "crystal/support/callbacks"

describe "Callbacks" do
  before :all do
    class BasicCallbacksSpec
      inherit Crystal::Callbacks

      set_callback :callback_name, :before, :before_callback
      set_callback :callback_name, :after, :after_callback
      set_callback :callback_name, :around, :around_callback

      protected
        def around_callback
          around_callback_called
          yield
        end
    end
  end
  
  it "basic" do
    o = BasicCallbacksSpec.new
    o.should_receive :before_callback
    o.should_receive :around_callback_called
    o.should_receive :after_callback
    o.run_callbacks(:callback_name){"result"}.should == "result"
  end

  it "blocks" do
    class BlockCallbacksSpec
      inherit Crystal::Callbacks
      
      set_callback(:callback_name, :before){|controller| controller.result << :before}
      set_callback :callback_name, :around do |controller, block|
        begin
          controller.result << :around_begin
          block.call
        ensure
          controller.result << :around_end
        end
      end
      set_callback(:callback_name, :after){|controller| controller.result << :after}      

      def result
        @result ||= []
      end
    end
    
    o = BlockCallbacksSpec.new
    o.run_callbacks(:callback_name){"result"}.should == "result"
    o.result.should == [:before, :around_begin, :after, :around_end]
  end

  it "inheritance" do
    class InheritedCallbacksSpec < BasicCallbacksSpec
      set_callback :callback_name, :before, :before_callback2
    end
    
    o = InheritedCallbacksSpec.new
    o.should_receive :before_callback
    o.should_receive :before_callback2
    o.should_receive :around_callback_called
    o.should_receive :after_callback
    o.run_callbacks(:callback_name){"result"}.should == "result"
  end
  
  it 'terminator' do
    class CallbackTerminatorSpec
      inherit Crystal::Callbacks

      set_callback :callback_name, :before, :before_callback, :terminator => false
      set_callback :callback_name, :before, :before_callback2      

      def method
        run_callbacks :callback_name do
          "result"
        end
      end

      protected
        def before_callback
          false 
        end
    end
    
    o = CallbackTerminatorSpec.new
    o.should_not_receive :before_callback2
    o.run_callbacks(:callback_name){"result"}.should_not == "result"
  end
  
  it 'conditions' do
    class CallbackConditionsSpec
      inherit Crystal::Callbacks

      set_callback :callback_name, :before, :before_callback, :only => :another_method
    end
    
    o = CallbackConditionsSpec.new
    o.should_not_receive :before_callback
    o.run_callbacks(:callback_name, :method => :method){"result"}.should == 'result'
    
    o = CallbackConditionsSpec.new
    o.should_receive :before_callback
    o.run_callbacks(:callback_name, :method => :another_method){"result"}.should == 'result'
  end
  
  it "if, unless conditions" do
    c = Crystal::Callbacks::AbstractCallback.new
    c.conditions = {:if => lambda{|target, inf| true}}
    c.run?(nil, {}).should be_true
    
    c.conditions = {:if => lambda{|target, inf| false}}
    c.run?(nil, {}).should be_false
    
    c.conditions = {:unless => lambda{|target, inf| true}}
    c.run?(nil, {}).should be_false
    
    c.conditions = {:unless => lambda{|target, inf| false}}
    c.run?(nil, {}).should be_true
  end
  
  it "only, except conditions" do
    c = Crystal::Callbacks::AbstractCallback.new
    c.conditions = {:only => :a}
    c.run?(nil, {:method => :a}).should be_true
    
    c.conditions = {:only => :b}
    c.run?(nil, {:method => :a}).should be_false
    
    c.conditions = {:except => :a}
    c.run?(nil, {:method => :a}).should be_false
    
    c.conditions = {:except => :b}
    c.run?(nil, {:method => :a}).should be_true
  end
  
  
  it "around callback should be able to change result value" do
    class ChangeResultValueSpec
      inherit Crystal::Callbacks
  
      set_callback :callback_name, :around, :around_callback
      
      def around_callback        
        yield
        'another result'
      end
    end
    
    o = ChangeResultValueSpec.new
    o.run_callbacks(:callback_name){"result"}.should == 'another result'
  end
end