Class: Merb::Router::Behavior

Inherits:
Object
  • Object
show all
Defined in:
lib/merb-core/dispatch/router/behavior.rb

Overview

The Behavior class is an interim route-building class that ties pattern-matching conditions to output parameters, params.


Constant Summary collapse

@@parent_resources =
[]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(conditions = {}, params = {}, parent = nil) ⇒ Behavior

Parameters

conditions<Hash>

Conditions to be met for this behavior to take effect.

params<Hash>

Hash describing the course action to take (Behavior) when the conditions match. The values of the params keys must be Strings.

parent<Behavior, Nil>

The parent of this Behavior. Defaults to nil.



73
74
75
76
77
78
79
80
81
# File 'lib/merb-core/dispatch/router/behavior.rb', line 73

def initialize(conditions = {}, params = {}, parent = nil)
  # Must wait until after deducing placeholders to set @params !
  @conditions, @params, @parent = conditions, {}, parent
  @placeholders = {}
  stringify_conditions
  copy_original_conditions
  deduce_placeholders
  @params.merge! params
end

Instance Attribute Details

#conditionsObject (readonly)

Returns the value of attribute conditions.



10
11
12
# File 'lib/merb-core/dispatch/router/behavior.rb', line 10

def conditions
  @conditions
end

#paramsObject (readonly)

Returns the value of attribute params.



10
11
12
# File 'lib/merb-core/dispatch/router/behavior.rb', line 10

def params
  @params
end

#parentObject

Returns the value of attribute parent.



11
12
13
# File 'lib/merb-core/dispatch/router/behavior.rb', line 11

def parent
  @parent
end

#placeholdersObject (readonly)

Returns the value of attribute placeholders.



10
11
12
# File 'lib/merb-core/dispatch/router/behavior.rb', line 10

def placeholders
  @placeholders
end

#redirect_statusObject (readonly)

Returns the value of attribute redirect_status.



10
11
12
# File 'lib/merb-core/dispatch/router/behavior.rb', line 10

def redirect_status
  @redirect_status
end

#redirect_urlObject (readonly)

Returns the value of attribute redirect_url.



10
11
12
# File 'lib/merb-core/dispatch/router/behavior.rb', line 10

def redirect_url
  @redirect_url
end

Class Method Details

.array_to_code(arr) ⇒ Object

Parameters

arr<Array>

The array to convert to a code string.

Returns

String

The arr’s elements converted to string and joined with “ + ”, with any string elements surrounded by quotes.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/merb-core/dispatch/router/behavior.rb', line 48

def array_to_code(arr)
  code = ''
  arr.each_with_index do |part, i|
    code << ' + ' if i > 0
    case part
    when Symbol
      code << part.to_s
    when String
      code << %{"#{part}"}
    else
      raise "Don't know how to compile array part: #{part.class} [#{i}]"
    end
  end
  code
end

.concat_without_endcaps(string1, string2) ⇒ Object

Parameters

string1<String>

The string to concatenate with.

string2<String>

The string to concatenate.

Returns

String

the concatenated string with regexp end caps removed.



32
33
34
35
36
37
38
39
# File 'lib/merb-core/dispatch/router/behavior.rb', line 32

def concat_without_endcaps(string1, string2)
  return nil if !string1 and !string2
  return string1 if string2.nil?
  return string2 if string1.nil?
  s1 = string1[-1] == ?$ ? string1[0..-2] : string1
  s2 = string2[0] == ?^ ? string2[1..-1] : string2
  s1 + s2
end

.count_parens_up_to(string, pos) ⇒ Object

Parameters

string<String>

The string in which to count parentheses.

pos<Fixnum>

The last character for counting.

Returns

Fixnum

The number of open parentheses in string, up to and including pos.



22
23
24
# File 'lib/merb-core/dispatch/router/behavior.rb', line 22

