GitHub Sale: sign up for any paid plan this week and pay nothing until January 1, 2009!  [ hide ]

public
Rubygem
Description: Merb Core: All you need. None you don't.
Homepage: http://www.merbivore.com
Clone URL: git://github.com/wycats/merb-core.git
Added the #resource controller helper for URL generation.

The #resource controller helper is a convenience method for generating
resource routes. It tracks the resource declarations in the router
file and is able to generate URLs from only models passed as arguments.
carllerche (author)
Mon Sep 29 11:10:32 -0700 2008
commit  36e76f35b62860bec3f63c30177115778586b618
tree    7dce5b2c882ff9b16813f6f8a1db5465220eefe6
parent  21db301aa5d4277dd19d9b1b1a30e469de012298
...
430
431
432
433
434
435
436
437
438
439
440
441
442
 
 
443
444
445
...
472
473
474
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
475
476
477
...
430
431
432
 
 
 
 
 
 
 
 
 
 
433
434
435
436
437
...
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
0
@@ -430,16 +430,8 @@ class Merb::AbstractController
0
   # ====
0
   # TODO: Update this documentation
0
   def url(name, *args)
0
- unless Symbol === name
0
- args.unshift(name)
0
- name = :default
0
- end
0
-
0
- unless route = Merb::Router.named_routes[name]
0
- raise Merb::Router::GenerationError, "Named route not found: #{name}"
0
- end
0
-
0
- route.generate(args, params)
0
+ args << params
0
+ Merb::Router.url(name, *args)
0
   end
0
   
0
   alias_method :relative_url, :url
0
@@ -472,6 +464,39 @@ class Merb::AbstractController
0
       (host || request.host) +
0
       url(name, rparams)
0
   end
0
+
0
+ # Generates a URL for a single or nested resource.
0
+ #
0
+ # ==== Parameters
0
+ # resources<Symbol,Object>:: The resources for which the URL
0
+ # should be generated. These resources should be specified
0
+ # in the router.rb file using #resources and #resource.
0
+ #
0
+ # options<Hash>:: Any extra parameters that are needed to
0
+ # generate the URL.
0
+ #
0
+ # ==== Returns
0
+ # String:: The generated URL.
0
+ #
0
+ # ==== Examples
0
+ #
0
+ # Merb::Router.prepare do
0
+ # resources :users do
0
+ # resources :comments
0
+ # end
0
+ # end
0
+ #
0
+ # resource(:users) # => /users
0
+ # resource(@user) # => /users/10
0
+ # resource(@user, :comments) # => /users/10/comments
0
+ # resource(@user, @comment) # => /users/10/comments/15
0
+ # resource(:users, :new) # => /users/new
0
+ # resource(:@user, :edit) # => /users/10/edit
0
+ #
0
+ def resource(*args)
0
+ args << params
0
+ Merb::Router.resource(*args)
0
+ end
0
 
0
   # Calls the capture method for the selected template engine.
0
   #
...
21
22
23
24
25
26
27
 
 
 
 
 
28
29
30
...
35
36
37
38
 
39
40
41
...
111
112
113
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
115
116
...
21
22
23
 
 
 
 
24
25
26
27
28
29
30
31
...
36
37
38
 
39
40
41
42
...
112
113
114
115
116
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
0
@@ -21,10 +21,11 @@ module Merb
0
   #
0
   # Compilation is synchronized by mutex.
0
   class Router
0
- @routes = []
0
- @named_routes = {}
0
- @compiler_mutex = Mutex.new
0
- @root_behavior = Behavior.new.defaults(:action => "index")
0
+ @routes = []
0
+ @named_routes = {}
0
+ @resource_routes = {}
0
+ @compiler_mutex = Mutex.new
0
+ @root_behavior = Behavior.new.defaults(:action => "index")
0
 
0
     # Raised when route lookup fails.
0
     class RouteNotFound < StandardError; end;
0
@@ -35,7 +36,7 @@ module Merb
0
 
0
     class << self
0
       # @private
0
- attr_accessor :routes, :named_routes, :root_behavior
0
+ attr_accessor :routes, :named_routes, :resource_routes, :root_behavior
0
       
0
       # Creates a route building context and evaluates the block in it. A
0
       # copy of +root_behavior+ (and instance of Behavior) is copied as
0
@@ -111,6 +112,82 @@ module Merb
0
       end
0
 
0
       alias_method :match, :match_before_compilation
