module Rack class API class Runner HTTP_METHODS = %w[get post put delete head] attr_accessor :settings def initialize @settings = { :prefix => "/", :formats => %w[json jsonp], :middlewares => [] } end # Add a middleware to the execution stack. # def use(middleware, *args) settings[:middlewares] << [middleware, *args] end # Set an additional url prefix. # def prefix(name) settings[:prefix] = name end # Create a new API version. # def version(name, &block) raise ArgumentError, "you need to pass a block" unless block_given? settings[:version] = name.to_s instance_eval(&block) settings.delete(:version) end # Run all routes. # def call(env) # :nodoc: route_set.freeze.call(env) end # Require basic authentication before processing other requests. # The authentication reques must be defined before routes. # # Rack::API.app do # basic_auth "Protected Area" do |user, pass| # User.authenticate(user, pass) # end # end def basic_auth(realm = "Restricted Area", &block) settings[:auth] = [realm, block] end # Define the formats that this app implements. # Respond only to :json by default. # # When setting a format you have some alternatives on how this object # will be formated. # # First, Rack::API will look for a formatter defined on Rack::API::Formatter # namespace. If there's no formatter, it will look for a method to_. # It will raise an exception if no formatter method has been defined. # # See Rack::API::Formatter::Jsonp for an example on how to create additional # formatters. # def respond_to(*formats) settings[:formats] = formats end # Hold all routes. # def route_set # :nodoc: @route_set ||= Rack::Mount::RouteSet.new end # Define a new routing that will be triggered when both request method and # path are recognized. # # You're better off using all verb shortcut methods. Implemented verbs are # +get+, +post+, +put+, +delete+ and +head+. # # class MyAPI < Rack::API # version "v1" do # get "users(.:format)" do # # do something # end # end # end # def route(method, path, requirements = {}, &block) path = Rack::Mount::Strexp.compile mount_path(path), requirements, %w[ / . ? ] route_set.add_route(build_app(block), :path_info => path, :request_method => method) end HTTP_METHODS.each do |method| class_eval <<-RUBY, __FILE__, __LINE__ def #{method}(*args, &block) # def get(*args, &block) route("#{method.upcase}", *args, &block) # route("GET", *args, &block) end # end RUBY end def mount_path(path) # :nodoc: Rack::Mount::Utils.normalize_path([settings[:prefix], settings[:version], path].join("/")) end def build_app(block) # :nodoc: builder = Rack::Builder.new builder.use Rack::Auth::Basic, settings[:auth][0], &settings[:auth][1] if settings[:auth] settings[:middlewares].each {|middleware| builder.use(*middleware)} builder.run App.new(:block => block) builder.to_app end end end end