Class: Utopia::Controller
- Inherits:
-
Object
- Object
- Utopia::Controller
- Defined in:
- lib/utopia/controller.rb,
lib/utopia/controller/base.rb,
lib/utopia/controller/actions.rb,
lib/utopia/controller/respond.rb,
lib/utopia/controller/rewrite.rb,
lib/utopia/controller/variables.rb
Overview
A middleware which loads controller classes and invokes functionality based on the requested path.
Defined Under Namespace
Modules: Actions, Respond, Rewrite Classes: Base, Variables
Constant Summary collapse
- CONTROLLER_RB =
The controller filename.
'controller.rb'.freeze
- CONTENT_TYPE =
HTTP::CONTENT_TYPE
Instance Attribute Summary collapse
-
#app ⇒ Object
readonly
Returns the value of attribute app.
Class Method Summary collapse
Instance Method Summary collapse
- #call(env) ⇒ Object
- #freeze ⇒ Object
-
#initialize(app, root: Utopia::default_root, base: Controller::Base) ⇒ Controller
constructor
A new instance of Controller.
-
#invoke_controllers(request) ⇒ Object
Invoke the controller layer for a given request.
-
#load_controller_file(uri_path) ⇒ Object
Loads the controller file for the given relative url_path.
-
#lookup_controller(path) ⇒ Object
Fetch the controller for the given relative path.
Constructor Details
#initialize(app, root: Utopia::default_root, base: Controller::Base) ⇒ Controller
Returns a new instance of Controller.
30 31 32 33 34 35 36 37 |
# File 'lib/utopia/controller.rb', line 30 def initialize(app, root: Utopia::default_root, base: Controller::Base) @app = app @root = root @controller_cache = Concurrent::Map.new @base = base end |
Instance Attribute Details
#app ⇒ Object (readonly)
Returns the value of attribute app.
39 40 41 |
# File 'lib/utopia/controller.rb', line 39 def app @app end |
Class Method Details
.[](request) ⇒ Object
24 25 26 |
# File 'lib/utopia/controller.rb', line 24 def self.[] request request.env[VARIABLES_KEY] end |
Instance Method Details
#call(env) ⇒ Object
124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/utopia/controller.rb', line 124 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 ⇒ Object
41 42 43 44 45 46 47 48 |
# File 'lib/utopia/controller.rb', line 41 def freeze return self if frozen? @root.freeze @base.freeze super end |
#invoke_controllers(request) ⇒ Object
Invoke the controller layer for a given request. The request path may be rewritten.
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/utopia/controller.rb', line 88 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) ⇒ Object
Loads the controller file for the given relative url_path.
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/utopia/controller.rb', line 58 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) ⇒ Object
Fetch the controller for the given relative path. May be cached.
51 52 53 54 55 |
# File 'lib/utopia/controller.rb', line 51 def lookup_controller(path) @controller_cache.fetch_or_store(path.to_s) do load_controller_file(path) end end |