class Utopia::Controller
A middleware which loads controller classes and invokes functionality based on the requested path.
Constants
- CONTENT_TYPE
- CONTROLLER_RB
The controller filename.
Attributes
app[R]
Public Class Methods
[](request)
click to toggle source
# File lib/utopia/controller.rb, line 41 def self.[] request request.env[VARIABLES_KEY] end
new(app, root: Utopia::default_root, base: Controller::Base)
click to toggle source
@param root [String] The content root where controllers will be loaded from. @param base [Class] The base class for controllers.
# File lib/utopia/controller.rb, line 47 def initialize(app, root: Utopia::default_root, base: Controller::Base) @app = app @root = root @controller_cache = Concurrent::Map.new @base = base end
Public Instance Methods
call(env)
click to toggle source
# File lib/utopia/controller.rb, line 141 def call(env) env[VARIABLES_KEY] ||= Variables.new request = Rack::Request.new(env) if result = invoke_controllers(request) return result end return @app.call(env) end
freeze()
click to toggle source
Calls superclass method
# File lib/utopia/controller.rb, line 58 def freeze return self if frozen? @root.freeze @base.freeze super end
invoke_controllers(request)
click to toggle source
Invoke the controller layer for a given request. The request path may be rewritten.
# File lib/utopia/controller.rb, line 105 def invoke_controllers(request) request_path = Path.from_string(request.path_info) # The request path must be absolute. We could handle this internally but it is probably better for this to be an error: raise ArgumentError.new("Invalid request path #{request_path}") unless request_path.absolute? # The controller path contains the current complete path being evaluated: controller_path = Path.new # Controller instance variables which eventually get processed by the view: variables = request.env[VARIABLES_KEY] while request_path.components.any? # We copy one path component from the relative path to the controller path at a time. The controller, when invoked, can modify the relative path (by assigning to relative_path.components). This allows for controller-relative rewrites, but only the remaining path postfix can be modified. controller_path.components << request_path.components.shift if controller = lookup_controller(controller_path) # Don't modify the original controller: controller = controller.clone # Append the controller to the set of controller variables, updates the controller with all current instance variables. variables << controller if result = controller.process!(request, request_path) return result end end end # Controllers can directly modify relative_path, which is copied into controller_path. The controllers may have rewriten the path so we update the path info: request.env[Rack::PATH_INFO] = controller_path.to_s # No controller gave a useful result: return nil end
load_controller_file(uri_path)
click to toggle source
Loads the controller file for the given relative url_path.
# File lib/utopia/controller.rb, line 75 def load_controller_file(uri_path) base_path = File.join(@root, uri_path.components) controller_path = File.join(base_path, CONTROLLER_RB) # puts "load_controller_file(#{path.inspect}) => #{controller_path}" if File.exist?(controller_path) klass = Class.new(@base) # base_path is expected to be a string representing a filesystem path: klass.const_set(:BASE_PATH, base_path.freeze) # uri_path is expected to be an instance of Path: klass.const_set(:URI_PATH, uri_path.dup.freeze) klass.const_set(:CONTROLLER, self) klass.class_eval(File.read(controller_path), controller_path) # We lock down the controller class to prevent unsafe modifications: klass.freeze # Create an instance of the controller: return klass.new else return nil end end
lookup_controller(path)
click to toggle source
Fetch the controller for the given relative path. May be cached.
# File lib/utopia/controller.rb, line 68 def lookup_controller(path) @controller_cache.fetch_or_store(path.to_s) do load_controller_file(path) end end