class Rack::ForNow::Service
The ‘Service` class is the base classes for all the third-party services made available by `Rack::ForNow`.
@abstract The ‘Service` class is not meant to be used directly.
Attributes
Public Class Methods
Sets up a service to be installed on an arbitrary path
@example
map '/romeo' do run Rack::ForNow::GitHub.new('will'). with(Rack::ForNow::Rubydoc). # this sets up `/romeo/docs` with(Rack::ForNow::Rubydoc.on('/documentation') # this sets up `/romeo/documentation` end
@see with
@api public
@param [String] path the path under which the service is to be installed
@return [Service] the service, set up so to be installed on ‘path`
# File lib/rack/for_now.rb, line 84 def self.on(path) service = self.new service.subpath = path return service end
Public Instance Methods
@api private
# File lib/rack/for_now.rb, line 98 def app builder = Rack::Builder.new @subservices ||= {} @subservices.each do |path, service| builder.map('/' + path) { run service.main_app } end service = self builder.map('/') { run service.main_app } return builder.to_app end
@api private
# File lib/rack/for_now.rb, line 92 def call(env) @app ||= app @app.call(env) end
@return [String] the default path where the service will be mounted.
# File lib/rack/for_now.rb, line 13 def default_subpath self.class.const_get(:DEFAULT_SUBPATH) end
Infer parameters from the Rack
request.
@api private
@return void
# File lib/rack/for_now.rb, line 170 def infer_runtime_parameters(env) # TODO: provide a way to update also other parameters params = self.instance_variables.map(&:to_sym) if params.include?(:@project) @project ||= last_URL_segment(env) end end
@api private
# File lib/rack/for_now.rb, line 179 def last_URL_segment(env) path = env['SCRIPT_NAME'].to_s + env['PATH_NAME'].to_s segments = path.split('/') idx_last = (segments.last == subpath) ? -2 : -1 return segments[idx_last] end
@api private
# File lib/rack/for_now.rb, line 113 def main_app lambda do |env| root_requested = env['PATH_INFO'].chomp('/').empty? if !root_requested return [404, {'Content-Type' => 'text/plain', 'X-Cascade' => 'pass'}, ["Not Found: #{env['PATH_INFO']}"]] end set_parameters infer_runtime_parameters(env) destination_url = personalized(template_url) return [307, { 'Location' => destination_url }, [""]] end end
@api private
# File lib/rack/for_now.rb, line 136 def parent_service return @parent_service || FakeService.new end
@api private
# File lib/rack/for_now.rb, line 188 def personalized(url_template) placeholders = url_template.scan(/\%\{(.*?)\}/).to_a.flatten.uniq values = Hash[placeholders.map { |placeholder| [placeholder, instance_variable_get("@#{placeholder}".to_sym)] }] values.each do |placeholder, value| if value.nil? raise "Unset template variable #{placeholder} for #{self.class}" end end url = url_template values.each do |placeholder, value| url = url.gsub("%{#{placeholder}}", value) end return url end
Set the service parameters, inheriting from the parent service if needed.
@api private
@return void
# File lib/rack/for_now.rb, line 147 def set_parameters if parent_service.nil? return end params = self.instance_variables.map(&:to_sym) params -= [:@app, :@parent_service, :@subpath, :@subservices] params.reject! { |param_name| !self.instance_variable_get(param_name).nil? } methods = params.map { |param_name| param_name.to_s[1..-1].to_sym } params.each_with_index do |param_name, idx| value = parent_service.send(methods[idx]) self.instance_variable_set(param_name, value) end end
@api private
# File lib/rack/for_now.rb, line 130 def subpath return @subpath || default_subpath end
@return [String] the template URL for the service.
# File lib/rack/for_now.rb, line 19 def template_url self.class.const_get(:TEMPLATE_URL) end
Mounts a service on a subpath.
@example Mount a GitHub
Issues redirect under ‘/romeo/issues` (default path)
map '/romeo' do run Rack::ForNow::GitHub.new('will').with(Rack::ForNow::GHIssues) end
@example Mount a GitHub
Issues redirect under ‘/romeo/bugs`
map '/romeo' do run Rack::ForNow::GitHub.new('will').with(Rack::ForNow::GHIssues.on('bugs')) end
@example Mount multiple services
map '/romeo' do run Rack::ForNow::GitHub.new('will'). with(Rack::ForNow::GHIssues, Rack::ForNow::Rubydoc). with(Rack::ForNow::GHPages.on('tutorial')). end
@see .on
@api public
@param [Service, Class] subservices the services to mount
@return [Service] the service itself
# File lib/rack/for_now.rb, line 53 def with(*subservices) subservices.each do |subservice| subservice = subservice.new if subservice.is_a? Class subservice.parent_service = self @subservices ||= {} @subservices[subservice.subpath] = subservice end return self end