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
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: nil, cache_controllers: false, base: nil) ⇒ 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: nil, cache_controllers: false, base: nil) ⇒ Controller
Returns a new instance of Controller.
57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/utopia/controller.rb', line 57 def initialize(app, root: nil, cache_controllers: false, base: nil) @app = app @root = root || Utopia::default_root if cache_controllers @controller_cache = Concurrent::Map.new else @controller_cache = nil end warn "Controller middleware is automatically prepending Actions! Will be deprecated in 2.x" if $VERBOSE and base.nil? @base = base || Controller::Base.dup.prepend(Controller::Actions) end |
Instance Attribute Details
#app ⇒ Object (readonly)
Returns the value of attribute app.
72 73 74 |
# File 'lib/utopia/controller.rb', line 72 def app @app end |
Class Method Details
.[](request) ⇒ Object
53 54 55 |
# File 'lib/utopia/controller.rb', line 53 def self.[] request request.env[VARIABLES_KEY] end |
Instance Method Details
#call(env) ⇒ Object
164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/utopia/controller.rb', line 164 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
74 75 76 77 78 79 80 81 |
# File 'lib/utopia/controller.rb', line 74 def freeze @root.freeze # Should we freeze the base class? # @base.freeze super end |
#invoke_controllers(request) ⇒ Object
Invoke the controller layer for a given request. The request path may be rewritten.
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/utopia/controller.rb', line 128 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.
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 123 124 125 |
# File 'lib/utopia/controller.rb', line 95 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) # Give the controller a useful name: # Controllers.define(klass) # 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.
84 85 86 87 88 89 90 91 92 |
# File 'lib/utopia/controller.rb', line 84 def lookup_controller(path) if @controller_cache @controller_cache.fetch_or_store(path.to_s) do load_controller_file(path) end else load_controller_file(path) end end |