class Rester::Service
Constants
- BASE_MIDDLEWARE
The base set of middleware to use for every service.
Middleware
will be executed in the order specified.
Attributes
Public Class Methods
# File lib/rester/service.rb, line 84 def _load_resources(version_module) version_module.constants.map { |c| version_module.const_get(c) }.select { |c| c.is_a?(Class) && c < Service::Resource } end
# File lib/rester/service.rb, line 73 def _load_version_module(version) versions.include?(version.to_sym) or raise ArgumentError, "invalid version #{version.inspect}" const_get(version.to_s.upcase) end
# File lib/rester/service.rb, line 36 def _middleware @__middleware ||= BASE_MIDDLEWARE.dup end
The call method needs to call the rack_call
method, which adds additional rack middleware.
# File lib/rester/service.rb, line 51 def call(env) instance.rack_call(env) end
# File lib/rester/service.rb, line 45 def instance @instance ||= new end
# File lib/rester/service.rb, line 55 def method_missing(meth, *args, &block) instance.public_send(meth, *args, &block) end
# File lib/rester/service.rb, line 80 def resources(version_module) (@__resources ||= {})[version_module] ||= _load_resources(version_module) end
# File lib/rester/service.rb, line 65 def service_name @__name ||= (name && name.split('::').last) || 'Anonymous' end
Middleware
DSL
# File lib/rester/service.rb, line 32 def use(klass, *args) _middleware << [klass, *args] end
# File lib/rester/service.rb, line 69 def version_module(version) (@__version_modules ||= {})[version.to_sym] ||= _load_version_module(version) end
# File lib/rester/service.rb, line 59 def versions @__versions ||= constants.map(&:to_s).select { |c| c.match(/^V\d{1,3}$/) }.map(&:downcase).map(&:to_sym) end
Public Instance Methods
Call the service app directly.
Duplicates the instance before processing the request so individual requests can't impact each other.
# File lib/rester/service.rb, line 117 def call(env) dup.call!(env) end
Process the request.
Calls methods that may modify instance variables, so the instance should be dup'd beforehand.
# File lib/rester/service.rb, line 126 def call!(env) _process_request(Rester.request) end
# File lib/rester/service.rb, line 97 def logger=(new_logger) new_logger = Utils::LoggerWrapper.new(new_logger) if new_logger @_logger = new_logger end
# File lib/rester/service.rb, line 102 def name self.class.service_name end
To be called by Rack. Wraps the app in middleware.
# File lib/rester/service.rb, line 108 def rack_call(env) _rack_app.call(env) end
Private Instance Methods
# File lib/rester/service.rb, line 136 def _build_rack_app Rack::Builder.new.tap { |app| self.class._middleware.each { |m| app.use(*m) } app.run self }.to_app end
Calls the appropriate method on the appropriate Service::Resource
for the request.
# File lib/rester/service.rb, line 165 def _call_method(request) params = request.params resource_obj = nil resource_id = nil request.each_resource do |name, id| unless resource_obj (resource_obj = _load_resource(request.version, name)) or _error!(Errors::NotFoundError) else mounted_resource = resource_obj.mounts[name] or _error!(Errors::NotFoundError) resource_obj = mounted_resource.new end params.merge!(resource_obj.id_param => id) if id resource_id = id end resource_obj.process(request.request_method, !!resource_id, params) end
Throws an exception (instead of raising it). This is done for performance reasons. The exception will be caught in the ErrorHandling middleware.
# File lib/rester/service.rb, line 224 def _error!(klass, message=nil) Errors.throw_error!(klass, message) end
Loads the appropriate Service::Resource
for the request. This will return the class, not an instance.
# File lib/rester/service.rb, line 190 def _load_resource(version, name) _version_module(version).const_get(name.camelcase.singularize).new rescue NameError nil end
Prepares the retval from a Service::Resource
method to be returned to the client (i.e., validates it and dumps it as JSON).
# File lib/rester/service.rb, line 205 def _prepare_response(retval) unless retval.is_a?(Hash) _error!(Errors::ServerError, "Invalid response: #{retval.inspect}") end JSON.dump(retval) end
Validates the request, calls the appropriate Service::Resource
method and returns a valid Rack response.
# File lib/rester/service.rb, line 146 def _process_request(request) _error!(Errors::NotFoundError) unless request.valid? _validate_version(request) retval = _call_method(request) _response(request.post? ? 201 : 200, _prepare_response(retval)) end
# File lib/rester/service.rb, line 132 def _rack_app @__rack_app ||= _build_rack_app end
Returns a valid rack response.
# File lib/rester/service.rb, line 215 def _response(status, body=nil, headers={}) body = [body].compact headers = headers.merge("Content-Type" => "application/json") Rack::Response.new(body, status, headers).finish end
Validates that the version of the request matches a defined version module. If the version is not found, it throws a NotFoundError (HTTP 404).
# File lib/rester/service.rb, line 156 def _validate_version(request) unless self.class.versions.include?(request.version) _error!(Errors::NotFoundError, request.version) end end
Returns the module specified by the version in the request.
# File lib/rester/service.rb, line 198 def _version_module(version) self.class.version_module(version) end