4
\$\begingroup\$

I've implemented just as Ryan Bates suggested in his railscast episode, I've got everything working just right, so here is his approach in the controller :

def new
    session[:order_params] ||= {}
    @order = Order.new(session[:order_params])
    @order.current_step = session[:order_step]
  end

  def create
    session[:order_params].deep_merge!(params[:order]) if params[:order]
    @order = Order.new(session[:order_params])
    @order.current_step = session[:order_step]
    if @order.valid?
      if params[:back_button]
        @order.previous_step
      elsif @order.last_step?
        @order.save if @order.all_valid?
      else
        @order.next_step
      end
      session[:order_step] = @order.current_step
    end
    if @order.new_record?
      render "new"
    else
      session[:order_step] = session[:order_params] = nil
      flash[:notice] = "Order saved!"
      redirect_to @order
    end
  end

I'm ok with new method, but I wonder how can the create method be refactored. He said that he will do an episode how to refactor this in the future but he did not yet.

This was my take on he reactoring (more splitting than refactoring):

  def create
    session[:order_params].deep_merge!(params[:order]) if params[:order]
    @order = Order.new(session[:order_params])
    @order.current_step = session[:order_step]

    process_step

    if @order.new_record?
      render "new"
    else
      session[:order_step] = session[:order_params] = nil
      flash[:notice] = "Order saved!"
      redirect_to @order
    end
  end

def process_step
  if @order.valid?
    if params[:back_button]
      @order.previous_step
    elsif @order.last_step?
      @order.save if @order.all_valid?
    else
      @order.next_step
    end
    session[:order_step] = @order.current_step
  end
end
\$\endgroup\$
  • \$\begingroup\$ If you found @BroiSatse's answer helpful, please checkmark it. \$\endgroup\$ – Cary Swoveland Feb 27 '14 at 20:40
5
\$\begingroup\$

I would advise you to look at wicked gem: https://github.com/schneems/wicked.

Using this gem you could write:

include Wicked::Wizard
steps <list_of_your_steps>

def new
  session[:order_id] ||= Order.create.id
end

def show
  @order = Order.find(session[:order_id])
end

def update
  @order = Order.find(session[:order_id])
  @order.assign_attributes(params[:order_params])
  @order.current_step = step
  render_wizard @order
end

It will also separate your forms for each step. To add any step-related action you can use step reader:

def update
  @order = Order.find(session[:order_id])
  @order.assign_attributes(params[:order_params])
  @order.current_step = step
  @order.do_sth if step == :my_step      
  render_wizard @order
end

render_wizard automatically tries to save an object and redirects you to the next step or renders current form depending on a result.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.