0
+
0
+ # Generates a URL from the params
0
+ #
0
+ # ==== Parameters
0
+ # name<Symbol>::
0
+ # The name of the route to generate
0
+ #
0
+ # anonymous_params<Object>::
0
+ # An array of anonymous parameters to generate the route
0
+ # with. These parameters are assigned to the route parameters
0
+ # in the order that they are passed.
0
+ #
0
+ # params<Hash>::
0
+ # Named parameters to generate the route with.
0
+ #
0
+ # defaults<Hash>::
0
+ # A hash of default parameters to generate the route with.
0
+ # This is usually the request parameters. If there are any
0
+ # required params that are missing to generate the route,
0
+ # they are pulled from this hash.
0
+ # ==== Returns
0
+ # String:: The generated URL
0
+ # ---
0
+ # @private
0
+ def url(name, *args)
0
+ unless name.is_a?(Symbol)
0
+ args.unshift(name)
0
+ name = :default
0
+ end
0
+
0
+ unless route = Merb::Router.named_routes[name]
0
+ raise Merb::Router::GenerationError, "Named route not found: #{name}"
0
+ end
0
+
0
+ defaults = args.pop
0
+
0
+ route.generate(args, defaults)
0
+ end
0
+
0
+ # Generates a URL from the resource(s)
0
+ #
0
+ # ==== Parameters
0
+ # resources<Symbol,Object>::
0
+ # The identifiers for the resource route to generate. These
0
+ # can either be symbols or objects. Symbols denote resource
0
+ # collection routes and objects denote the members.
0
+ #
0
+ # params<Hash>::
0
+ # Any extra parameters needed to generate the route.
0
+ # ==== Returns
0
+ # String:: The generated URL
0
+ # ---
0
+ # @private
0
+ def resource(*args)
0
+ defaults = args.pop
0
+ options = extract_options_from_args!(args) || {}
0
+ key = []
0
+ params = []
0
+
0
+ args.each do |arg|
0
+ if arg.is_a?(Symbol) || arg.is_a?(String)
0
+ key << arg.to_s
0
+ else
0
+ key << arg.class.to_s
0
+ params << arg
0
+ end
0
+ end
0
+
0
+ params << options
0
+
0
+ unless route = Merb::Router.resource_routes[key]
0
+ raise Merb::Router::GenerationError, "Resource route not found: #{args.inspect}"
0
+ end
0
+
0
+ route.generate(params, defaults)
0
+ end
0
 
0
     private
0
     
...
32
33
34
35
36
 
 
 
37
38
39
...
335
336
337
 
338
339
340
...
32
33
34
 
 
35
36
37
38
39
40
...
336
337
338
339
340
341
342
0
@@ -32,8 +32,9 @@ module Merb
0
         # explicitly define the Behavior methods to proxy here (specifically namespace)
0
         # Rake's methods take precedence.
0
         %w(
0
- match to with register default defaults options option namespace identify
0
- default_routes defer_to name full_name fixatable redirect capture
0
+ match to with register default defaults options option namespace
0
+ identify default_routes defer_to name full_name fixatable redirect
0
+ capture resources resource
0
         ).each do |method|
