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 22 def before_each(new_setup = false, &block) @before_each ||= [] 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 55 def generate_api_method(method_name, &block) if instance_methods.include?(method_name.to_sym) || instance_methods.include?(method_name.to_s) fail 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 do |endpoint_instance| ActiveSupport::Notifications.instrument('endpoint_render.grape', endpoint: endpoint_instance) do method.bind(endpoint_instance).call end end end
# File lib/grape/endpoint.rb, line 14 def new(*args, &block) if self == Endpoint Class.new(Endpoint).new(*args, &block) else super end end
# File lib/grape/endpoint.rb, line 72 def initialize(new_settings, options = {}, &block) require_option(options, :path) require_option(options, :method) self.inheritable_setting = new_settings.point_in_time_copy route_setting(:saved_declared_params, namespace_stackable(:declared_params)) route_setting(:saved_validations, namespace_stackable(:validations)) namespace_stackable(:representations, []) unless namespace_stackable(:representations) namespace_inheritable(:default_error_status, 500) unless namespace_inheritable(:default_error_status) @options = options @options[:path] = Array(options[:path]) @options[:path] << '/' if options[:path].empty? @options[:method] = Array(options[:method]) @options[:route_options] ||= {} @lazy_initialize_lock = Mutex.new return unless block_given? @source = block @block = self.class.generate_api_method(method_name, &block) end
# File lib/grape/endpoint.rb, line 35 def run_before_each(endpoint) superclass.run_before_each(endpoint) unless self == Endpoint before_each.each do |blk| blk.call(endpoint) if blk.respond_to? :call end end
Public Instance Methods
# File lib/grape/endpoint.rb, line 206 def call(env) lazy_initialize! dup.call!(env) end
# File lib/grape/endpoint.rb, line 211 def call!(env) env[Grape::Env::API_ENDPOINT] = self @env = env @app.call(env) end
# File lib/grape/endpoint.rb, line 199 def compile_path(prepared_path, anchor = true, requirements = {}) endpoint_options = {} endpoint_options[:version] = /#{namespace_inheritable(:version).join('|')}/ if namespace_inheritable(:version) endpoint_options.merge!(requirements) Rack::Mount::Strexp.compile(prepared_path, endpoint_options, %w( / . ? ), anchor) 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 219 def endpoints options[:app].endpoints if options[:app] && options[:app].respond_to?(:endpoints) end
# File lib/grape/endpoint.rb, line 223 def equals?(e) (options == e.options) && (inheritable_setting.to_hash == e.inheritable_setting.to_hash) end
# File lib/grape/endpoint.rb, line 104 def method_name [options[:method], Namespace.joined_space(namespace_stackable(:namespace)), (namespace_stackable(:mount_path) || []).join('/'), options[:path].join('/') ].join(' ') end
# File lib/grape/endpoint.rb, line 122 def mount_in(route_set) if endpoints endpoints.each do |e| e.mount_in(route_set) end else reset_routes! routes.each do |route| methods = [route.route_method] if !namespace_inheritable(:do_not_route_head) && route.route_method == Grape::Http::Headers::GET methods << Grape::Http::Headers::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 195 def namespace @namespace ||= Namespace.joined_space_path(namespace_stackable(:namespace)) end
# File lib/grape/endpoint.rb, line 190 def prepare_path(path) path_settings = inheritable_setting.to_hash[:namespace_stackable].merge(inheritable_setting.to_hash[:namespace_inheritable]) Path.prepare(path, namespace, path_settings) end
# File lib/grape/endpoint.rb, line 168 def prepare_routes options[:method].map do |method| options[:path].map do |path| prepared_path = prepare_path(path) anchor = options[:route_options].fetch(:anchor, true) path = compile_path(prepared_path, anchor && !options[:app], prepare_routes_requirements) request_method = (method.to_s.upcase unless method == :any) Route.new(options[:route_options].clone.merge( prefix: namespace_inheritable(:root_prefix), version: namespace_inheritable(:version) ? namespace_inheritable(:version).join('|') : nil, namespace: namespace, method: request_method, path: prepared_path, params: prepare_routes_path_params(path), compiled: path, settings: inheritable_setting.route.except(:saved_declared_params, :saved_validations) )) end end.flatten end
# File lib/grape/endpoint.rb, line 153 def prepare_routes_path_params(path) path_params = {} # named parameters in the api path regex = Rack::Mount::RegexpWithNamedGroups.new(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 if route_params path_params end
# File lib/grape/endpoint.rb, line 145 def prepare_routes_requirements endpoint_requirements = options[:route_options][:requirements] || {} all_requirements = (namespace_stackable(:namespace).map(&:requirements) << endpoint_requirements) all_requirements.reduce({}) do |base_requirements, single_requirements| base_requirements.merge!(single_requirements) end end
# File lib/grape/endpoint.rb, line 100 def require_option(options, key) fail Grape::Exceptions::MissingOption.new(key) unless options.key?(key) end
# File lib/grape/endpoint.rb, line 116 def reset_routes! endpoints.each(&:reset_routes!) if endpoints @namespace = nil @routes = nil end
# File lib/grape/endpoint.rb, line 112 def routes @routes ||= endpoints ? endpoints.collect(&:routes).flatten : prepare_routes end
Protected Instance Methods
# File lib/grape/endpoint.rb, line 367 def after_validations namespace_stackable(:after_validations) || [] end
# File lib/grape/endpoint.rb, line 371 def afters namespace_stackable(:afters) || [] end
# File lib/grape/endpoint.rb, line 363 def before_validations namespace_stackable(:before_validations) || [] end
# File lib/grape/endpoint.rb, line 359 def befores namespace_stackable(:befores) || [] end
# File lib/grape/endpoint.rb, line 316 def helpers lazy_initialize! && @helpers end
# File lib/grape/endpoint.rb, line 320 def lazy_initialize! return true if @lazy_initialized @lazy_initialize_lock.synchronize do return true if @lazy_initialized @app = options[:app] || build_stack @helpers = build_helpers.tap do |mod| self.class.send(:include, mod) end @lazy_initialized = true end end
# File lib/grape/endpoint.rb, line 229 def run ActiveSupport::Notifications.instrument('endpoint_run.grape', endpoint: self, env: env) do @header = {} @request = Grape::Request.new(env) @params = @request.params @headers = @request.headers cookies.read(@request) self.class.run_before_each(self) run_filters befores, :before run_filters before_validations, :before_validation run_validators validations, request run_filters after_validations, :after_validation response_object = @block ? @block.call(self) : nil run_filters afters, :after cookies.write(header) # The Body commonly is an Array of Strings, the application instance itself, or a File-like object. response_object = file || [body || response_object] [status, header, response_object] end end
# File lib/grape/endpoint.rb, line 349 def run_filters(filters, type = :other) ActiveSupport::Notifications.instrument('endpoint_run_filters.grape', endpoint: self, filters: filters, type: type) do (filters || []).each do |filter| instance_eval(&filter) end end post_extension = DSL::InsideRoute.post_filter_methods(type) extend post_extension if post_extension end
# File lib/grape/endpoint.rb, line 335 def run_validators(validators, request) validation_errors = [] validators.each do |validator| begin validator.validate(request) rescue Grape::Exceptions::Validation => e validation_errors << e end end validation_errors.any? && fail(Grape::Exceptions::ValidationErrors, errors: validation_errors, headers: header) end
# File lib/grape/endpoint.rb, line 375 def validations route_setting(:saved_validations) || [] end
Private Instance Methods
# File lib/grape/endpoint.rb, line 305 def build_helpers helpers = namespace_stackable(:helpers) || [] Module.new do helpers.each do |mod_to_include| include mod_to_include end end end
# File lib/grape/endpoint.rb, line 259 def build_stack b = Rack::Builder.new b.use Rack::Head b.use Grape::Middleware::Error, format: namespace_inheritable(:format), content_types: namespace_stackable_with_hash(:content_types), default_status: namespace_inheritable(:default_error_status), rescue_all: namespace_inheritable(:rescue_all), default_error_formatter: namespace_inheritable(:default_error_formatter), error_formatters: namespace_stackable_with_hash(:error_formatters), rescue_options: namespace_stackable_with_hash(:rescue_options) || {}, rescue_handlers: namespace_stackable_with_hash(:rescue_handlers) || {}, base_only_rescue_handlers: namespace_stackable_with_hash(:base_only_rescue_handlers) || {}, all_rescue_handler: namespace_inheritable(:all_rescue_handler) (namespace_stackable(: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 namespace_inheritable(:version) b.use Grape::Middleware::Versioner.using(namespace_inheritable(:version_options)[:using]), versions: namespace_inheritable(:version) ? namespace_inheritable(:version).flatten : nil, version_options: namespace_inheritable(:version_options), prefix: namespace_inheritable(:root_prefix) end b.use Grape::Middleware::Formatter, format: namespace_inheritable(:format), default_format: namespace_inheritable(:default_format) || :txt, content_types: namespace_stackable_with_hash(:content_types), formatters: namespace_stackable_with_hash(:formatters), parsers: namespace_stackable_with_hash(:parsers) b.run ->(env) { env[Grape::Env::API_ENDPOINT].run } b.to_app end