class Grape::Endpoint
from inside a ‘get`, `post`, etc.
on the instance level of this class may be called
blocks are executed. In other words, any methods
An Endpoint is the proxy scope in which all routing
def after_validations
def after_validations namespace_stackable(:after_validations) end
def afters
def afters namespace_stackable(:afters) end
def before_each(new_setup = false, &block)
def before_each(new_setup = false, &block) @before_each ||= [] if new_setup == false return @before_each unless block @before_each << block else @before_each = [new_setup] end end
def before_validations
def before_validations namespace_stackable(:before_validations) end
def befores
def befores namespace_stackable(:befores) end
def build_helpers
def build_helpers helpers = namespace_stackable(:helpers) return if helpers.empty? Module.new { helpers.each { |mod_to_include| include mod_to_include } } end
def build_stack(helpers)
def build_stack(helpers) stack = Grape::Middleware::Stack.new content_types = namespace_stackable_with_hash(:content_types) format = namespace_inheritable(:format) stack.use Rack::Head stack.use Class.new(Grape::Middleware::Error), helpers: helpers, format: format, content_types: content_types, default_status: namespace_inheritable(:default_error_status), rescue_all: namespace_inheritable(:rescue_all), rescue_grape_exceptions: namespace_inheritable(:rescue_grape_exceptions), 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_reverse_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), grape_exceptions_rescue_handler: namespace_inheritable(:grape_exceptions_rescue_handler) stack.concat namespace_stackable(:middleware) if namespace_inheritable(:version).present? stack.use Grape::Middleware::Versioner.using(namespace_inheritable(:version_options)[:using]), versions: namespace_inheritable(:version).flatten, version_options: namespace_inheritable(:version_options), prefix: namespace_inheritable(:root_prefix), mount_path: namespace_stackable(:mount_path).first end stack.use Grape::Middleware::Formatter, format: format, default_format: namespace_inheritable(:default_format) || :txt, content_types: content_types, formatters: namespace_stackable_with_hash(:formatters), parsers: namespace_stackable_with_hash(:parsers) builder = stack.build builder.run ->(env) { env[Grape::Env::API_ENDPOINT].run } builder.to_app end
def call(env)
def call(env) lazy_initialize! dup.call!(env) end
def call!(env)
def call!(env) env[Grape::Env::API_ENDPOINT] = self @env = env @app.call(env) end
def endpoints
Return the collection of endpoints within this endpoint.
def endpoints options[:app].endpoints if options[:app].respond_to?(:endpoints) end
def equals?(endpoint)
def equals?(endpoint) (options == endpoint.options) && (inheritable_setting.to_hash == endpoint.inheritable_setting.to_hash) end
def execute
def execute @block&.call(self) end
def finallies
def finallies namespace_stackable(:finallies) end
def generate_api_method(method_name, &block)
-
(NameError)- an instance method with the same name already exists
Returns:
-
(Proc)-
Parameters:
-
method_name(String, Symbol) --
Other tags:
- Api: - private
def generate_api_method(method_name, &block) raise NameError.new("method #{method_name.inspect} already exists and cannot be used as an unbound method name") if method_defined?(method_name) 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_call(endpoint_instance) end end end
def helpers
def helpers lazy_initialize! && @helpers end
def inherit_settings(namespace_stackable)
Update our settings from a given set of stackable parameters. Used when
def inherit_settings(namespace_stackable) parent_validations = namespace_stackable[:validations] inheritable_setting.route[:saved_validations].concat(parent_validations) if parent_validations.any? parent_declared_params = namespace_stackable[:declared_params] inheritable_setting.route[:declared_params].concat(parent_declared_params.flatten) if parent_declared_params.any? endpoints&.each { |e| e.inherit_settings(namespace_stackable) } end
def initialize(new_settings, options = {}, &block)
- Yield: - a block defining what your API should do when this endpoint is hit
Other tags:
- Note: - This happens at the time of API definition, so in this context the
Options Hash:
(**options)-
route_options(Hash) -- -
method(String or Array) -- which HTTP method(s) can be used -
path(String or Array) -- the path to this endpoint, within
Parameters:
-
options(Hash) -- attributes of this endpoint -
new_settings(InheritableSetting) -- settings to determine the params,
def initialize(new_settings, options = {}, &block) require_option(options, :path) require_option(options, :method) self.inheritable_setting = new_settings.point_in_time_copy # now +namespace_stackable(:declared_params)+ contains all params defined for # this endpoint and its parents, but later it will be cleaned up, # see +reset_validations!+ in lib/grape/dsl/validations.rb route_setting(:declared_params, namespace_stackable(:declared_params).flatten) 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 @lazy_initialized = nil @block = nil @status = nil @stream = nil @body = nil @proc = nil return unless block @source = block @block = self.class.generate_api_method(method_name, &block) end
def inspect
an endpoint through an api. See https://github.com/ruby-grape/grape/issues/2398
The purpose of this override is solely for stripping internals when an error occurs while calling
def inspect return super unless env "#{self.class} in '#{route.origin}' endpoint" end
def lazy_initialize!
def lazy_initialize! return true if @lazy_initialized @lazy_initialize_lock.synchronize do return true if @lazy_initialized @helpers = build_helpers&.tap { |mod| self.class.include mod } @app = options[:app] || build_stack(@helpers) @lazy_initialized = true end end
def map_routes
def map_routes options[:method].map { |method| options[:path].map { |path| yield method, path } } end
def method_name
def method_name [options[:method], Namespace.joined_space(namespace_stackable(:namespace)), (namespace_stackable(:mount_path) || []).join('/'), options[:path].join('/')] .join(' ') end
def mount_in(router)
def mount_in(router) return endpoints.each { |e| e.mount_in(router) } if endpoints reset_routes! routes.each do |route| router.append(route.apply(self)) next unless !namespace_inheritable(:do_not_route_head) && route.request_method == Rack::GET route.dup.then do |head_route| head_route.convert_to_head_request! router.append(head_route.apply(self)) end end end
def namespace
def namespace @namespace ||= Namespace.joined_space_path(namespace_stackable(:namespace)) end
def new(...)
def new(...) self == Endpoint ? Class.new(Endpoint).new(...) : super end
def options?
def options? options[:options_route_enabled] && env[Rack::REQUEST_METHOD] == Rack::OPTIONS end
def prepare_default_path_settings
def prepare_default_path_settings namespace_stackable_hash = inheritable_setting.namespace_stackable.to_hash namespace_inheritable_hash = inheritable_setting.namespace_inheritable.to_hash namespace_stackable_hash.merge!(namespace_inheritable_hash) end
def prepare_default_route_attributes
def prepare_default_route_attributes { namespace: namespace, version: prepare_version, requirements: prepare_routes_requirements, prefix: namespace_inheritable(:root_prefix), anchor: options[:route_options].fetch(:anchor, true), settings: inheritable_setting.route.except(:declared_params, :saved_validations), forward_match: options[:forward_match] } end
def prepare_routes_requirements
def prepare_routes_requirements {}.merge!(*namespace_stackable(:namespace).map(&:requirements)).tap do |requirements| endpoint_requirements = options.dig(:route_options, :requirements) requirements.merge!(endpoint_requirements) if endpoint_requirements end end
def prepare_version
def prepare_version version = namespace_inheritable(:version) return if version.blank? version.length == 1 ? version.first : version end
def require_option(options, key)
def require_option(options, key) raise Grape::Exceptions::MissingOption.new(key) unless options.key?(key) end
def reset_routes!
def reset_routes! endpoints&.each(&:reset_routes!) @namespace = nil @routes = nil end
def routes
def routes @routes ||= endpoints ? endpoints.collect(&:routes).flatten : to_routes end
def run
def run ActiveSupport::Notifications.instrument('endpoint_run.grape', endpoint: self, env: env) do @header = Grape::Util::Header.new @request = Grape::Request.new(env, build_params_with: namespace_inheritable(:build_params_with)) @params = @request.params @headers = @request.headers begin cookies.read(@request) self.class.run_before_each(self) run_filters befores, :before if (allowed_methods = env[Grape::Env::GRAPE_ALLOWED_METHODS]) allow_header_value = allowed_methods.join(', ') raise Grape::Exceptions::MethodNotAllowed.new(header.merge('Allow' => allow_header_value)) unless options? header Grape::Http::Headers::ALLOW, allow_header_value response_object = '' status 204 else run_filters before_validations, :before_validation run_validators validations, request run_filters after_validations, :after_validation response_object = execute end run_filters afters, :after cookies.write(header) # status verifies body presence when DELETE @body ||= response_object # The body commonly is an Array of Strings, the application instance itself, or a Stream-like object response_object = stream || [body] [status, header, response_object] ensure run_filters finallies, :finally end end end
def run_before_each(endpoint)
def run_before_each(endpoint) superclass.run_before_each(endpoint) unless self == Endpoint before_each.each { |blk| blk.call(endpoint) if blk.respond_to?(:call) } end
def run_filters(filters, type = :other)
def run_filters(filters, type = :other) ActiveSupport::Notifications.instrument('endpoint_run_filters.grape', endpoint: self, filters: filters, type: type) do filters&.each { |filter| instance_eval(&filter) } end post_extension = DSL::InsideRoute.post_filter_methods(type) extend post_extension if post_extension end
def run_validators(validators, request)
def run_validators(validators, request) validation_errors = [] ActiveSupport::Notifications.instrument('endpoint_run_validators.grape', endpoint: self, validators: validators, request: request) do validators.each do |validator| validator.validate(request) rescue Grape::Exceptions::Validation => e validation_errors << e break if validator.fail_fast? rescue Grape::Exceptions::ValidationArrayErrors => e validation_errors.concat e.errors break if validator.fail_fast? end end validation_errors.any? && raise(Grape::Exceptions::ValidationErrors.new(errors: validation_errors, headers: header)) end
def to_routes
def to_routes default_route_options = prepare_default_route_attributes default_path_settings = prepare_default_path_settings map_routes do |method, raw_path| prepared_path = Path.new(raw_path, namespace, default_path_settings) params = options[:route_options].present? ? options[:route_options].merge(default_route_options) : default_route_options route = Grape::Router::Route.new(method, prepared_path.origin, prepared_path.suffix, params) route.apply(self) end.flatten end
def validations
def validations return enum_for(:validations) unless block_given? route_setting(:saved_validations)&.each do |saved_validation| yield Grape::Validations::ValidatorFactory.create_validator(saved_validation) end end