def count_parens_up_to(string, pos)
  string[0..pos].gsub(/[^\(]/, '').size
end

Instance Method Details

#add(path, params = {}) ⇒ Object

Register a new route.

Parameters

path<String, Regex>

The url path to match

params<Hash>

The parameters the new routes maps to.

Returns

Route

The resulting Route.




93
94
95
# File 'lib/merb-core/dispatch/router/behavior.rb', line 93

def add(path, params = {})
  match(path).to(params)
end

#ancestorsObject



666
667
668
# File 'lib/merb-core/dispatch/router/behavior.rb', line 666

def ancestors
  @ancestors ||= find_ancestors
end

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

Creates the most common routes /:controller/:action/:id.format when called with no arguments. You can pass a hash or a block to add parameters or override the default behavior.

Parameters

params<Hash>

This optional hash can be used to augment the default settings

&block

When passing a block a new behavior is yielded and more refinement is possible.

Returns

Route

the default route

Examples

# Passing an extra parameter "mode" to all matches
r.default_routes :mode => "default"

# specifying exceptions within a block
r.default_routes do |nr|
  nr.defer_to do |request, params|
    nr.match(:protocol => "http://").to(:controller => "login",
      :action => "new") if request.env["REQUEST_URI"] =~ /\/private\//
  end
end



304
305
306
# File 'lib/merb-core/dispatch/router/behavior.rb', line 304

def default_routes(params = {}, &block)
  match(%r{/:controller(/:action(/:id)?)?(\.:format)?}).to(params, &block)
end

#defer_to(params = {}, &conditional_block) ⇒ Object

Takes a block and stores it for deferred conditional routes. The block takes the request object and the params hash as parameters.

Parameters

params<Hash>

Parameters and conditions associated with this behavior.

&conditional_block

A block with the conditions to be met for the behavior to take effect.

Returns

Route

The default route.

Examples

r.defer_to do |request, params|
  params.merge :controller => 'here',
    :action => 'there' if request.xhr?
end



271
272
273
# File 'lib/merb-core/dispatch/router/behavior.rb', line 271

def defer_to(params = {}, &conditional_block)
  to_route(params, &conditional_block).register
end

#inspectObject

Returns

String

A human readable form of the behavior.



643
644
645
# File 'lib/merb-core/dispatch/router/behavior.rb', line 643

def inspect
  "[captures: #{path_captures.inspect}, conditions: #{@original_conditions.inspect}, params: #{@params.inspect}, placeholders: #{@placeholders.inspect}]"
end

#match(path = '', conditions = {}, &block) ⇒ Object

Matches a path and any number of optional request methods as conditions of a route. Alternatively, path can be a hash of conditions, in which case conditions ignored.

Parameters

path<String, Regexp>

When passing a string as path you’re defining a literal definition for your route. Using a colon, ex.: “:login”, defines both a capture and a named param. When passing a regular expression you can define captures explicitly within the regular expression syntax. path is optional.

conditions<Hash>

Addational conditions that the request must meet in order to match. the keys must be methods that the Merb::Request instance will respond to. The value is the string or regexp that matched the returned value. Conditions are inherited by child routes.

The Following have special meaning:

  • :method – Limit this match based on the request method. (GET, POST, PUT, DELETE)

  • :path – Used internally to maintain URL form information

  • :controller and :action – These can be used here instead of ‘#to’, and will be inherited in the block.

  • :params – Sets other key/value pairs that are placed in the params hash. The value must be a hash.

&block

Passes a new instance of a Behavior object into the optional block so that sub-matching and routes nesting may occur.

Returns

Behavior

A new instance of Behavior with the specified path and conditions.

Tip: When nesting always make sure the most inner sub-match registers a Route and doesn’t just returns new Behaviors.

Examples

# registers /foo/bar to controller => "foo", :action => "bar"
# and /foo/baz to controller => "foo", :action => "baz"
r.match "/foo", :controller=>"foo" do |f|
  f.match("/bar").to(:action => "bar")
  f.match("/baz").to(:action => "caz")
end

#match only of the browser string contains MSIE or Gecko
r.match ('/foo', :user_agent => /(MSIE|Gecko)/ )
     .to({:controller=>'foo', :action=>'popular')

# Route GET and POST requests to different actions (see also #resources)
r.match('/foo', :method=>:get).to(:action=>'show')
r.mathc('/foo', :method=>:post).to(:action=>'create')

# match also takes regular expressions

r.match(%r[/account/([a-z]{4,6})]).to(:controller => "account",
   :action => "show", :id => "[1]")

r.match(/\/?(en|es|fr|be|nl)?/).to(:language => "[1]") do |l|
  l.match("/guides/:action/:id").to(:controller => "tour_guides")
end



162
163
164
165
166
167
168
169
# File 'lib/merb-core/dispatch/router/behavior.rb', line 162

def match(path = '', conditions = {}, &block)
  if path.is_a? Hash
    conditions = path
  else
    conditions[:path] = path
  end
  match_without_path(conditions, &block)
end

#match!(path = '', conditions = {}, &block) ⇒ Object

Combines common case of match being used with to({}).

Returns

<Route>

route that uses params from named path segments.

Examples

r.match!(“/api/:token/:controller/:action/:id”)

is the same thing as

r.match!(“/api/:token/:controller/:action/:id”).to({})



214
215
216
# File 'lib/merb-core/dispatch/router/behavior.rb', line 214

def match!(path = '', conditions = {}, &block)
  self.match(path, conditions, &block).to({})
end

#match_without_path(conditions = {}) {|new_behavior| ... } ⇒ Object

Generates a new child behavior without the path if the path matches an empty string. Yields the new behavior to a block.

Parameters

conditions<Hash>

Optional conditions to pass to the new route.

Block parameters

new_behavior<Behavior>

The child behavior.

Returns

Behavior

The new behavior.

Yields:

  • (new_behavior)


182
183
184
185
186
187
188
189
# File 'lib/merb-core/dispatch/router/behavior.rb', line 182

def match_without_path(conditions = {})
  params = conditions.delete(:params) || {} #parents params will be merged  in Route#new
  params[:controller] = conditions.delete(:controller) if conditions[:controller]
  params[:action] = conditions.delete(:action) if conditions[:action]
  new_behavior = self.class.new(conditions, params, self)
  yield new_behavior if block_given?
  new_behavior
end

#merged_conditionsObject

Returns

Hash

The conditions of this behavior merged with the conditions of all its ancestors.



601
602
603
604
605
606
607
608
609
610
611
612
# File 'lib/merb-core/dispatch/router/behavior.rb', line 601

def merged_conditions
  if parent.nil?
    @conditions
  else
    merged_so_far = parent.merged_conditions
    if path = Behavior.concat_without_endcaps(merged_so_far[:path], @conditions[:path])
      merged_so_far.merge(@conditions).merge(:path => path)
    else
      merged_so_far.merge(@conditions)
    end
  end
end

#merged_original_conditionsObject

Returns

Hash

The original conditions of this behavior merged with the original conditions of all its ancestors.



584
585
586
587
588
589
590
591
592
593
594
595
# File 'lib/merb-core/dispatch/router/behavior.rb', line 584

def merged_original_conditions
  if parent.nil?
    @original_conditions
  else
    merged_so_far = parent.merged_original_conditions
    if path = Behavior.concat_without_endcaps(merged_so_far[:path], @original_conditions[:path])
      merged_so_far.merge(@original_conditions).merge(:path => path)
    else
      merged_so_far.merge(@original_conditions)
    end
  end
end

#merged_paramsObject

Returns

Hash

The params of this behavior merged with the params of all its ancestors.



618
619
620
621
622
623
624
# File 'lib/merb-core/dispatch/router/behavior.rb', line 618

def merged_params
  if parent.nil?
    @params
  else
    parent.merged_params.merge(@params)
  end
end

#merged_placeholdersObject

Returns

Hash

The route placeholders, e.g. :controllers, of this behavior merged with the placeholders of all its ancestors.



630
631
632
633
634
635
636
637
638
639
# File 'lib/merb-core/dispatch/router/behavior.rb', line 630

def merged_placeholders
  placeholders = {}
  (ancestors.reverse + [self]).each do |a|
    a.placeholders.each_pair do |k, pair|
      param, place = pair
      placeholders[k] = [param, place + (param == :path ? a.total_previous_captures : 0)]
    end
  end
  placeholders
end

#namespace(name_or_path, options = {}, &block) ⇒ Object

Creates a namespace for a route. This way you can have logical separation to your routes.

Parameters

name_or_path<String, Symbol>

The name or path of the namespace.

options<Hash>

Optional hash, set :path if you want to override what appears on the url

&block

A new Behavior instance is yielded in the block for nested resources.

Block parameters

r<Behavior>

The namespace behavior object.

Examples

r.namespace :admin do |admin|
  admin.resources :accounts
  admin.resource :email
end

# /super_admin/accounts
r.namespace(:admin, :path=>"super_admin") do |admin|
  admin.resources :accounts
end



332
333
334
335
336
337
# File 'lib/merb-core/dispatch/router/behavior.rb', line 332

def namespace(name_or_path, options={}, &block)
  path = options[:path] || name_or_path.to_s
  (path.empty? ? self : match("/#{path}")).to(:namespace => name_or_path.to_s) do |r|
    yield r
  end
end

#redirect(url, permanent = true) ⇒ Object



653
654
655
656
657
658
659
660
# File 'lib/merb-core/dispatch/router/behavior.rb', line 653

def redirect(url, permanent = true)
  @redirects       = true
  @redirect_url    = url
  @redirect_status = permanent ? 301 : 302

  # satisfy route compilation
  self.to({})
end

#redirects?Boolean

Returns:

  • (Boolean)


662
663
664
# File 'lib/merb-core/dispatch/router/behavior.rb', line 662

def redirects?
  @redirects
end

#regexp?Boolean

Returns

Boolean

True if this behavior has a regexp.

Returns:

  • (Boolean)


649
650
651
# File 'lib/merb-core/dispatch/router/behavior.rb', line 649

def regexp?
  @conditions_have_regexp
end

#resource(name, options = {}) ⇒ Object

Behavior#resource is a route helper for defining a singular RESTful resource. It yields to a block for child routes.

Parameters

name<String, Symbol>

The name of the resource.

options<Hash>

Overides and parameters to be associated with the route.

Options (options)

:namespace<~to_s>: The namespace for this route. :name_prefix<~to_s>:

A prefix for the named routes. If a namespace is passed and there
isn't a name prefix, the namespace will become the prefix.

:controller<~to_s>: The controller for this route

Block parameters

next_level<Behavior>

The child behavior.

Returns

Array

Routes which define a RESTful single resource.

Examples

r.resource :account # will result in the typical RESTful CRUD
  # shows new resource form      
  # GET     /account/new                :action => "new"

  # creates resource      
  # POST    /account/?(\.:format)?,     :action => "create"

  # shows resource      
  # GET     /account/(\.:format)?       :action => "show"

  # shows edit form      
  # GET     /account//edit           :action => "edit"

  # updates resource      
  # PUT     /account/(\.:format)?       :action => "update"

  # shows deletion confirmation page      
  # GET     /account//delete         :action => "delete"

  # destroys resources      
  # DELETE  /account/(\.:format)?       :action => "destroy"

You can optionally pass :namespace and :controller to refine the routing or pass a block to nest resources.

r.resource :account, :namespace => "admin" do ||
  .resources :preferences, :controller => "settings"
end



524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
# File 'lib/merb-core/dispatch/router/behavior.rb', line 524

def resource(name, options = {})
  namespace  = options[:namespace] || merged_params[:namespace]

  next_level = match "/#{name}"

  options[:controller] ||= merged_params[:controller] || name.to_s

  # Do not pass :name_prefix option on to to_resource
  name_prefix = options.delete :name_prefix

  if name_prefix.nil? && !namespace.nil?
    name_prefix = namespace_to_name_prefix namespace
  end

  unless @@parent_resources.empty?
    parent_resource = namespace_to_name_prefix @@parent_resources.join('_')
  end

  routes = next_level.to_resource options

  route_name = "#{name_prefix}#{name}"

  next_level.match('').to_route.name(:"#{route_name}")
  next_level.match('/new').to_route.name(:"new_#{route_name}")
  next_level.match('/edit').to_route.name(:"edit_#{route_name}")
  next_level.match('/delete').to_route.name(:"delete_#{route_name}")

  if block_given?
    @@parent_resources.push(route_name)
    yield next_level
    @@parent_resources.pop
  end

  routes
end

#resources(name, options = {}) ⇒ Object

Behavior#resources is a route helper for defining a collection of RESTful resources. It yields to a block for child routes.

Parameters

name<String, Symbol>

The name of the resources

options<Hash>

Ovverides and parameters to be associated with the route

Options (options)

:namespace<~to_s>: The namespace for this route. :name_prefix<~to_s>:

A prefix for the named routes. If a namespace is passed and there
isn't a name prefix, the namespace will become the prefix.

:controller<~to_s>: The controller for this route :collection<~to_s>: Special settings for the collections routes :member<Hash>:

Special settings and resources related to a specific member of this
resource.

:keys<Array>:

A list of the keys to be used instead of :id with the resource in the order of the url.

Block parameters

next_level<Behavior>

The child behavior.

Returns

Array

Routes which will define the specified RESTful collection of resources

Examples

r.resources :posts # will result in the typical RESTful CRUD
  # lists resources
  # GET     /posts/?(\.:format)?      :action => "index"
  # GET     /posts/index(\.:format)?  :action => "index"

  # shows new resource form
  # GET     /posts/new                :action => "new"

  # creates resource
  # POST    /posts/?(\.:format)?,     :action => "create"

  # shows resource
  # GET     /posts/:id(\.:format)?    :action => "show"

  # shows edit form
  # GET     /posts/:id/edit        :action => "edit"

  # updates resource
  # PUT     /posts/:id(\.:format)?    :action => "update"

  # shows deletion confirmation page
  # GET     /posts/:id/delete      :action => "delete"

  # destroys resources
  # DELETE  /posts/:id(\.:format)?    :action => "destroy"

# Nesting resources
r.resources :posts do |posts|
  posts.resources :comments
end



401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
# File 'lib/merb-core/dispatch/router/behavior.rb', line 401

def resources(name, options = {})
  namespace = options[:namespace] || merged_params[:namespace]

  next_level = match "/#{name}"

  name_prefix = options.delete :name_prefix
  matched_keys =  options[:keys] ? options.delete(:keys).map{|k| ":#{k}"}.join("/")  : ":id"

  if name_prefix.nil? && !namespace.nil?
    name_prefix = namespace_to_name_prefix namespace
  end

  unless @@parent_resources.empty?
    parent_resource = namespace_to_name_prefix @@parent_resources.join('_')
  end

  options[:controller] ||= merged_params[:controller] || name.to_s

  singular = name.to_s.singularize

  route_plural_name   = "#{name_prefix}#{parent_resource}#{name}"
  route_singular_name = "#{name_prefix}#{parent_resource}#{singular}"

  behaviors = []

  if member = options.delete(:member)
    member.each_pair do |action, methods|
      behaviors << Behavior.new(
      { :path => %r{^/#{matched_keys}/#{action}(\.:format)?$}, :method => /^(#{[methods].flatten * '|'})$/ },
      { :action => action.to_s }, next_level
      )
      next_level.match("/#{matched_keys}/#{action}").to_route.name(:"#{action}_#{route_singular_name}")
    end
  end

  if collection = options.delete(:collection)
    collection.each_pair do |action, methods|
      behaviors << Behavior.new(
      { :path => %r{^/#{action}(\.:format)?$}, :method => /^(#{[methods].flatten * '|'})$/ },
      { :action => action.to_s }, next_level
      )
      next_level.match("/#{action}").to_route.name(:"#{action}_#{route_plural_name}")
    end
  end

  routes = many_behaviors_to(behaviors + next_level.send(:resources_behaviors, matched_keys), options)



  # Add names to some routes
  [['', :"#{route_plural_name}"],
  ["/#{matched_keys}", :"#{route_singular_name}"],
  ['/new', :"new_#{route_singular_name}"],
  ["/#{matched_keys}/edit", :"edit_#{route_singular_name}"],
  ["/#{matched_keys}/delete", :"delete_#{route_singular_name}"]
  ].each do |path,name|
    next_level.match(path).to_route.name(name)
  end


  parent_keys = (matched_keys == ":id") ? ":#{singular}_id" : matched_keys
  if block_given?
    @@parent_resources.push(singular)
    yield next_level.match("/#{parent_keys}")
    @@parent_resources.pop
  end

  routes
end

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

Creates a Route from one or more Behavior objects, unless a block is passed in.

Parameters

params<Hash>

The parameters the route maps to.

&block

Optional block. A new Behavior object is yielded and further #to operations may be called in the block.

Block parameters

new_behavior<Behavior>

The child behavior.

Returns

Route

It registers a new route and returns it.

Examples

r.match('/:controller/:id).to(:action => 'show')

r.to :controller => 'simple' do |s|
  s.match('/test').to(:action => 'index')
  s.match('/other').to(:action => 'other')
end



242
243
244
245
246
247
248
249
250
# File 'lib/merb-core/dispatch/router/behavior.rb', line 242

def to(params = {}, &block)
  if block_given?
    new_behavior = self.class.new({}, params, self)
    yield new_behavior if block_given?
    new_behavior
  else
    to_route(params).register
  end
end

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

Parameters

params<Hash>

Optional params for generating the RESTful routes.

&block

Optional block for the route generation.

Returns

Array

Routes matching the RESTful singular resource.



576
577
578
# File 'lib/merb-core/dispatch/router/behavior.rb', line 576

def to_resource(params = {}, &block)
  many_behaviors_to resource_behaviors, params, &block
end

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

Parameters

params<Hash>

Optional params for generating the RESTful routes.

&block

Optional block for the route generation.

Returns

Array

Routes matching the RESTful resource.



566
567
568
# File 'lib/merb-core/dispatch/router/behavior.rb', line 566

def to_resources(params = {}, &block)
  many_behaviors_to resources_behaviors, params, &block
end

#to_route(params = {}, &conditional_block) ⇒ Object

Parameters

params<Hash>

Optional additional parameters for generating the route.

&conditional_block

A conditional block to be passed to Route.new.

Returns

Route

A new route based on this behavior.



197
198
199
200
# File 'lib/merb-core/dispatch/router/behavior.rb', line 197

def to_route(params = {}, &conditional_block)
  @params.merge! params
  Route.new compiled_conditions, compiled_params, self, &conditional_block
end