This project is archived and is in readonly mode.

#6397 ✓wontfix
lic (at crd)

Routing when member name is update

Reported by lic (at crd) | February 9th, 2011 @ 01:19 PM

Hi I have a weird situation with routing and member
Looks like that

post 'update', :on => :member

misbehaves.

LONG STORY: I need to integrate a backend to Adobe Flash.
Since Flash runs in a browser I don't have any PUT and DELETE.
So I used to make a POST request to ex: /api/profiles/:id/update(.:format)

In the old Rails2 days I used to have this route

map.namespace :api do |api|
  api.resources :profiles,:member => { :update  => :post}
end

This generated a fine route like:

update_api_profile POST   /api/profiles/:id/update(.:format) {:action=>"update", :controller=>"api/profiles"}

Now in Rails 3 I have

namespace :api do
  resources :profiles do
    post 'update', :on => :member
  end
end

And I don't get my /api/profiles/:id/update(.:format) anymore.

If I try something like

namespace :api do
  resources :profiles do
    post 'update2', :on => :member
  end
end

So I get the expected route /api/profiles/:id/update2(.:format)

Comments and changes to this ticket

  • lic (at crd)

    lic (at crd) February 9th, 2011 @ 01:26 PM

    So far I have fixed with a workaround

    namespace :api do
      resources :profiles, :only => [:show, :create]
    end
    match '/api/profiles/:id/update(.:format)' => 'profile#update', :method => :post
    

    but shouldn't it work as in the Rails2 API ? I mean with a proper syntax?

  • Andrew White

    Andrew White February 9th, 2011 @ 02:33 PM

    • State changed from “new” to “wontfix”
    • Importance changed from “” to “Low”

    Browsers don't support the PUT and DELETE http methods either so to work around this problem you can pass a form parameter which overrides the HTTP method. Just pass _method=put and the normal route will be recognised. Alternatively, if you can't change the Flash movie then just add the update route manually as you have done.

  • lic (at crd)

    lic (at crd) February 10th, 2011 @ 09:01 AM

    Sorry Andrew but I can't pass a _method=put. My payload is XML or JSON.

  • Andrew White

    Andrew White February 10th, 2011 @ 09:52 AM

    Okay, how about sending a custom header? You can set the X-HTTP-Method-Override header to 'PUT' and the Rack middleware will adjust the HTTP method. You'll need to provide a cross-domain policy file on the server or the request will fail: http://kb2.adobe.com/cps/403/kb403185.html

  • lic (at crd)

    lic (at crd) February 10th, 2011 @ 09:59 AM

    Yeps that is a possibility. We can also do this in the route

    namespace :api do
      resources :profiles, :only => [:show, :create]
      match '/profiles/:id/update(.:format)' => 'profiles#update', :method => :post
      match '/profiles/:id/delete(.:format)' => 'profiles#destroy', :method => :delete
    end
    

    And it works. My question here is why can't we make an API that is back compatible with Rails2.

  • Andrew White

    Andrew White February 10th, 2011 @ 10:29 AM

    How about this:

      namespace :api do
        resources :profiles, :only => [:show, :create] do
          member do
            post :update, :path => 'update', :as => :update
            post :destroy, :path => 'destroy', :as => :destroy
          end
        end
      end
    

    You need the :path and :as options to override the inbuilt defaults of blank for the action path and helper prefix.

  • lic (at crd)

    lic (at crd) February 10th, 2011 @ 10:40 AM

    Superb!
    I didn't know that the :path option could override inbuilt defaults!
    That's perfect!

    Thank you for helping out! I own you a beer!

  • lic (at crd)

    lic (at crd) February 10th, 2011 @ 10:53 AM

    Weird thing...

    rake routes | grep profile
    
    api_profile_sessions POST   /api/profile_sessions(.:format)    {:action=>"create", :controller=>"api/profile_sessions"}
    

    update_api_profile POST /api/profiles/:id/update(.:format) {:action=>"update", :controller=>"api/profiles"}

        api_profiles POST   /api/profiles(.:format)            {:action=>"create", :controller=>"api/profiles"}
         api_profile GET    /api/profiles/:id(.:format)        {:action=>"show", :controller=>"api/profiles"}
    

    So I have it and works fine.

    From rspec:

    Failure/Error: assert_routing("/api/profiles/PUBLICKEY/update.xml",
     No route matches "/api/profiles/PUBLICKEY/update.xml"
    
  • lic (at crd)

    lic (at crd) February 10th, 2011 @ 11:24 AM

    Could it be a bug in ActionDispatch::Assertions::RoutingAssertions ?

  • Andrew White

    Andrew White February 10th, 2011 @ 12:01 PM

    I don't think so - the following test works in a new app:

    require 'test_helper'
    
    class RoutingTest < ActionDispatch::IntegrationTest
    
      test "API routing" do
    
        assert_routing(
          { :path => "/api/profiles/PUBLICKEY.xml", :method => :get },
          {
            :controller => "api/profiles",
            :action => "show",
            :id => "PUBLICKEY",
            :format => "xml"
          }
        )
    
        assert_routing(
          { :path => "/api/profiles.xml", :method => :post },
          {
            :controller => "api/profiles",
            :action => "create",
            :format => "xml"
          }
        )
    
        assert_routing(
          { :path => "/api/profiles/PUBLICKEY/update.xml", :method => :post },
          {
            :controller => "api/profiles",
            :action => "update",
            :id => "PUBLICKEY",
            :format => "xml"
          }
        )
    
        assert_routing(
          { :path => "/api/profiles/PUBLICKEY/destroy.xml", :method => :post },
          {
            :controller => "api/profiles",
            :action => "destroy",
            :id => "PUBLICKEY",
            :format => "xml"
          }
        )
    
      end
    end
    

    Are you sure that the assert_routing call is being passed :method => :post? Are you using assert_routing in your spec directly?

  • lic (at crd)

    lic (at crd) February 10th, 2011 @ 02:51 PM

    I made it work in rspec I guess the issue was calling the wrong assertion method.

    describe "Routes" do
      it "should route update profile from Flash" do
        {:post => "/api/profiles/PUBLICKEY/update.xml"}.should route_to(
         :controller => "api/profiles",
         :action     => "update",
         :id         => "PUBLICKEY",
         :format     => "xml"
         )
      end
    end
    

    Ok I own you definitively a beer now!
    Thank you for tips!

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

<h2 style="font-size: 14px">Tickets have moved to Github</h2>

The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>

Tags

Pages