require 'addressable/template'

module Juxt
  module Sinatra
    # Allows for declared, named URI Templates in Sinatra applications,
    # for both routing and URI generation.
    #
    # The URI generation method (included as a Sinatra helper) modifies
    # the path of generated URIs with request.script_name, meaning the
    # URIs generated will still be valid if multiple Sinatra applications
    # are hosted in the same application, using Rack::Builder.
    #
    # Requires Addressable (http://github.com/sporkmonger/addressable/tree/master).
    #
    # == Example
    #
    # class MyApplication < Sinatra::Base
    #   extend Juxt::Sinatra::URITemplates
    #
    #   uri :index, '/'
    #   uri :entry, '/entries/{id}'
    #
    #   get :index do
    #     "Hello"
    #   end
    #
    #   get :entry do
    #     "The URI of this entry is #{uri(:entry, 'id' => params[:id])}"
    #   end
    # end
    module URITemplates
      def reset_uri_templates!
        @uri_templates = {}
      end
    
      # Declare a named URI Template for use in this application, and
      # in subclasses of this application.
      def uri(name, template)
        @uri_templates[name] = Addressable::Template.new(template)
      end
    
      # Gets the merged URI Template collection for this application,
      # and all superclasses.
      def uri_templates
        if superclass.respond_to?(:uri_templates)
          superclass.uri_templates.merge(@uri_templates)
        else
          @uri_templates
        end
      end
    
      private
    
      # Override Sinatra route compilation (dirty).
      def compile(path)
        super(uri_templates[path] || path)
      end
    
      def inherited(subclass)
        subclass.reset_uri_templates!
        super
      end
    
      # Initialize the URI Template collection for the application
      # being extended, and add a Sinatra helper for expanding
      # templates, by name.
      def self.extended(base)
        base.reset_uri_templates!
        base.helpers do
          def uri(name, *args)
            template = self.class.uri_templates[name]
            if template.nil?
              return nil
            end
            args[0] ||= {}
            uri = template.expand(*args)
            uri.path = request.script_name + uri.path
            uri
          end
        end
      end
    end
  end
end