class Grape::Endpoint
An Endpoint
is the proxy scope in which all routing blocks are executed. In other words, any methods on the instance level of this class may be called from inside a `get`, `post`, etc.
Attributes
Public Class Methods
# File lib/grape/endpoint.rb, line 11 def before_each(new_setup = false, &block) if new_setup == false if block_given? @before_each = block else return @before_each end else @before_each = new_setup end end
@api private
Create an UnboundMethod that is appropriate for executing an endpoint route.
The unbound method allows explicit calls to return
without raising a LocalJumpError
. The method will be removed, but a Proc
reference to it will be returned. The returned Proc
expects a single argument: the instance of Endpoint
to bind to the method during the call.
@param [String, Symbol] method_name
@return [Proc] @raise [NameError] an instance method with the same name already exists
# File lib/grape/endpoint.rb, line 36 def generate_api_method(method_name, &block) if instance_methods.include?(method_name.to_sym) || instance_methods.include?(method_name.to_s) raise NameError.new("method #{method_name.inspect} already exists and cannot be used as an unbound method name") end define_method(method_name, &block) method = instance_method(method_name) remove_method(method_name) proc { |endpoint_instance| method.bind(endpoint_instance).call } end
# File lib/grape/endpoint.rb, line 47 def initialize(settings, options = {}, &block) require_option(options, :path) require_option(options, :method) @settings = settings @options = options @options[:path] = Array(options[:path]) @options[:path] << '/' if options[:path].empty? @options[:method] = Array(options[:method]) @options[:route_options] ||= {} if block_given? @source = block @block = self.class.generate_api_method(method_name, &block) end end
Public Instance Methods
Allows you to define the response body as something other than the return value.
@example
get '/body' do body "Body" "Not the Body" end GET /body # => "Body"
# File lib/grape/endpoint.rb, line 306 def body(value = nil) if value @body = value else @body end end
# File lib/grape/endpoint.rb, line 155 def call(env) dup.call!(env) end
# File lib/grape/endpoint.rb, line 159 def call!(env) extend helpers env['api.endpoint'] = self if options[:app] options[:app].call(env) else builder = build_middleware builder.run options[:app] || lambda { |arg| run(arg) } builder.call(env) end end
# File lib/grape/endpoint.rb, line 148 def compile_path(prepared_path, anchor = true, requirements = {}) endpoint_options = {} endpoint_options[:version] = /#{settings[:version].join('|')}/ if settings[:version] endpoint_options.merge!(requirements) Rack::Mount::Strexp.compile(prepared_path, endpoint_options, %w( / . ? ), anchor) end
Set response content-type
# File lib/grape/endpoint.rb, line 280 def content_type(val) header('Content-Type', val) end
A filtering method that will return a hash consisting only of keys that have been declared by a `params` statement against the current/target endpoint or parent namespaces
@param params [Hash] The initial hash to filter. Usually this will just be `params` @param options [Hash] Can pass `:include_missing`, `:stringify` and `:include_parent_namespaces` options. `:include_parent_namespaces` defaults to true, hence must be set to false if you want only to return params declared against the current/target endpoint
# File lib/grape/endpoint.rb, line 181 def declared(params, options = {}, declared_params = nil) options[:include_missing] = true unless options.key?(:include_missing) options[:include_parent_namespaces] = true unless options.key?(:include_parent_namespaces) if declared_params.nil? declared_params = !options[:include_parent_namespaces] ? settings[:declared_params] : settings.gather(:declared_params) end unless declared_params raise ArgumentError, "Tried to filter for declared parameters but none exist." end if params.is_a? Array params.map do |param| declared(param || {}, options, declared_params) end else declared_params.inject({}) do |hash, key| key = { key => nil } unless key.is_a? Hash key.each_pair do |parent, children| output_key = options[:stringify] ? parent.to_s : parent.to_sym if params.key?(parent) || options[:include_missing] hash[output_key] = if children declared(params[parent] || {}, options, Array(children)) else params[parent] end end end hash end end end
Return the collection of endpoints within this endpoint. This is the case when an Grape::API
mounts another Grape::API
.
# File lib/grape/endpoint.rb, line 382 def endpoints if options[:app] && options[:app].respond_to?(:endpoints) options[:app].endpoints else nil end end
End the request and display an error to the end user with the specified message.
@param message [String] The message to display. @param status [Integer] the HTTP Status Code. Defaults to default_error_status, 500 if not set.
# File lib/grape/endpoint.rb, line 227 def error!(message, status = nil, headers = nil) status = settings[:default_error_status] unless status throw :error, message: message, status: status, headers: headers end
Set an individual header or retrieve all headers that have been set.
# File lib/grape/endpoint.rb, line 271 def header(key = nil, val = nil) if key val ? @header[key.to_s] = val : @header.delete(key.to_s) else @header end end
# File lib/grape/endpoint.rb, line 70 def method_name [options[:method], Namespace.joined_space(settings), settings.gather(:mount_path).join('/'), options[:path].join('/') ].join(" ") end
# File lib/grape/endpoint.rb, line 82 def mount_in(route_set) if endpoints endpoints.each { |e| e.mount_in(route_set) } else routes.each do |route| methods = [route.route_method] if !settings[:do_not_route_head] && route.route_method == "GET" methods << "HEAD" end methods.each do |method| route_set.add_route(self, { path_info: route.route_compiled, request_method: method }, route_info: route) end end end end
# File lib/grape/endpoint.rb, line 144 def namespace @namespace ||= Namespace.joined_space_path(settings) end
# File lib/grape/endpoint.rb, line 140 def prepare_path(path) Path.prepare(path, namespace, settings) end
# File lib/grape/endpoint.rb, line 101 def prepare_routes routes = [] options[:method].each do |method| options[:path].each do |path| prepared_path = prepare_path(path) anchor = options[:route_options][:anchor] anchor = anchor.nil? ? true : anchor endpoint_requirements = options[:route_options][:requirements] || {} all_requirements = (settings.gather(:namespace).map(&:requirements) << endpoint_requirements) requirements = all_requirements.reduce({}) do |base_requirements, single_requirements| base_requirements.merge!(single_requirements) end path = compile_path(prepared_path, anchor && !options[:app], requirements) regex = Rack::Mount::RegexpWithNamedGroups.new(path) path_params = {} # named parameters in the api path named_params = regex.named_captures.map { |nc| nc[0] } - %w(version format) named_params.each { |named_param| path_params[named_param] = "" } # route parameters declared via desc or appended to the api declaration route_params = (options[:route_options][:params] || {}) path_params.merge!(route_params) request_method = (method.to_s.upcase unless method == :any) routes << Route.new(options[:route_options].clone.merge( prefix: settings[:root_prefix], version: settings[:version] ? settings[:version].join('|') : nil, namespace: namespace, method: request_method, path: prepared_path, params: path_params, compiled: path )) end end routes end
Allows you to make use of Grape
Entities by setting the response body to the serializable hash of the entity provided in the `:with` option. This has the added benefit of automatically passing along environment and version information to the serialization, making it very easy to do conditional exposures. See Entity docs for more info.
@example
get '/users/:id' do present User.find(params[:id]), with: API::Entities::User, admin: current_user.admin? end
# File lib/grape/endpoint.rb, line 329 def present(*args) options = args.count > 1 ? args.extract_options! : {} key, object = if args.count == 2 && args.first.is_a?(Symbol) args else [nil, args.first] end entity_class = options.delete(:with) if entity_class.nil? # entity class not explicitely defined, auto-detect from relation#klass or first object in the collection object_class = if object.respond_to?(:klass) object.klass else object.respond_to?(:first) ? object.first.class : object.class end object_class.ancestors.each do |potential| entity_class ||= (settings[:representations] || {})[potential] end entity_class ||= object_class.const_get(:Entity) if object_class.const_defined?(:Entity) && object_class.const_get(:Entity).respond_to?(:represent) end root = options.delete(:root) representation = if entity_class embeds = { env: env } embeds[:version] = env['api.version'] if env['api.version'] entity_class.represent(object, embeds.merge(options)) else object end representation = { root => representation } if root representation = (@body || {}).merge(key => representation) if key body representation end
Redirect to a new url.
@param url [String] The url to be redirect. @param options [Hash] The options used when redirect.
:permanent, default true.
# File lib/grape/endpoint.rb, line 237 def redirect(url, options = {}) merged_options = { permanent: false }.merge(options) if merged_options[:permanent] status 301 else if env['HTTP_VERSION'] == 'HTTP/1.1' && request.request_method.to_s.upcase != "GET" status 303 else status 302 end end header "Location", url body "" end
# File lib/grape/endpoint.rb, line 66 def require_option(options, key) raise Grape::Exceptions::MissingOption.new(key) unless options.key?(key) end
Returns route information for the current request.
@example
desc "Returns the route description." get '/' do route.route_description end
# File lib/grape/endpoint.rb, line 376 def route env["rack.routing_args"][:route_info] end
# File lib/grape/endpoint.rb, line 78 def routes @routes ||= endpoints ? endpoints.collect(&:routes).flatten : prepare_routes end
Set or retrieve the HTTP status code.
@param status [Integer] The HTTP Status Code to return for this request.
# File lib/grape/endpoint.rb, line 255 def status(status = nil) if status @status = status else return @status if @status case request.request_method.to_s.upcase when 'POST' 201 else 200 end end end
The API
version as specified in the URL.
# File lib/grape/endpoint.rb, line 218 def version env['api.version'] end
Protected Instance Methods
# File lib/grape/endpoint.rb, line 525 def after_validations aggregate_setting(:after_validations) end
# File lib/grape/endpoint.rb, line 529 def afters aggregate_setting(:afters) end
# File lib/grape/endpoint.rb, line 499 def aggregate_setting(key) settings.stack.inject([]) do |aggregate, frame| aggregate + (frame[key] || []) end end
# File lib/grape/endpoint.rb, line 521 def before_validations aggregate_setting(:before_validations) end
# File lib/grape/endpoint.rb, line 517 def befores aggregate_setting(:befores) end
# File lib/grape/endpoint.rb, line 431 def build_middleware b = Rack::Builder.new b.use Rack::Head b.use Grape::Middleware::Error, format: settings[:format], content_types: settings[:content_types], default_status: settings[:default_error_status] || 500, rescue_all: settings[:rescue_all], default_error_formatter: settings[:default_error_formatter], error_formatters: settings[:error_formatters], rescue_options: settings[:rescue_options], rescue_handlers: merged_setting(:rescue_handlers), base_only_rescue_handlers: merged_setting(:base_only_rescue_handlers), all_rescue_handler: settings[:all_rescue_handler] aggregate_setting(:middleware).each do |m| m = m.dup block = m.pop if m.last.is_a?(Proc) if block b.use(*m, &block) else b.use(*m) end end if settings[:auth] auth_proc = settings[:auth][:proc] auth_proc_context = self auth_middleware = { http_basic: { class: Rack::Auth::Basic, args: [settings[:auth][:realm]] }, http_digest: { class: Rack::Auth::Digest::MD5, args: [settings[:auth][:realm], settings[:auth][:opaque]] } }[settings[:auth][:type]] # evaluate auth proc in context of endpoint if auth_middleware b.use auth_middleware[:class], *auth_middleware[:args] do |*args| auth_proc_context.instance_exec(*args, &auth_proc) end end end if settings[:version] b.use Grape::Middleware::Versioner.using(settings[:version_options][:using]), versions: settings[:version] ? settings[:version].flatten : nil, version_options: settings[:version_options], prefix: settings[:root_prefix] end b.use Grape::Middleware::Formatter, format: settings[:format], default_format: settings[:default_format] || :txt, content_types: settings[:content_types], formatters: settings[:formatters], parsers: settings[:parsers] b end
# File lib/grape/endpoint.rb, line 491 def helpers m = Module.new settings.stack.each do |frame| m.send :include, frame[:helpers] if frame[:helpers] end m end
# File lib/grape/endpoint.rb, line 505 def merged_setting(key) settings.stack.inject({}) do |merged, frame| merged.merge(frame[key] || {}) end end
# File lib/grape/endpoint.rb, line 392 def run(env) @env = env @header = {} @request = Grape::Request.new(env) @params = @request.params @headers = @request.headers cookies.read(@request) self.class.before_each.call(self) if self.class.before_each run_filters befores run_filters before_validations # Retrieve validations from this namespace and all parent namespaces. validation_errors = [] settings.gather(:validations).each do |validator| begin validator.validate!(params) rescue Grape::Exceptions::Validation => e validation_errors << e end end if validation_errors.any? raise Grape::Exceptions::ValidationErrors, errors: validation_errors end run_filters after_validations response_text = @block ? @block.call(self) : nil run_filters afters cookies.write(header) [status, header, [body || response_text]] end
# File lib/grape/endpoint.rb, line 511 def run_filters(filters) (filters || []).each do |filter| instance_eval(&filter) end end