lib/grape/api.rb in grape-0.1.0 vs lib/grape/api.rb in grape-0.1.1

- old
+ new

@@ -1,23 +1,29 @@ require 'rack/mount' require 'rack/auth/basic' +require 'logger' module Grape + # The API class is the primary entry point for + # creating Grape APIs. Users should subclass this + # class in order to build an API. class API - module Helpers; end - class << self attr_reader :route_set + def logger + @logger ||= Logger.new($STDOUT) + end + def reset! @settings = [{}] @route_set = Rack::Mount::RouteSet.new @prototype = nil end def call(env) - puts "#{env['REQUEST_METHOD']} #{env['PATH_INFO']}" + logger.info "#{env['REQUEST_METHOD']} #{env['PATH_INFO']}" route_set.freeze.call(env) end # Settings are a stack, so when we # want to access them they are merged @@ -28,10 +34,15 @@ def settings_stack @settings end + # Set a configuration value for this + # namespace. + # + # @param key [Symbol] The key of the configuration variable. + # @param value [Object] The value to which to set the configuration variable. def set(key, value) @settings.last[key.to_sym] = value end # Define a root prefix for your entire @@ -46,10 +57,13 @@ def version(*new_versions, &block) new_versions.any? ? nest(block){ set(:version, new_versions) } : settings[:version] end + # Specify the default format for the API's + # serializers. Currently only `:json` is + # supported. def default_format(new_format = nil) new_format ? set(:default_format, new_format.to_sym) : settings[:default_format] end # Add helper methods that will be accessible from any @@ -74,10 +88,12 @@ end m end end + # Add an authentication type to the API. Currently + # only `:http_basic` is supported. def auth(type = nil, options = {}, &block) if type set(:auth, {:type => type.to_sym, :proc => block}.merge(options)) else settings[:auth] @@ -91,51 +107,38 @@ def http_basic(options = {}, &block) options[:realm] ||= "API Authorization" auth :http_basic, options, &block end - def route_set - @route_set ||= Rack::Mount::RouteSet.new - end - - def compile_path(path) - parts = [] - parts << prefix if prefix - parts << ':version' if version - parts << namespace if namespace - parts << path - Rack::Mount::Utils.normalize_path(parts.join('/')) - end - - def route(method, path_info, &block) - route_set.add_route(build_endpoint(&block), - :path_info => Rack::Mount::Strexp.compile(compile_path(path_info)), - :request_method => method - ) - end - - def build_endpoint(&block) + # Defines a route that will be recognized + # by the Grape API. + # + # @param methods [HTTP Verb(s)] One or more HTTP verbs that are accepted by this route. Set to `:any` if you want any verb to be accepted. + # @param paths [String(s)] One or more strings representing the URL segment(s) for this route. + # @param block [Proc] The code to be executed + def route(methods, paths, &block) + methods = Array(methods) + paths = ['/'] if paths == [] + paths = Array(paths) + endpoint = build_endpoint(&block) - b = Rack::Builder.new - b.use Grape::Middleware::Error - b.use Rack::Auth::Basic, settings[:auth][:realm], &settings[:auth][:proc] if settings[:auth] && settings[:auth][:type] == :http_basic - b.use Grape::Middleware::Prefixer, :prefix => prefix if prefix - b.use Grape::Middleware::Versioner, :versions => (version if version.is_a?(Array)) if version - b.use Grape::Middleware::Formatter, :default_format => default_format || :json - - endpoint = Grape::Endpoint.new(&block) - endpoint.send :extend, helpers - b.run endpoint - - b.to_app + methods.each do |method| + paths.each do |path| + path = Rack::Mount::Strexp.compile(compile_path(path)) + route_set.add_route(endpoint, + :path_info => path, + :request_method => (method.to_s.upcase unless method == :any) + ) + end + end end - def get(path_info = '', &block); route('GET', path_info, &block) end - def post(path_info = '', &block); route('POST', path_info, &block) end - def put(path_info = '', &block); route('PUT', path_info, &block) end - def head(path_info = '', &block); route('HEAD', path_info, &block) end - def delete(path_info = '', &block); route('DELETE', path_info, &block) end + def get(*paths, &block); route('GET', paths, &block) end + def post(*paths, &block); route('POST', paths, &block) end + def put(*paths, &block); route('PUT', paths, &block) end + def head(*paths, &block); route('HEAD', paths, &block) end + def delete(*paths, &block); route('DELETE', paths, &block) end def namespace(space = nil, &block) if space || block_given? nest(block) do set(:namespace, space.to_s) if space @@ -143,30 +146,67 @@ else Rack::Mount::Utils.normalize_path(settings_stack.map{|s| s[:namespace]}.join('/')) end end + alias_method :group, :namespace + alias_method :resource, :namespace + alias_method :resources, :namespace + + # Create a scope without affecting the URL. + # + # @param name [Symbol] Purely placebo, just allows to to name the scope to make the code more readable. + def scope(name = nil, &block) + nest(block) + end + + protected + # Execute first the provided block, then each of the # block passed in. Allows for simple 'before' setups # of settings stack pushes. def nest(*blocks, &block) blocks.reject!{|b| b.nil?} if blocks.any? settings_stack << {} - instance_eval &block + instance_eval &block if block_given? blocks.each{|b| instance_eval &b} settings_stack.pop else instance_eval &block end end - alias_method :group, :namespace - alias_method :resource, :namespace - alias_method :resources, :namespace + def build_endpoint(&block) + b = Rack::Builder.new + b.use Grape::Middleware::Error + b.use Rack::Auth::Basic, settings[:auth][:realm], &settings[:auth][:proc] if settings[:auth] && settings[:auth][:type] == :http_basic + b.use Grape::Middleware::Prefixer, :prefix => prefix if prefix + b.use Grape::Middleware::Versioner, :versions => (version if version.is_a?(Array)) if version + b.use Grape::Middleware::Formatter, :default_format => default_format || :json + + endpoint = Grape::Endpoint.generate(&block) + endpoint.send :include, helpers + b.run endpoint + + b.to_app + end def inherited(subclass) subclass.reset! + end + + def route_set + @route_set ||= Rack::Mount::RouteSet.new + end + + def compile_path(path) + parts = [] + parts << prefix if prefix + parts << ':version' if version + parts << namespace if namespace + parts << path + Rack::Mount::Utils.normalize_path(parts.join('/')) end end reset! end \ No newline at end of file