Module: Waves::Mapping

Defined in:
lib/mapping/mapping.rb,
lib/mapping/pretty_urls.rb

Overview

Waves::Mapping is a mixin for defining Waves URI mappings (mapping a request to Ruby code). Mappings can work against the request url, path, and elements of the request (such as the request method or accept header). Mappings may also include before, after, or wrap filters to be run if they match the request. Mappings are created using an appropriate mapping method along with a URL pattern (a string or regular expression), a hash of constraint options, and a block, which is the code to run if the pattern matches.

Examples

resource = '([\w\-]+)'
name = '([\w\-\_\.\+\@]+)'

path %r{^/#{resource}/#{name}/?$} do |resource, name|
  "Hello from a #{resource} named #{name.capitalize}."
end

In this example, we are using binding regular expressions defined by resource and name. The matches are passed into the block as parameters. Thus, this rule, given the URL ‘/person/john’ will return:

Hello from a person named John.

The given block may simple return a string. The content type is inferred from the request if possible, otherwise it defaults to text/html.

path '/critters', :method => :post do
  request.content_type
end

/critters # => 'text/html'

In this example, we match against a string and check to make sure that the request is a POST. If so, we return the request content_type. The request (and response) objects are available from within the block implicitly.

Invoking Controllers and Views

You may invoke a controller or view method for the primary application by using the corresponding methods, preceded by the use directive.

Examples

path %r{^/#{resource}/#{name}/?$} do |resource, name|
  resource( resource ) do
    controller { find( name ) } |  view { | instance | show( resource => instance ) }
  end
end

In this example, we take the same rule from above but invoke a controller and view method. We use the resource directive and the resource parameter to set the MVC instances we’re going to use. This is necessary to use the controller or view methods. Each of these take a block as arguments which are evaluated in the context of the instance. The view method can further take an argument which is “piped” from the result of the controller block. This isn’t required, but helps to clarify the request processing. Within a view block, a hash may also be passed in to the view method, which is converted into instance variables for the view instance. In this example, the show method is assigned to an instance variable with the same name as the resource type.

So given the same URL as above - /person/john - what will happen is the find method for the Person controller will be invoked and the result passed to the Person view’s show method, with @person holding the value returned.

Crucially, the controller does not need to know what variables the view depends on. This is the job of the mapping block, to act as the “glue” between the controller and view. The controller and view can thus be completely decoupled and become easier to reuse separately.

url 'http://admin.foobar.com:/' do
  resource( :admin ) { view { console } }
end

In this example, we are using the url method to map a subdomain of foobar.com to the console method of the Admin view. In this case, we did not need a controller method, so we simply didn’t call one.

Mapping Modules

You may encapsulate sets of related rules into modules and simply include them into your mapping module. Some rule sets come packaged with Waves, such as PrettyUrls (rules for matching resources using names instead of ids). The simplest way to define such modules for reuse is by defining the included class method for the rules module, and then define the rules using module_eval. See the PrettyUrls module for an example of how to do this.

Important: Using pre-packaged mapping rules does not prevent you from adding to or overriding these rules. However, order does matter, so you should put your own rules ahead of those your may be importing. Also, place rules with constraints (for example, rules that require a POST) ahead of those with no constraints, otherwise the constrainted rules may never be called.

Defined Under Namespace

Modules: PrettyUrls

Instance Method Summary collapse

Instance Method Details

#[](request) ⇒ Object

Match the given request against the defined rules. This is typically only called by a dispatcher object, so you shouldn’t typically use it directly.



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/mapping/mapping.rb', line 192

def []( request )

  rx = { :before => [], :after => [], :action => nil, :handlers => [] }

  ( filters[:before] + filters[:wrap] ).each do | options, function |
    matches = match( request, options, function )
    rx[:before] << matches if matches
  end

  mapping.find do | options, params, function |
    rx[:action] = match( request, options, function )
    break if rx[:action]
  end

  ( filters[:after] + filters[:wrap] ).each do | options, function |
    matches = match( request, options, function )
    rx[:after] << matches if matches
  end

  handlers.each do | exception, options, function |
    matches = match( request, options, function )
    rx[:handlers] << matches.unshift(exception) if matches
  end

  return rx
end

#after(path, options = {}, &block) ⇒ Object

Similar to before, except it runs its actions after any matching url or path actions.



107
108
109
110
111
112
113
114
# File 'lib/mapping/mapping.rb', line 107

def after( path, options = {}, &block )
  if path.is_a? Hash
    options = path
  else
    options[:path] = path
  end
  filters[:after] << [ options, block ]
end

#before(path, options = {}, &block) ⇒ Object

If the pattern matches and constraints given by the options hash are satisfied, run the block before running any path or url actions. You can have as many before matches as you want - they will all run, unless one of them calls redirect, generates an unhandled exception, etc.



97
98
99
100
101
102
103
104
# File 'lib/mapping/mapping.rb', line 97

def before( path, options = {}, &block )
  if path.is_a? Hash
    options = path
  else
    options[:path] = path
  end
  filters[:before] << [ options, block ]
end

#clearObject

Clear all mapping rules



220
221
222
# File 'lib/mapping/mapping.rb', line 220

def clear
  @mapping = @filters = nil;
end

#handle(exception, options = {}, &block) ⇒ Object

Maps an exception handler to a block.



160
161
162
# File 'lib/mapping/mapping.rb', line 160

def handle(exception, options = {}, &block )
  handlers << [exception,options, block]
end

#map(path, options = {}, params = {}, &block) ⇒ Object

Maps a request to a block. Don’t use this method directly unless you know what you’re doing. Use path or url instead.



129
130
131
132
133
134
135
136
137
# File 'lib/mapping/mapping.rb', line 129

def map( path, options = {}, params = {}, &block )
  if path.is_a? Hash
    params = options
    options = path
  else
    options[:path] = path
  end
  mapping << [ options, params, block ]
end

#path(pat, options = {}, params = {}, &block) ⇒ Object

Match pattern against the request.path, along with satisfying any constraints specified by the options hash. If the pattern matches and the constraints are satisfied, run the block. Only one path or url match will be run (the first one).



142
143
144
# File 'lib/mapping/mapping.rb', line 142

def path( pat, options = {}, params = {}, &block )
  options[:path] = pat; map( options, params, &block )
end

#root(options = {}, params = {}, &block) ⇒ Object

Maps the root of the application to a block. If an options hash is specified it must satisfy those constraints in order to run the block.



155
156
157
# File 'lib/mapping/mapping.rb', line 155

def root( options = {}, params = {}, &block )
  path( %r{^/?$}, options, params, &block )
end

#threaded(pat, options = {}, params = {}, &block) ⇒ Object

Maps a request to a block that will be executed within it’s own thread. This is especially useful when you’re running with an event driven server like thin or ebb, and this block is going to take a relatively long time.



168
169
170
171
# File 'lib/mapping/mapping.rb', line 168

def threaded( pat, options = {}, params = {}, &block)
  params[:threaded] = true
  map( pat, options, params, &block)
end

#threaded?(request) ⇒ Boolean

Determines whether the request should be handled in a separate thread. This is used by event driven servers like thin and ebb, and is most useful for those methods that take a long time to complete, like for example upload processes. E.g.:

threaded("/upload", :method => :post) do
  handle_upload
end

You typically wouldn’t use this method directly.

Returns:

  • (Boolean)


182
183
184
185
186
187
188
# File 'lib/mapping/mapping.rb', line 182

def threaded?( request )
  mapping.find do | options, params, function |
    match = match( request, options, function )
    return params[:threaded] == true if match
  end
  return false
end

#url(pat, options = {}, params = {}, &block) ⇒ Object

Match pattern against the request.url, along with satisfying any constraints specified by the options hash. If the pattern matches and the constraints are satisfied, run the block. Only one path or url match will be run (the first one).



149
150
151
# File 'lib/mapping/mapping.rb', line 149

def url( pat, options = {}, params = {}, &block )
  options[:url] = pat; map( options, params, &block )
end

#wrap(path, options = {}, &block) ⇒ Object

Run the action before and after the matching url or path action.



117
118
119
120
121
122
123
124
125
# File 'lib/mapping/mapping.rb', line 117

def wrap( path, options = {}, &block )
  if path.is_a? Hash
    options = path
  else
    options[:path] = path
  end
  filters[:before] << [ options, block ]
  filters[:after] << [ options, block ]
end