4

Orders can have many states. I would like to create named routes for those. I need the state to be passed in to the controller as a param. Here is what I was thinking, but it obviously does not work.

match "order/:state/:id" => "orders#%{state}", as: "%{state}"

So I would like order/address/17 to route to orders#address, with :state and :id being passed in as params. Likewise, order/shipping/17 would route to orders#shipping, again :state and :id would be passed in.

Here is the controller.

class OrdersController < ApplicationController

  before_filter :load_order, only: [:address, :shipping, :confirmation, :receipt]
  before_filter :validate_state, only: [:address, :shipping, :confirmation, :receipt]


  def address
    @order.build_billing_address unless @order.billing_address
    @order.build_shipping_address unless @order.shipping_address
  end


  def shipping
    @shipping_rates = @order.calculate_shipping_rates
  end


  def confirmation

  end


  def receipt

  end

  private 

  def load_order
    @order = Order.find(params[:id])
  end

  # Check to see if the user is on the correct action
  def validate_state
    if params[:state]
      unless params[:state] == @order.state
        redirect_to eval("#{@order.state}_path(:#{@order.state},#{@order.id})")
        return
      end
    end
  end

end

Here is what we ended up going with:

routes.rb

%w(address shipping confirmation receipt).each do |state|
    match "order/#{state}/:id", :to => "orders##{state}", :as => state, :state => state
end

orders_controller.rb

def validate_state
    if params[:state]
      unless params[:state] == @order.state
        redirect_to(eval("#{@order.state}_path(@order)"))
        return
      end
    end
  end
2
  • If your url is /order/address/17, where state information is taken from? If this is not intended url, what is it? Commented Dec 28, 2010 at 18:00
  • I just edited it to show a little more of what I am doing. Commented Dec 28, 2010 at 18:05

1 Answer 1

8

You aren't going to be able to create dynamic named routes with that sort of syntax, but you're basically just using :state as the :action. If you replace :state with :action and specify the controller manually, it'll work. Obviously, you will have to change your code to look at params[:action] rather than params[:state] (or map that variable in a before_filter), but beyond that it should work fine.

match "order/:action/:id", :controller => "orders"

Be aware that if orders has RESTful resource mappings like create or delete, this route would allow GET requests to them, which would be bad; you may just want to add explicit routes for each action you want to complete. This will let you get params[:state], as well:

%w(address shipping).each do |state|
  match "order/#{state}/:id", :to => "orders##{state}", :as => state, :state => state
end
Sign up to request clarification or add additional context in comments.

1 Comment

This is a great response. Thank you for that second part, made a huge difference in how we implemented it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.