Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

Routing when member name is update

#6397

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)

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

State: wontfix
Milestone: none
Assigned to: nobody
Importance: Low

Activity

  1. lic (at crd)
    lic (at crd)

    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?

    February 9th, 2011 @ 01:26 PM

  2. Andrew White
    Andrew White
    • 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.

    February 9th, 2011 @ 02:33 PM

  3. lic (at crd)
    lic (at crd)

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

    February 10th, 2011 @ 09:01 AM

  4. Andrew White
    Andrew White

    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

    February 10th, 2011 @ 09:52 AM

  5. lic (at crd)
    lic (at crd)

    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.

    February 10th, 2011 @ 09:59 AM

  6. Andrew White
    Andrew White

    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.

    February 10th, 2011 @ 10:29 AM

  7. lic (at crd)
    lic (at crd)

    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!

    February 10th, 2011 @ 10:40 AM

  8. lic (at crd)
    lic (at crd)

    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"
    

    February 10th, 2011 @ 10:53 AM

  9. lic (at crd)
    lic (at crd)

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

    February 10th, 2011 @ 11:24 AM

  10. Andrew White
    Andrew White

    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?

    February 10th, 2011 @ 12:01 PM

  11. lic (at crd)
    lic (at crd)

    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!

    February 10th, 2011 @ 02:51 PM