module Spec
  module Rails
    module Matchers
    
      class RenderTemplate #:nodoc:
    
        def initialize(expected, controller)
          @controller = controller
          @expected = expected
        end
      
        def matches?(response_or_controller)
          response  = response_or_controller.respond_to?(:response) ?
                      response_or_controller.response :
                      response_or_controller

          if response.respond_to?(:redirect?) && response.redirect?
            @redirect_url = response.redirect_url
          elsif response.respond_to?(:rendered_file)
            @actual = response.rendered_file
          elsif response.respond_to?(:rendered)
            case template = response.rendered[:template]
            when nil
              unless response.rendered[:partials].empty?
                @actual = path_and_file(response.rendered[:partials].keys.first).join("/_")
              end
            when ActionView::Template
              @actual = template.path
            when String
              @actual = template
            end
          else
            @actual = response.rendered_template.to_s
          end
          return false if @actual.blank?
          given_controller_path, given_file = path_and_file(@actual)
          expected_controller_path, expected_file = path_and_file(@expected)
          given_controller_path == expected_controller_path && match_files(given_file, expected_file)
        end
        
        def match_files(actual, expected)
          actual_parts = actual.split('.')
          expected_parts = expected.split('.')
          expected_parts.each_with_index do |expected_part, index|
            return false unless expected_part == actual_parts[index]
          end
          true
        end
        
        def failure_message_for_should
          if @redirect_url
            "expected #{@expected.inspect}, got redirected to #{@redirect_url.inspect}"
          else
            "expected #{@expected.inspect}, got #{@actual.inspect}"
          end
        end
        
        def failure_message_for_should_not
          "expected not to render #{@expected.inspect}, but did"
        end
        
        def description
          "render template #{@expected.inspect}"
        end
      
        private
          def path_and_file(path)
            parts = path.split('/')
            file = parts.pop
            controller = parts.empty? ? current_controller_path : parts.join('/')
            return controller, file
          end
        
          def controller_path_from(path)
            parts = path.split('/')
            parts.pop
            parts.join('/')
          end

          def current_controller_path
            @controller.class.to_s.underscore.gsub(/_controller$/,'')
          end
        
      end
      
      # :call-seq:
      #   response.should render_template(template)
      #   response.should_not render_template(template)
      #
      # For use in controller code examples (integration or isolation mode).
      #
      # Passes if the specified template (view file) is rendered by the
      # response. This file can be any view file, including a partial. However
      # if it is a partial it must be rendered directly i.e. you can't detect
      # that a partial has been rendered as part of a view using
      # render_template. For that you should use a message expectation
      # (mock) instead:
      #
      #   controller.should_receive(:render).with(:partial => 'path/to/partial')
      #
      # <code>template</code> can include the controller path. It can also
      # include an optional extension, which you only need to use when there
      # is ambiguity.
      #
      # Note that partials must be spelled with the preceding underscore.
      #
      # == Examples
      #
      #   response.should render_template('list')
      #   response.should render_template('same_controller/list')
      #   response.should render_template('other_controller/list')
      #
      #   # with extensions
      #   response.should render_template('list.rjs')
      #   response.should render_template('list.haml')
      #   response.should render_template('same_controller/list.rjs')
      #   response.should render_template('other_controller/list.rjs')
      #
      #   # partials
      #   response.should render_template('_a_partial')
      #   response.should render_template('same_controller/_a_partial')
      #   response.should render_template('other_controller/_a_partial')
      def render_template(path)
        RenderTemplate.new(path.to_s, @controller)
      end

    end
  end
end
