Class: Raw::Dispatcher

Inherits:
Object
  • Object
show all
Defined in:
lib/raw/dispatcher.rb,
lib/raw/dispatcher/mounter.rb

Overview

The Dispatcher manages a set of controllers. It selects the appropriate Controller and action to handle the given request.

This dispatcher intelligently handles RESTful uris according to the following scheme:

GET /links GET /links/index Link::Controller#index POST /links POST /links/create Link::Controller#create

GET /links;new GET /links/new Link::Controller#new GET /links/1 Link::Controller#view(1) GET /links/1;edit GET /links/edit/1 Link::Controller#edit(1) PUT /links/1 POST /links/update/1 Link::Controller#update DELETE /links/1 GET /links/delete/1 Link::Controller#delete(1) GET /links/index.xml Link::Controller#index # Atom GET /links/index.json Link::Controller#index # JSON

The default actions for the various methods are:

 GET: index
POST: create
 PUT: update

DELETE: delete

Defined Under Namespace

Classes: Mounter

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(controller_or_map = nil) ⇒ Dispatcher

Initialize the dispatcher.



49
50
51
52
53
54
55
56
57
58
# File 'lib/raw/dispatcher.rb', line 49

def initialize(controller_or_map = nil)
  @controllers = {} 
  @formats = Nitro::STANDARD_FORMATS.dup
  
  if controller_or_map.is_a?(Class)
    mount("/" => controller_or_map)
  elsif controller_or_map.is_a?(Hash)
    mount(controller_or_map)
  end 
end

Instance Attribute Details

#controllersObject

The hash that maps mount paths to controllers.



41
42
43
# File 'lib/raw/dispatcher.rb', line 41

def controllers
  @controllers
end

#formatsObject

The representation formats this dispatcher understands.



45
46
47
# File 'lib/raw/dispatcher.rb', line 45

def formats
  @formats
end

#routerObject

The (optional) router.



37
38
39
# File 'lib/raw/dispatcher.rb', line 37

def router
  @router
end

Instance Method Details

#[](path = "/") ⇒ Object

Return the controller for the given mount path.



70
71
72
# File 'lib/raw/dispatcher.rb', line 70

def [](path = "/")
  @controllers[path]
end

#[]=(path, controller) ⇒ Object

Mount a controller to the given mount path.



76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/raw/dispatcher.rb', line 76

def []=(path, controller)
  controller = resolve_controller(controller)

  # Customize the class for mounting at the given path.
  controller.mount_at(path) if controller.respond_to? :mount_at
  
  # Call the mounted callback to allow for post mount
  # initialization.
  controller.mounted(path) if controller.respond_to? :mounted

  @controllers[path] = controller
end

#dispatch(uri, method = :get) ⇒ Object

Dispatch a path given the request method. This method handles fully resolved paths (containing an extension that denotes the expected content type).

This method automatically handles ‘nice’ (seo friendly, elegant) parameters, ie:

/links/view/1

instead of

/links/view?oid=1

Output

controller, action, query_string, nice_params, extension

– Lower level, useful for testing. ++



117
118
119
120
121
122
123
124
125
126
127
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/raw/dispatcher.rb', line 117

def dispatch(uri, method = :get)
  # Extract the query string.
  
  path, query = uri.split("?", 2)

  # Try to route the path.
  
  path = @router.route(path) if @router

  # The characters after the last '.' in the path form the 
  # extension that itself represents the expected response
  # content type.
  
  ext = File.extname(path)[1..-1] || "html" 

  # The resource representation format for this request.
  
  unless format = Context.current.format = @formats.by_extension[ext]
    raise ActionError.new("Cannot respond to '#{path}' using the '#{ext}' format representation.") 
  end
  
  # Remove the extension from the path.
  
  path = path.gsub(/\.(.*)$/, '')
  
  # Try to extract the controller from the path (that may also
  # include 'nice' parameters). This algorithm tries to find 
  # the bigest substring of the path that represents a mount 
  # path for a controller.

  key = path.dup    

  while (controller = @controllers[key]).nil?
    key = key[%r{^(/.+)/.+$}, 1] || '/'
  end

  # Try to extract the controller from the path. This 
  # algorithm tries to find the bigest substring of the path 
  # that represents an action of this controller. 
  #
  # The algorithm respects action name conventions, ie
  # simple/sub/action maps to simple__sub__action.

  action = key = path.sub(%r{^#{key}}, '').gsub(%r{^/}, '').gsub(%r{/}, '__')

  while (!action.blank?) and !controller.action_or_template?(action, format)
    action = action[%r{^(.+)__.+$}, 1]
  end

  # Extract the 'nice' parameters.
  
  params = key.sub(%r{^#{action}}, '').gsub(/^__/, '').split('__')
  
  # Do we have an action?
  
  if action.blank?
    # Try to use a standard action for this http method.

    case method
      when :get
        action = "index"
        
      when :post
        action = "create"
          
      when :delete
        action = "delete"
        
      when :put
        action = "update"
    end
    
    unless controller.action_or_template?(action, format)
      raise ActionError.new("Cannot respond to '#{path}' using '#{controller}'") 
    end
  end

  return controller, "#{action}___super", query, params, ext
end

#dispatch_request(request) ⇒ Object Also known as: dispatch_context

Dispatch a request. Calls the lower level dispatch method.



91
92
93
# File 'lib/raw/dispatcher.rb', line 91

def dispatch_request(request)  
  dispatch(request.uri, request.method)
end

#mount(map) ⇒ Object

Mounts a map of controllers.



62
63
64
65
66
# File 'lib/raw/dispatcher.rb', line 62

def mount(map)
  for path, controller in map
    self[path] = controller
  end
end

#rootObject



52
53
54
55
56
# File 'lib/raw/dispatcher/mounter.rb', line 52

def root
  if controller = self["/"]
    Mounter.new(self, controller)
  end
end

#root=(controller) ⇒ Object

An alternative mounting mechanism (CherryPy like).

Example

dispatcher.root = RootController dispatcher.root.users = User::Controller dispatcher.root.users.comments = User::Comment::Controller



48
49
50
# File 'lib/raw/dispatcher/mounter.rb', line 48

def root=(controller)
  self["/"] = resolve_controller(controller)
end