<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>spec/public/controller/resource_spec.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -430,16 +430,8 @@ class Merb::AbstractController
   # ====
   # TODO: Update this documentation
   def url(name, *args)
-    unless Symbol === name
-      args.unshift(name)
-      name = :default
-    end
-    
-    unless route = Merb::Router.named_routes[name]
-      raise Merb::Router::GenerationError, &quot;Named route not found: #{name}&quot;
-    end
-    
-    route.generate(args, params)
+    args &lt;&lt; params
+    Merb::Router.url(name, *args)
   end
   
   alias_method :relative_url, :url
@@ -472,6 +464,39 @@ class Merb::AbstractController
       (host || request.host) +
       url(name, rparams)
   end
+  
+  # Generates a URL for a single or nested resource.
+  #
+  # ==== Parameters
+  # resources&lt;Symbol,Object&gt;:: The resources for which the URL
+  #   should be generated. These resources should be specified
+  #   in the router.rb file using #resources and #resource.
+  #
+  # options&lt;Hash&gt;:: Any extra parameters that are needed to
+  #   generate the URL.
+  #
+  # ==== Returns
+  # String:: The generated URL.
+  #
+  # ==== Examples
+  #
+  # Merb::Router.prepare do
+  #   resources :users do
+  #     resources :comments
+  #   end
+  # end
+  #
+  # resource(:users)            # =&gt; /users
+  # resource(@user)             # =&gt; /users/10
+  # resource(@user, :comments)  # =&gt; /users/10/comments
+  # resource(@user, @comment)   # =&gt; /users/10/comments/15
+  # resource(:users, :new)      # =&gt; /users/new
+  # resource(:@user, :edit)     # =&gt; /users/10/edit
+  #
+  def resource(*args)
+    args &lt;&lt; params
+    Merb::Router.resource(*args)
+  end
 
   # Calls the capture method for the selected template engine.
   #</diff>
      <filename>lib/merb-core/controller/abstract_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -21,10 +21,11 @@ module Merb
   #
   # Compilation is synchronized by mutex.
   class Router
-    @routes         = []
-    @named_routes   = {}
-    @compiler_mutex = Mutex.new
-    @root_behavior  = Behavior.new.defaults(:action =&gt; &quot;index&quot;)
+    @routes          = []
+    @named_routes    = {}
+    @resource_routes = {}
+    @compiler_mutex  = Mutex.new
+    @root_behavior   = Behavior.new.defaults(:action =&gt; &quot;index&quot;)
 
     # Raised when route lookup fails.
     class RouteNotFound &lt; StandardError; end;
@@ -35,7 +36,7 @@ module Merb
 
     class &lt;&lt; self
       # @private
-      attr_accessor :routes, :named_routes, :root_behavior
+      attr_accessor :routes, :named_routes, :resource_routes, :root_behavior
       
       # Creates a route building context and evaluates the block in it. A
       # copy of +root_behavior+ (and instance of Behavior) is copied as
@@ -111,6 +112,82 @@ module Merb
       end
 
       alias_method :match, :match_before_compilation
