lib/trusty_cms/resource_responses.rb



require 'ostruct'
module TrustyCms
  module ResourceResponses
    def self.extended(base)
      base.send :class_attribute, :responses
      base.send :include, InstanceMethods
    end

    def create_responses
      r = (self.responses ||= Collector.new)
      yield r if block_given?
      r
    end

    module InstanceMethods
      def response_for(action)
        responses = self.class.responses.send(action)
        respond_to do |wants|
          responses.each_format do |f, format_block|
            if format_block
              wants.send(f, &wrap(format_block))
            else
              wants.send(f)
            end
          end
          responses.each_published do |pub, pub_block|
            wants.send(pub, &wrap(pub_block))
          end
          if responses.default
            wants.any(&wrap(responses.default))
          else
            wants.any
          end
        end
      end

      def wrap(proc)
        # Makes sure our response blocks get evaluated in the right context
        lambda do
          # Ruby 1.9.2 yields self in instance_eval... see https://gist.github.com/479572
          # lambdas are as strict as methods in 1.9.x, making sure that the args match, Procs are not.
          if RUBY_VERSION =~ /^1\.9/ && proc.lambda? && (proc.arity != 1)
            raise "You can only pass a proc ('Proc.new') or a lambda that takes exactly one arg (for self) to the wrap method."
          end

          instance_eval(&proc)
        end
      end
    end

    class Collector < OpenStruct
      def initialize
        super
        @table = Hash.new { |h, k| h[k] = Response.new }
      end

      def initialize_copy(orig)
        super
        @table.keys.each do |key|
          @table[key] = orig.send(key).dup
        end
      end
    end

    class Response
      attr_reader :publish_formats, :publish_block, :blocks, :block_order

      def initialize
        @publish_formats = []
        @blocks = {}
        @block_order = []
      end

      def initialize_copy(orig)
        @publish_formats = orig.publish_formats.dup
        @blocks = orig.blocks.dup
        @block_order = orig.block_order.dup
        @publish_block = orig.publish_block.dup if orig.publish_block
        @default = orig.default.dup if orig.default
      end

      def default(&block)
        if block_given?
          @default = block
        end
        @default
      end

      def publish(*formats, &block)
        @publish_formats.concat(formats)
        if block_given?
          @publish_block = block
        else
          raise ArgumentError, 'Block required to publish' unless @publish_block
        end
      end

      def each_published
        publish_formats.each do |format|
          yield format, publish_block if block_given?
        end
      end

      def each_format
        @block_order.each do |format|
          yield format, @blocks[format] if block_given?
        end
      end

      def method_missing(method, *args, &block)
        if block_given?
          @blocks[method] = block
          @block_order << method unless @block_order.include?(method)
        elsif args.empty?
          @block_order << method
        else
          super
        end
      end
    end
  end
end