-
Notifications
You must be signed in to change notification settings - Fork 54
/
dispatcher.rb
99 lines (85 loc) · 3 KB
/
dispatcher.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
require 'forwardable'
require 'webmachine/decision'
require 'webmachine/dispatcher/route'
module Webmachine
# Handles dispatching incoming requests to the proper registered
# resources and initializing the decision logic.
class Dispatcher
WM_DISPATCH = 'wm.dispatch'.freeze
# @return [Array<Route>] the list of routes that will be
# dispatched to
# @see #add_route
attr_reader :routes
# The creator for resources used to process requests.
# Must respond to call(route, request, response) and return
# a newly created resource instance.
attr_accessor :resource_creator
# Initialize a Dispatcher instance
# @param resource_creator Invoked to create resource instances.
def initialize(resource_creator = method(:create_resource))
@routes = []
@resource_creator = resource_creator
end
# Adds a route to the dispatch list. Routes will be matched in the
# order they are added.
# @see Route#new
def add_route(*args, &block)
route = Route.new(*args, &block)
@routes << route
route
end
alias_method :add, :add_route
# Dispatches a request to the appropriate {Resource} in the
# dispatch list. If a matching resource is not found, a "404 Not
# Found" will be rendered.
# @param [Request] request the request object
# @param [Response] response the response object
def dispatch(request, response)
if resource = find_resource(request, response)
Webmachine::Events.instrument(WM_DISPATCH) do |payload|
Webmachine::Decision::FSM.new(resource, request, response).run
payload[:resource] = resource.class.name
payload[:request] = request.dup
payload[:code] = response.code
end
else
Webmachine.render_error(404, request, response)
end
end
# Resets, removing all routes. Useful for testing or reloading the
# application.
def reset
@routes.clear
end
# Find the first resource that matches an incoming request
# @param [Request] request the request to match
# @param [Response] response the response for the resource
def find_resource(request, response)
if route = find_route(request)
prepare_resource(route, request, response)
end
end
# Find the first route that matches an incoming request
# @param [Request] request the request to match
def find_route(request)
@routes.find { |r| r.match?(request) }
end
private
def prepare_resource(route, request, response)
route.apply(request)
@resource_creator.call(route, request, response)
end
def create_resource(route, request, response)
route.resource.new(request, response)
end
end
# Evaluates the passed block in the context of
# {Webmachine::Dispatcher} for use in adding a number of routes at
# once.
# @return [Webmachine] self
# @see Webmachine::Dispatcher#add_route
def self.routes(&block)
application.routes(&block)
self
end
end # module Webmachine