+      
+      # Generates a URL from the params
+      #
+      # ==== Parameters
+      # name&lt;Symbol&gt;::
+      #   The name of the route to generate
+      #
+      # anonymous_params&lt;Object&gt;::
+      #   An array of anonymous parameters to generate the route
+      #   with. These parameters are assigned to the route parameters
+      #   in the order that they are passed.
+      #
+      # params&lt;Hash&gt;::
+      #   Named parameters to generate the route with.
+      #
+      # defaults&lt;Hash&gt;::
+      #   A hash of default parameters to generate the route with.
+      #   This is usually the request parameters. If there are any
+      #   required params that are missing to generate the route,
+      #   they are pulled from this hash.
+      # ==== Returns
+      # String:: The generated URL
+      # ---
+      # @private
+      def url(name, *args)
+        unless name.is_a?(Symbol)
+          args.unshift(name)
+          name = :default
+        end
+
+        unless route = Merb::Router.named_routes[name]
+          raise Merb::Router::GenerationError, &quot;Named route not found: #{name}&quot;
+        end
+        
+        defaults = args.pop
+        
+        route.generate(args, defaults)
+      end
+      
+      # Generates a URL from the resource(s)
+      #
+      # ==== Parameters
+      # resources&lt;Symbol,Object&gt;::
+      #   The identifiers for the resource route to generate. These
+      #   can either be symbols or objects. Symbols denote resource
+      #   collection routes and objects denote the members.
+      #
+      # params&lt;Hash&gt;::
+      #   Any extra parameters needed to generate the route.
+      # ==== Returns
+      # String:: The generated URL
+      # ---
+      # @private
+      def resource(*args)
+        defaults = args.pop
+        options  = extract_options_from_args!(args) || {}
+        key      = []
+        params   = []
+
+        args.each do |arg|
+          if arg.is_a?(Symbol) || arg.is_a?(String)
+            key &lt;&lt; arg.to_s
+          else
+            key &lt;&lt; arg.class.to_s
+            params &lt;&lt; arg
+          end
+        end
+
+        params &lt;&lt; options
+
+        unless route = Merb::Router.resource_routes[key]
+          raise Merb::Router::GenerationError, &quot;Resource route not found: #{args.inspect}&quot;
+        end
+
+        route.generate(params, defaults)
+      end
 
     private
     </diff>
      <filename>lib/merb-core/dispatch/router.rb</filename>
    </modified>
    <modified>
      <diff>@@ -32,8 +32,9 @@ module Merb
         # explicitly define the Behavior methods to proxy here (specifically namespace)
         # Rake's methods take precedence.
         %w(
-          match to with register default defaults options option namespace identify
-          default_routes defer_to name full_name fixatable redirect capture
+          match to with register default defaults options option namespace
+          identify default_routes defer_to name full_name fixatable redirect
+          capture resources resource
         ).each do |method|
           class_eval %{
             def #{method}(*args, &amp;block)
@@ -335,6 +336,7 @@ module Merb
         # option keys could be nil
         opts[:controller_prefix] = name unless opts.has_key?(:controller_prefix)
         opts[:name_prefix]       = name unless opts.has_key?(:name_prefix)
+        opts[:resource_prefix]   = opts[:name_prefix] unless opts.has_key?(:resource_prefix)
 
         behavior = self
         behavior = behavior.match(&quot;/#{path}&quot;) unless path.nil? || path.empty?</diff>
      <filename>lib/merb-core/dispatch/router/behavior.rb</filename>
    </modified>
    <modified>
      <diff>@@ -65,47 +65,64 @@ module Merb
         #  end
         #---
         # @public
-        def resources(name, options = {}, &amp;block)
-          singular   = name.to_s.singularize
-          keys       = options.delete(:keys) || [:id]
-          params     = { :controller =&gt; options.delete(:controller) || name.to_s }
+        def resources(name, *args, &amp;block)
+          name       = name.to_s
+          options    = extract_options_from_args!(args) || {}
+          singular   = Extlib::Inflection.singularize(name)
+          klass      = args.first ? args.first.to_s : Extlib::Inflection.classify(singular)
+          keys       = [ options.delete(:keys) || :id ].flatten
+          params     = { :controller =&gt; options.delete(:controller) || name }
           collection = options.delete(:collection) || {}
           member     = { :edit =&gt; :get, :delete =&gt; :get }.merge(options.delete(:member) || {})
 
           # Try pulling :namespace out of options for backwards compatibility
           options[:name_prefix]       ||= nil # Don't use a name_prefix if not needed
+          options[:resource_prefix]   ||= nil # Don't use a resource_prefix if not needed
           options[:controller_prefix] ||= options.delete(:namespace)
 
           self.namespace(name, options).to(params) do |resource|
             root_keys = keys.map { |k| &quot;:#{k}&quot; }.join(&quot;/&quot;)
+            
             # =&gt; index
-            resource.match(&quot;(/index)(.:format)&quot;, :method =&gt; :get).to(:action =&gt; &quot;index&quot;).name(name)
+            resource.match(&quot;(/index)(.:format)&quot;, :method =&gt; :get).to(:action =&gt; &quot;index&quot;).
+              name(name).register_resource(name)
+              
             # =&gt; create
             resource.match(&quot;(.:format)&quot;, :method =&gt; :post).to(:action =&gt; &quot;create&quot;)
+            
             # =&gt; new
-            resource.match(&quot;/new(.:format)&quot;, :method =&gt; :get).to(:action =&gt; &quot;new&quot;).name(:new, singular)
+            resource.match(&quot;/new(.:format)&quot;, :method =&gt; :get).to(:action =&gt; &quot;new&quot;).
+              name(&quot;new&quot;, singular).register_resource(name, &quot;new&quot;)
 
             # =&gt; user defined collection routes
             collection.each_pair do |action, method|
-              resource.match(&quot;/#{action}(.:format)&quot;, :method =&gt; method).to(:action =&gt; &quot;#{action}&quot;).name(action, name)
+              action = action.to_s
+              resource.match(&quot;/#{action}(.:format)&quot;, :method =&gt; method).to(:action =&gt; &quot;#{action}&quot;).
+                name(action, name).register_resource(name, action)
             end
 
             # =&gt; show
-            resource.match(&quot;/#{root_keys}(.:format)&quot;, :method =&gt; :get).to(:action =&gt; &quot;show&quot;).name(singular)
+            resource.match(&quot;/#{root_keys}(.:format)&quot;, :method =&gt; :get).to(:action =&gt; &quot;show&quot;).
+              name(singular).register_resource(klass)
 
             # =&gt; user defined member routes
             member.each_pair do |action, method|
-              resource.match(&quot;/#{root_keys}/#{action}(.:format)&quot;, :method =&gt; method).to(:action =&gt; &quot;#{action}&quot;).name(action, singular)
+              action = action.to_s
+              resource.match(&quot;/#{root_keys}/#{action}(.:format)&quot;, :method =&gt; method).
+                to(:action =&gt; &quot;#{action}&quot;).name(action, singular).register_resource(klass, action)
             end
 
             # =&gt; update
-            resource.match(&quot;/#{root_keys}(.:format)&quot;, :method =&gt; :put).to(:action =&gt; &quot;update&quot;)
+            resource.match(&quot;/#{root_keys}(.:format)&quot;, :method =&gt; :put).
+              to(:action =&gt; &quot;update&quot;)
+              
             # =&gt; destroy
-            resource.match(&quot;/#{root_keys}(.:format)&quot;, :method =&gt; :delete).to(:action =&gt; &quot;destroy&quot;)
+            resource.match(&quot;/#{root_keys}(.:format)&quot;, :method =&gt; :delete).
+              to(:action =&gt; &quot;destroy&quot;)
 
             if block_given?
               nested_keys = keys.map { |k| k.to_s == &quot;id&quot; ? &quot;:#{singular}_id&quot; : &quot;:#{k}&quot; }.join(&quot;/&quot;)
-              resource.options(:name_prefix =&gt; singular).match(&quot;/#{nested_keys}&quot;, &amp;block)
+              resource.options(:name_prefix =&gt; singular, :resource_prefix =&gt; klass).match(&quot;/#{nested_keys}&quot;, &amp;block)
             end
 
           end
@@ -164,24 +181,35 @@ module Merb
         #   end
         # ---
         # @public
-        def resource(name, options = {}, &amp;block)
-          params = { :controller =&gt; options.delete(:controller) || name.to_s.pluralize }
+        def resource(name, *args, &amp;block)
+          name    = name.to_s
+          options = extract_options_from_args!(args) || {}
+          params  = { :controller =&gt; options.delete(:controller) || name.pluralize }
 
           options[:name_prefix]       ||= nil # Don't use a name_prefix if not needed
+          options[:resource_prefix]   ||= nil # Don't use a resource_prefix if not needed
           options[:controller_prefix] ||= options.delete(:namespace)
 
           self.namespace(name, options).to(params) do |resource|
-            resource.match(&quot;(.:format)&quot;,        :method =&gt; :get   ).to(:action =&gt; &quot;show&quot;   ).name(name)
+            resource.match(&quot;(.:format)&quot;,        :method =&gt; :get   ).to(:action =&gt; &quot;show&quot;   ).name(name).register_resource(name)
             resource.match(&quot;(.:format)&quot;,        :method =&gt; :post  ).to(:action =&gt; &quot;create&quot; )
             resource.match(&quot;(.:format)&quot;,        :method =&gt; :put   ).to(:action =&gt; &quot;update&quot; )
             resource.match(&quot;(.:format)&quot;,        :method =&gt; :delete).to(:action =&gt; &quot;destroy&quot;)
-            resource.match(&quot;/new(.:format)&quot;,    :method =&gt; :get   ).to(:action =&gt; &quot;new&quot;    ).name(:new, name)
-            resource.match(&quot;/edit(.:format)&quot;,   :method =&gt; :get   ).to(:action =&gt; &quot;edit&quot;   ).name(:edit, name)
-            resource.match(&quot;/delete(.:format)&quot;, :method =&gt; :get   ).to(:action =&gt; &quot;delete&quot; ).name(:delete, name)
+            resource.match(&quot;/new(.:format)&quot;,    :method =&gt; :get   ).to(:action =&gt; &quot;new&quot;    ).name(:new,    name).register_resource(name, &quot;new&quot;)
+            resource.match(&quot;/edit(.:format)&quot;,   :method =&gt; :get   ).to(:action =&gt; &quot;edit&quot;   ).name(:edit,   name).register_resource(name, &quot;edit&quot;)
+            resource.match(&quot;/delete(.:format)&quot;, :method =&gt; :get   ).to(:action =&gt; &quot;delete&quot; ).name(:delete, name).register_resource(name, &quot;delete&quot;)
 
-            resource.options(:name_prefix =&gt; name, &amp;block) if block_given?
+            resource.options(:name_prefix =&gt; name, :resource_prefix =&gt; name,  &amp;block) if block_given?
           end
         end
+        
+      protected
+      
+        def register_resource(*key)
+          key = [@options[:resource_prefix], key].flatten.compact
+          @route.resource = key
+          self
+        end
 
       end
 </diff>
      <filename>lib/merb-core/dispatch/router/resources.rb</filename>
    </modified>
    <modified>
      <diff>@@ -61,6 +61,13 @@ module Merb
         self
       end
       
+      # Sets the route as a resource route with the given key as the 
+      # lookup key.
+      def resource=(key)
+        Router.resource_routes[key] = self
+        key
+      end
+      
       def name=(name)
         @name = name.to_sym
         Router.named_routes[@name] = self
@@ -75,10 +82,13 @@ module Merb
         
         # Support for anonymous params
         unless args.empty?
-          raise GenerationError, &quot;The route has #{@variables.length} variables: #{@variables.inspect}&quot; if args.length &gt; @variables.length
+          # First, let's determine which variables are missing
+          variables = @variables - params.keys
+          
+          raise GenerationError, &quot;The route has #{@variables.length} variables: #{@variables.inspect}&quot; if args.length &gt; variables.length
           
           args.each_with_index do |param, i|
-            params[@variables[i]] ||= param
+            params[variables[i]] ||= param
           end
         end
         </diff>
      <filename>lib/merb-core/dispatch/router/route.rb</filename>
    </modified>
    <modified>
      <diff>@@ -12,13 +12,21 @@ module Merb
       # ==== Returns
       # String:: The generated URL.
       def url(*args)
-        name = args.first.is_a?(Symbol) ? args.shift : :default
-        
-        unless route = Merb::Router.named_routes[name]
-          raise Merb::Router::GenerationError, &quot;Named route not found: #{name}&quot;
-        end
-
-        route.generate(args, @request_params || {})
+        args &lt;&lt; @request_params || {}
+        Merb::Router.url(*args)
+      end
+      
+      # Mimics the resource method available to controllers
+      #
+      # ==== Paramaters
+      # resources&lt;Object&gt;:: The resources to generate URLs from
+      # params&lt;Hash&gt;:: Any extra parameters that are required.
+      #
+      # ==== Returns
+      # String:: The generated URL.
+      def resource(*args)
+        args &lt;&lt; @request_params || {}
+        Merb::Router.resource(*args)
       end
       
       # ==== Parameters</diff>
      <filename>lib/merb-core/test/helpers/route_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -31,7 +31,14 @@ describe &quot;When generating URLs,&quot; do
     end
     
     it &quot;should give precedence to the params hash&quot; do
-      url(:ordered, &quot;one&quot;, &quot;two&quot;, &quot;three&quot;, :second =&gt; &quot;deux&quot;).should == &quot;/one/deux/three&quot;
+      url(:ordered, &quot;one&quot;, &quot;two&quot;, :first =&gt; &quot;un&quot;).should             == &quot;/un/one/two&quot;
+      url(:ordered, &quot;one&quot;, :first =&gt; &quot;un&quot;, :second =&gt; &quot;deux&quot;).should == &quot;/un/deux/one&quot;
+      url(:ordered, &quot;one&quot;, :first =&gt; &quot;un&quot;, :third =&gt; &quot;trois&quot;).should == &quot;/un/one/trois&quot;
+      url(:ordered, &quot;one&quot;, &quot;two&quot;, :second =&gt; &quot;deux&quot;).should          == &quot;/one/deux/two&quot;
+    end
+    
+    it &quot;should raise an exception when there are two many anonymous params after the named params were placed&quot; do
+      lambda { url(:ordered, &quot;one&quot;, &quot;two&quot;, :first =&gt; &quot;un&quot;, :second =&gt; &quot;deux&quot;) }.should raise_error(Merb::Router::GenerationError)
     end
   end
   </diff>
      <filename>spec/public/router/generation/params_spec.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>21db301aa5d4277dd19d9b1b1a30e469de012298</id>
    </parent>
  </parents>
  <author>
    <name>Carl Lerche</name>
    <email>carllerche@mac.com</email>
  </author>
  <url>http://github.com/wycats/merb-core/commit/36e76f35b62860bec3f63c30177115778586b618</url>
  <id>36e76f35b62860bec3f63c30177115778586b618</id>
  <committed-date>2008-09-29T11:10:32-07:00</committed-date>
  <authored-date>2008-09-29T11:10:32-07:00</authored-date>
  <message>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.</message>
  <tree>7dce5b2c882ff9b16813f6f8a1db5465220eefe6</tree>
  <committer>
    <name>Carl Lerche</name>
    <email>carllerche@mac.com</email>
  </committer>
</commit>
