Module: Derailleur::Application
- Included in:
- Grease
- Defined in:
- lib/derailleur/core/application.rb,
lib/derailleur/base/application.rb
Overview
In Derailleur, an application is an object extending the Application module. It will have routes which hold handlers. By default, we use Derailleur’s components, but you can easily modify your application to use custom routes’ node, HTTP methods dispatcher, and handlers.
Instance Attribute Summary collapse
-
#default_dispatcher ⇒ Object
The default HTTP method dispatcher ( Derailleur::Dispatcher ) See Derailleur::Dispatcher if you want a personal one.
-
#default_handler ⇒ Object
The default handler ( Derailleur::DefaultRackHandler ) See Derailleur::Handler to understand how to build your own.
-
#default_internal_error_handler ⇒ Object
The default error handler ( Derailleur::InternalErrorHandler ).
-
#default_root_node_type ⇒ Object
The default root node type ( Derailleur::ArrayTrie ) You could change it to ( Derailleur::HashTrie ) for better performance.
Instance Method Summary collapse
-
#build_route(path) ⇒ Object
Builds a route by appending nodes on the path.
-
#call(env) ⇒ Object
Method implemented to comply to the Rack specification.
-
#chunk_path(path) ⇒ Object
Chunks a path, splitting around ‘/’ separators there always is an empty name ‘foo/bar’ => [”, ‘foo’, ‘bar’].
-
#default_node_type ⇒ Object
The default node type (is the node_type of the default_root_node_type ).
-
#delete(path, content = nil, params = {}, &blk) ⇒ Object
registers a handler for the DELETE HTTP method see get for the use of parameters.
-
#get(path, content = nil, params = {}, &blk) ⇒ Object
registers a handler for the GET HTTP method the handler is either content OR the passed block if content is true and there is a block, then the handler will be the content params is a hash of parameters, the only parameter supported is: * :overwrite , if true, then you can rewrite a handler.
-
#get_route(path) ⇒ Object
Same as get_route_silent but raise a NoSuchRoute error if there is no matching route.
-
#get_route_silent(path) ⇒ Object
Return the node corresponding to a given path.
-
#get_route_with_params(path) ⇒ Object
Similar to get route, but also interprets nodes names as keys for a hash.
-
#head(path, content = nil, params = {}, &blk) ⇒ Object
registers a handler for the HEAD HTTP method see get for the use of parameters.
-
#normalize(path) ⇒ Object
Normalize a path by making sure it starts with ‘/’.
-
#post(path, content = nil, params = {}, &blk) ⇒ Object
registers a handler for the POST HTTP method see get for the use of parameters.
-
#put(path, content = nil, params = {}, &blk) ⇒ Object
registers a handler for the PUT HTTP method see get for the use of parameters.
-
#register_route(path, method = :default, handler = nil, params = {}, &blk) ⇒ Object
Registers an handler for a given path.
-
#routes ⇒ Object
An object representing the routes.
-
#split_to!(path, app) ⇒ Object
Split a whole branch of the application at the given path, and graft the branch to the app in second parameter.
-
#undelete(path) ⇒ Object
removes a handler for the DELETE HTTP method.
-
#unget(path) ⇒ Object
removes a handler for the GET HTTP method.
-
#unhead(path) ⇒ Object
removes a handler for the HEAD HTTP method.
-
#unpost(path) ⇒ Object
removes a handler for the POST HTTP method.
-
#unput(path) ⇒ Object
removes a handler for the PUT HTTP method.
-
#unregister_route(path, method = :default) ⇒ Object
Removes an handler for a path/method pair.
Instance Attribute Details
#default_dispatcher ⇒ Object
The default HTTP method dispatcher ( Derailleur::Dispatcher ) See Derailleur::Dispatcher if you want a personal one
45 46 47 |
# File 'lib/derailleur/core/application.rb', line 45 def default_dispatcher @default_dispatcher ||= Dispatcher end |
#default_handler ⇒ Object
The default handler ( Derailleur::DefaultRackHandler ) See Derailleur::Handler to understand how to build your own
39 40 41 |
# File 'lib/derailleur/core/application.rb', line 39 def default_handler @default_handler ||= DefaultRackHandler end |
#default_internal_error_handler ⇒ Object
The default error handler ( Derailleur::InternalErrorHandler )
20 21 22 |
# File 'lib/derailleur/core/application.rb', line 20 def default_internal_error_handler @default_internal_error_handler ||= InternalErrorHandler end |
#default_root_node_type ⇒ Object
The default root node type ( Derailleur::ArrayTrie ) You could change it to ( Derailleur::HashTrie ) for better performance. The rule of thumb is: benchmark your application with both and pick the best one.
28 29 30 |
# File 'lib/derailleur/core/application.rb', line 28 def default_root_node_type @default_root_node_type ||= ArrayTrie end |
Instance Method Details
#build_route(path) ⇒ Object
Builds a route by appending nodes on the path. The implementation of nodes should create missing nodes on the path. See ArrayTrieNode#<< or HashTrieNode#<< Returns the last node for this path
70 71 72 73 74 75 76 |
# File 'lib/derailleur/core/application.rb', line 70 def build_route(path) current_node = routes chunk_path(path).each do |chunk| current_node = current_node << chunk end current_node end |
#call(env) ⇒ Object
Method implemented to comply to the Rack specification. see rack.rubyforge.org/doc/files/SPEC.html to understand what to return.
If everything goes right, an instance of default_handler will serve the request.
The routing handler will be created with three params
-
the application handler contained in the dispatcher
-
the Rack env
-
a context hash with three keys:
-
‘derailleur’ at self, i.e., a reference to the application
-
‘derailleur.params’ with the parameters/spalt in the path
-
‘derailleur.node’ the node responsible for handling this path
-
If there is any exception during this, it will be catched and the default_internal_error_handler will be called.
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/derailleur/core/application.rb', line 207 def call(env) begin path = env['PATH_INFO'].sub(/\.\w+$/,'') #ignores the extension if any ctx = {} route, params = get_route_with_params(path) ctx['derailleur.node'] = route ctx['derailleur.params'] = params ctx['derailleur'] = self dispatcher = route.content raise NoSuchRoute, "no dispatcher for #{path}" if dispatcher.nil? handler = dispatcher.send(env['REQUEST_METHOD']) raise NoSuchRoute, "no handler for valid path: #{path}" if handler.nil? default_handler.new(handler, env, ctx).to_rack_output rescue Exception => err default_internal_error_handler.new(err, env, ctx).to_rack_output end end |
#chunk_path(path) ⇒ Object
Chunks a path, splitting around ‘/’ separators there always is an empty name ‘foo/bar’ => [”, ‘foo’, ‘bar’]
62 63 64 |
# File 'lib/derailleur/core/application.rb', line 62 def chunk_path(path) normalize(path).split('/') end |
#default_node_type ⇒ Object
The default node type (is the node_type of the default_root_node_type )
33 34 35 |
# File 'lib/derailleur/core/application.rb', line 33 def default_node_type default_root_node_type.node_type end |
#delete(path, content = nil, params = {}, &blk) ⇒ Object
registers a handler for the DELETE HTTP method see get for the use of parameters
55 56 57 |
# File 'lib/derailleur/base/application.rb', line 55 def delete(path, content=nil, params={}, &blk) register_route(path, :DELETE, content, params, &blk) end |
#get(path, content = nil, params = {}, &blk) ⇒ Object
registers a handler for the GET HTTP method the handler is either content OR the passed block if content is true and there is a block, then the handler will be the content params is a hash of parameters, the only parameter supported is:
-
:overwrite , if true, then you can rewrite a handler
11 12 13 |
# File 'lib/derailleur/base/application.rb', line 11 def get(path, content=nil, params={}, &blk) register_route(path, :GET, content, params, &blk) end |
#get_route(path) ⇒ Object
Same as get_route_silent but raise a NoSuchRoute error if there is no matching route.
96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/derailleur/core/application.rb', line 96 def get_route(path) node = if block_given? get_route_silent(path) do |node,chunk| yield node, chunk end else get_route_silent(path) end raise NoSuchRoute, "no such path #{path}" unless node node end |
#get_route_silent(path) ⇒ Object
Return the node corresponding to a given path.
Will (optionally) consecutively yield all the [node, chunk_name] this is useful when you want to interpret the members of the path as a parameter.
82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/derailleur/core/application.rb', line 82 def get_route_silent(path) current_node = routes chunk_path(path).each do |chunk| unless current_node.absorbent? current_node = current_node.child_for_name(chunk) return nil unless current_node end yield current_node, chunk if block_given? end current_node end |
#get_route_with_params(path) ⇒ Object
Similar to get route, but also interprets nodes names as keys for a hash. The values in the parameters hash are the string corresponding to the nodes in the path. A specific key is :splat, which correspond to the remaining chunks in the paths. Does NOT take care of key collisions. This should be taken care of at the application level.
178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/derailleur/core/application.rb', line 178 def get_route_with_params(path) params = {:splat => []} route = get_route(path) do |node, val| if node.wildcard? params[node.name] = val elsif node.absorbent? params[:splat] << val end end [route, params] end |
#head(path, content = nil, params = {}, &blk) ⇒ Object
registers a handler for the HEAD HTTP method see get for the use of parameters
22 23 24 |
# File 'lib/derailleur/base/application.rb', line 22 def head(path, content=nil, params={}, &blk) register_route(path, :HEAD, content, params, &blk) end |
#normalize(path) ⇒ Object
Normalize a path by making sure it starts with ‘/’
55 56 57 |
# File 'lib/derailleur/core/application.rb', line 55 def normalize(path) File.join('/', path) end |
#post(path, content = nil, params = {}, &blk) ⇒ Object
registers a handler for the POST HTTP method see get for the use of parameters
33 34 35 |
# File 'lib/derailleur/base/application.rb', line 33 def post(path, content=nil, params={}, &blk) register_route(path, :POST, content, params, &blk) end |
#put(path, content = nil, params = {}, &blk) ⇒ Object
registers a handler for the PUT HTTP method see get for the use of parameters
44 45 46 |
# File 'lib/derailleur/base/application.rb', line 44 def put(path, content=nil, params={}, &blk) register_route(path, :PUT, content, params, &blk) end |
#register_route(path, method = :default, handler = nil, params = {}, &blk) ⇒ Object
Registers an handler for a given path. The path will be interpreted as an absolute path prefixed by ‘/’ .
Usually you will not use this method but a method from the base/application.rb code (with the name of the HTTP method: e.g. get post put)
The method argument is the HTTP method name as a symbol (e.g. :GET) (handler || blk) is the handler set. i.e., if there’s both a handler and a block, the block will be ignored.
Params is hash of parameters, currently, the only key looked at is :overwrite to overwrite an handler for an existing path/method pair.
Internally, the path will be created node by node when nodes for this path are missing. A default_dispatcher will be here to map the various HTTP methods for the same path to their respective handlers.
127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/derailleur/core/application.rb', line 127 def register_route(path, method=:default, handler=nil, params={}, &blk) if path == '*' raise ArgumentError.new("Cannot register on #{path} because of ambiguity, in Derailleur, '*' translated'/*' would not catch the path '/' like '/foo/*' doesn't catch '/foo'") end handler = handler || blk node = build_route(path) node.content ||= default_dispatcher.new if (params[:overwrite]) or (not node.content.has_handler?(method)) node.content.set_handler(method, handler) else raise RouteObjectAlreadyPresent, "could not overwrite #{method} handler at path #{path}" end node end |
#routes ⇒ Object
An object representing the routes. Usually, it is the root of a Trie
50 51 52 |
# File 'lib/derailleur/core/application.rb', line 50 def routes @routes ||= default_root_node_type.new end |
#split_to!(path, app) ⇒ Object
Split a whole branch of the application at the given path, and graft the branch to the app in second parameter. This method does NOT prevents you from cancelling handlers in the second app if any. Because it does not check for handlers in the receiving branch. Use with care. See ArrayNode#graft!
162 163 164 165 166 167 168 169 |
# File 'lib/derailleur/core/application.rb', line 162 def split_to!(path, app) app_node = app.build_route(path) split_node = get_route(normalize(path)) split_node.prune! app_node.graft!(split_node) end |
#undelete(path) ⇒ Object
removes a handler for the DELETE HTTP method
60 61 62 |
# File 'lib/derailleur/base/application.rb', line 60 def undelete(path) unregister_route(path, :DELETE) end |
#unget(path) ⇒ Object
removes a handler for the GET HTTP method
16 17 18 |
# File 'lib/derailleur/base/application.rb', line 16 def unget(path) unregister_route(path, :GET) end |
#unhead(path) ⇒ Object
removes a handler for the HEAD HTTP method
27 28 29 |
# File 'lib/derailleur/base/application.rb', line 27 def unhead(path) unregister_route(path, :HEAD) end |
#unpost(path) ⇒ Object
removes a handler for the POST HTTP method
38 39 40 |
# File 'lib/derailleur/base/application.rb', line 38 def unpost(path) unregister_route(path, :POST) end |
#unput(path) ⇒ Object
removes a handler for the PUT HTTP method
49 50 51 |
# File 'lib/derailleur/base/application.rb', line 49 def unput(path) unregister_route(path, :PUT) end |
#unregister_route(path, method = :default) ⇒ Object
Removes an handler for a path/method pair. The path will be interpreted as an absolute path prefixed with ‘/’
144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/derailleur/core/application.rb', line 144 def unregister_route(path, method=:default) node = get_route(normalize(path)) if node.children.empty? if node.content.no_handler? node.prune! else node.content.set_handler(method, nil) end else node.hand_off_to! default_node_type.new(node.name) end end |