0
           class_eval %{
0
             def #{method}(*args, &block)
0
@@ -335,6 +336,7 @@ module Merb
0
         # option keys could be nil
0
         opts[:controller_prefix] = name unless opts.has_key?(:controller_prefix)
0
         opts[:name_prefix] = name unless opts.has_key?(:name_prefix)
0
+ opts[:resource_prefix] = opts[:name_prefix] unless opts.has_key?(:resource_prefix)
0
 
0
         behavior = self
0
         behavior = behavior.match("/#{path}") unless path.nil? || path.empty?
...
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
100
101
102
 
 
 
103
104
 
 
105
106
107
108
 
109
110
111
...
164
165
166
167
168
 
 
 
 
169
170
 
171
172
173
174
 
175
176
177
178
179
180
 
 
 
181
182
 
183
184
 
 
 
 
 
 
 
 
185
186
187
...
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
100
101
102
103
104
 
105
106
107
108
109
 
110
111
112
113
114
115
 
116
117
118
119
 
120
121
122
123
124
 
125
126
127
128
...
181
182
183
 
 
184
185
186
187
188
189
190
191
192
193
 
194
195
196
197
 
 
 
198
199
200
201
 
202
203
204
205
206
207
208
209
210
211
212
213
214
215
0
@@ -65,47 +65,64 @@ module Merb
0
         # end
0
         #---
0
         # @public
0
- def resources(name, options = {}, &block)
0
- singular = name.to_s.singularize
0
- keys = options.delete(:keys) || [:id]
0
- params = { :controller => options.delete(:controller) || name.to_s }
0
+ def resources(name, *args, &block)
0
+ name = name.to_s
0
+ options = extract_options_from_args!(args) || {}
0
+ singular = Extlib::Inflection.singularize(name)
0
+ klass = args.first ? args.first.to_s : Extlib::Inflection.classify(singular)
0
+ keys = [ options.delete(:keys) || :id ].flatten
0
+ params = { :controller => options.delete(:controller) || name }
0
           collection = options.delete(:collection) || {}
0
           member = { :edit => :get, :delete => :get }.merge(options.delete(:member) || {})
0
 
0
           # Try pulling :namespace out of options for backwards compatibility
0
           options[:name_prefix] ||= nil # Don't use a name_prefix if not needed
0
+ options[:resource_prefix] ||= nil # Don't use a resource_prefix if not needed
0
           options[:controller_prefix] ||= options.delete(:namespace)
0
 
0
           self.namespace(name, options).to(params) do |resource|
0
             root_keys = keys.map { |k| ":#{k}" }.join("/")
0
+
0
             # => index
0
- resource.match("(/index)(.:format)", :method => :get).to(:action => "index").name(name)
0
+ resource.match("(/index)(.:format)", :method => :get).to(:action => "index").
0
+ name(name).register_resource(name)
0
+
0
             # => create
0
             resource.match("(.:format)", :method => :post).to(:action => "create")
0
+
0
             # => new
0
- resource.match("/new(.:format)", :method => :get).to(:action => "new").name(:new, singular)
0
+ resource.match("/new(.:format)", :method => :get).to(:action => "new").
0
+ name("new", singular).register_resource(name, "new")
0
 
0
             # => user defined collection routes
0
             collection.each_pair do |action, method|
0
- resource.match("/#{action}(.:format)", :method => method).to(:action => "#{action}").name(action, name)
0
+ action = action.to_s
0
+ resource.match("/#{action}(.:format)", :method => method).to(:action => "#{action}").
0
+ name(action, name).register_resource(name, action)
0
             end
0
 
0
             # => show
0
- resource.match("/#{root_keys}(.:format)", :method => :get).to(:action => "show").name(singular)
0
+ resource.match("/#{root_keys}(.:format)", :method => :get).to(:action => "show").
0
+ name(singular).register_resource(klass)
0
 
0
             # => user defined member routes
0
             member.each_pair do |action, method|
0
- resource.match("/#{root_keys}/#{action}(.:format)", :method => method).to(:action => "#{action}").name(action, singular)
0
+ action = action.to_s
0
+ resource.match("/#{root_keys}/#{action}(.:format)", :method => method).
0
+ to(:action => "#{action}").name(action, singular).register_resource(klass, action)
0
             end
0
 
0
             # => update
0
- resource.match("/#{root_keys}(.:format)", :method => :put).to(:action => "update")
0
+ resource.match("/#{root_keys}(.:format)", :method => :put).
0
+ to(:action => "update")
0
+
0
             # => destroy
0
- resource.match("/#{root_keys}(.:format)", :method => :delete).to(:action => "destroy")
0
+ resource.match("/#{root_keys}(.:format)", :method => :delete).
0
+ to(:action => "destroy")
0
 
0
             if block_given?
0
               nested_keys = keys.map { |k| k.to_s == "id" ? ":#{singular}_id" : ":#{k}" }.join("/")
0
- resource.options(:name_prefix => singular).match("/#{nested_keys}", &block)
0
+ resource.options(:name_prefix => singular, :resource_prefix => klass).match("/#{nested_keys}", &block)
0
             end
0
 
0
           end
0
@@ -164,24 +181,35 @@ module Merb
0
         # end
0
         # ---
0
         # @public
0
- def resource(name, options = {}, &block)
0
- params = { :controller => options.delete(:controller) || name.to_s.pluralize }
0
+ def resource(name, *args, &block)
0
+ name = name.to_s
0
+ options = extract_options_from_args!(args) || {}
0
+ params = { :controller => options.delete(:controller) || name.pluralize }
0
 
0
           options[:name_prefix] ||= nil # Don't use a name_prefix if not needed
0
+ options[:resource_prefix] ||= nil # Don't use a resource_prefix if not needed
0
           options[:controller_prefix] ||= options.delete(:namespace)
0
 
0
           self.namespace(name, options).to(params) do |resource|
0
- resource.match("(.:format)", :method => :get ).to(:action => "show" ).name(name)
0
+ resource.match("(.:format)", :method => :get ).to(:action => "show" ).name(name).register_resource(name)
0
             resource.match("(.:format)", :method => :post ).to(:action => "create" )
0
             resource.match("(.:format)", :method => :put ).to(:action => "update" )
0
             resource.match("(.:format)", :method => :delete).to(:action => "destroy")
0
- resource.match("/new(.:format)", :method => :get ).to(:action => "new" ).name(:new, name)
0
- resource.match("/edit(.:format)", :method => :get ).to(:action => "edit" ).name(:edit, name)
0
- resource.match("/delete(.:format)", :method => :get ).to(:action => "delete" ).name(:delete, name)
0
+ resource.match("/new(.:format)", :method => :get ).to(:action => "new" ).name(:new, name).register_resource(name, "new")
0
+ resource.match("/edit(.:format)", :method => :get ).to(:action => "edit" ).name(:edit, name).register_resource(name, "edit")
0
+ resource.match("/delete(.:format)", :method => :get ).to(:action => "delete" ).name(:delete, name).register_resource(name, "delete")
0
 
0
- resource.options(:name_prefix => name, &block) if block_given?
0
+ resource.options(:name_prefix => name, :resource_prefix => name, &block) if block_given?
0
           end
0
         end
0
+
0
+ protected
0
+
0
+ def register_resource(*key)
0
+ key = [@options[:resource_prefix], key].flatten.compact
0
+ @route.resource = key
0
+ self
0
+ end
0
 
0
       end
0
 
...
61
62
63
 
 
 
 
 
 
 
64
65
66
...
75
76
77
78
 
 
 
 
79
80
81
 
82
83
84
...
61
62
63
64
65
66
67
68
69
70
71
72
73
...
82
83
84
 
85
86
87
88
89
90
 
91
92
93
94
0
@@ -61,6 +61,13 @@ module Merb
0
         self
0
       end
0
       
0
+ # Sets the route as a resource route with the given key as the
0
+ # lookup key.
0
+ def resource=(key)
0
+ Router.resource_routes[key] = self
0
+ key
0
+ end
0
+
0
       def name=(name)
0
         @name = name.to_sym
0
         Router.named_routes[@name] = self
0
@@ -75,10 +82,13 @@ module Merb
0
         
0
         # Support for anonymous params
0
         unless args.empty?
0
- raise GenerationError, "The route has #{@variables.length} variables: #{@variables.inspect}" if args.length > @variables.length
0
+ # First, let's determine which variables are missing
0
+ variables = @variables - params.keys
0
+
0
+ raise GenerationError, "The route has #{@variables.length} variables: #{@variables.inspect}" if args.length > variables.length
0
           
0
           args.each_with_index do |param, i|
0
- params[@variables[i]] ||= param
0
+ params[variables[i]] ||= param
0
           end
0
         end
0
         
...
12
13
14
15
16
17
18
19
20
21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
23
24
...
12
13
14
 
 
 
 
 
 
 
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
0
@@ -12,13 +12,21 @@ module Merb
0
       # ==== Returns
0
       # String:: The generated URL.
0
       def url(*args)
0
- name = args.first.is_a?(Symbol) ? args.shift : :default
0
-
0
- unless route = Merb::Router.named_routes[name]
0
- raise Merb::Router::GenerationError, "Named route not found: #{name}"
0
- end
0
-
0
- route.generate(args, @request_params || {})
0
+ args << @request_params || {}
0
+ Merb::Router.url(*args)
0
+ end
0
+
0
+ # Mimics the resource method available to controllers
0
+ #
0
+ # ==== Paramaters
0
+ # resources<Object>:: The resources to generate URLs from
0
+ # params<Hash>:: Any extra parameters that are required.
0
+ #
0
+ # ==== Returns
0
+ # String:: The generated URL.
0
+ def resource(*args)
0
+ args << @request_params || {}
0
+ Merb::Router.resource(*args)
0
       end
0
       
0
       # ==== Parameters
...
31
32
33
34
 
 
 
 
 
 
 
 
35
36
37
...
31
32
33
 
34
35
36
37
38
39
40
41
42
43
44
0
@@ -31,7 +31,14 @@ describe "When generating URLs," do
0
     end
0
     
0
     it "should give precedence to the params hash" do
0
- url(:ordered, "one", "two", "three", :second => "deux").should == "/one/deux/three"
0
+ url(:ordered, "one", "two", :first => "un").should == "/un/one/two"
0
+ url(:ordered, "one", :first => "un", :second => "deux").should == "/un/deux/one"
0
+ url(:ordered, "one", :first => "un", :third => "trois").should == "/un/one/trois"
0
+ url(:ordered, "one", "two", :second => "deux").should == "/one/deux/two"
0
+ end
0
+
0
+ it "should raise an exception when there are two many anonymous params after the named params were placed" do
0
+ lambda { url(:ordered, "one", "two", :first => "un", :second => "deux") }.should raise_error(Merb::Router::GenerationError)
0
     end
0
   end
0
   

Comments

    No one has commented yet.