Here is my solution I was kinda hoping to get it to redirect to the cart but I didn’t get it working as I wanted and ended up redirecting back to the catalog and leaving a notice

Show.html.erb


<div class="cart_title">Your Cart</div>
<table>
  <% for item in @cart.line_items %>
    <tr>
      <td><%= item.quantity %>&times;</td>
      <td><%= item.product.title %></td>
      <td class="item_price"><%= number_to_currency(item.total_price) %></td>
      <td><%= button_to 'Delete Line Item', item, :method => :delete, :confirm => 'Are You Sure' %></td>

    </tr>
  <% end %>

  <tr class="total_line">
    <td colspan="2">Total</td>
    <td class="total_cell"><%= number_to_currency(@cart.total_price) %></td>
  </tr>

</table>

<%= button_to 'Empty cart', @cart, :method => :delete,
    :confirm => 'Are you sure?' %>

line_items_controller.rb


  def destroy
    @line_item = LineItem.find(params[:id])
    @line_item.destroy

    respond_to do |format|
      format.html { redirect_to(store_url, :notice => 'Line Item Removed' )}
      format.xml  { head :ok }
    end
  end

Redirecting to Cart

In order to get it to redirect to cart, I used the following code. But with this code, I couldn’t get it to post a notice. Any ideas?

line_items_controller.rb


  def destroy
    @line_item = LineItem.find(params[:id])
    @line_item.destroy

    respond_to do |format|
      format.html { redirect_to(@line_item.cart, :notice => 'Item has been removed from your cart.') }
      format.xml  { head :ok }
    end
  end
notice in view

Make sure that your view is setup to display your notice on the cart page. i.e.


  <% if notice %> 
    <p id="notice"><%= notice %></p> 
  <% end %>

Diogo Says

I went a bit further and added a condition to redirect to the store if the cart is empty.

line_items_controller.rb


def destroy
    @line_item = LineItem.find(params[:id])
    @line_item.destroy

    respond_to do |format|
      if current_cart.line_items.empty?
        format.html { redirect_to(store_url, :notice=> 'Your cart is empty') }
      else 
        format.html { redirect_to(current_cart, :notice=> 'Item Removed') } 
      end

      format.xml  { head :ok }
    end
  end

Doc says

I took the route of decrementing the quantity of each line item before destroying it.

File: app/controllers/carts_controller.rb (return to the store once cart is empty)

...
  def show
    begin
      @cart = Cart.find(params[:id])
    rescue ActiveRecord::RecordNotFound
      logger.error "Attempt to access invalid cart #{params[:id]}" 
      redirect_to store_url, :notice => 'Invalid cart'
    else
      if @cart.line_items.count.zero?
        respond_to do |format|
          format.html { redirect_to(store_url,
            :notice => 'Your cart is currently empty') }
          format.xml  { head :ok }
        end
      else
        respond_to do |format|
          format.html # show.html.erb
          format.xml  { render :xml => @cart }
        end
      end
    end
  end
...
File: app/controllers/line_items_controller.rb (decrement quantity of line item until it reaches 1 before destroying it; return to cart)

...
  def destroy
    @line_item = LineItem.find(params[:id])
    if @line_item.quantity > 1
      @line_item.update_attributes(:quantity => @line_item.quantity - 1)
    else
      @line_item.destroy
    end

    respond_to do |format|
      format.html { redirect_to(cart_url(session[:cart_id])) }
      format.xml  { head :ok }
    end
  end
...
File: app/views/carts/show.html.erb (modify Back link to go to store main page, provide Remove link for each line item; return to cart)

...
  <% for item in @cart.line_items %>
    <tr>
      <td><%= item.quantity %>&times;</td>
      <td><%= item.product.title %></td>
      <td class="item_price">
        <%= number_to_currency(item.total_price) %>
      </td>
      <td><%= link_to 'Remove', item, :method => :delete %></td>
    </tr>
  <% end %>
...
<%= link_to 'Back', store_path %>
...

Menachem says: I went one step further and allowed the user to change the quantity to any amount he wants; changing the quantity to 0 causes the item to be deleted

Show.html.erb


<div class="cart_title" >Your Cart</div>
<table>
  <% for item in @cart.line_items %>
    <% form_for item do |f|%>
      <tr>
        <td>
        <%= f.submit :value => "Update Qty" %>
        <%= f.text_field :quantity, :size => 1 %>
        </td>
        <td><%= item.product.title %></td>
        <td class="item_price" ><%= number_to_currency(item.total_price) %></td>
      </tr>
    <%  end %>
  <% end %>
    <tr class="total_line" >
    <td colspan="2" >Total</td>
...

line_items_controller.rb


def update
    @line_item = LineItem.find(params[:id])

    respond_to do |format|
      if @line_item.update_attributes(params[:line_item])
        if (@line_item.quantity == 0)
          @line_item.destroy
        end
        format.html { redirect_to(@line_item.cart) }
        format.xml  { head :ok }
...

Functional Test for Removing Line Item

So I took the approach of redirecting to the cart (empty or not) after the line item is removed.

  format.html { redirect_to(@line_item.cart, :notice => 'Removed item') }

when I update the corresponding functional test with:

assert_redirected_to cart_path

I get

test_should_destroy_line_item(LineItemsControllerTest): ActionController::ActionControllerError: Cannot redirect to nil!

Has anyone found a workable solution to this yet?

WRT getting back to the cart redirect_to :back works


  def destroy
    @line_item = LineItem.find(params[:id])
    @line_item.destroy

    respond_to do |format|
      format.html {redirect_to :back}
      format.xml  { head :ok }
    end
  end

Sam Says

I used Diogo’s solution to redirect to different places. However I can’t seem to get the testing and fixtures to work. The controller seems to have a new empty cart instead of my non-empty fixture cart, which causes my test to fail.


  test "should destroy line_item" do
    @line_item = line_items(:one)
    @cart = carts(:one)
    assert_difference('LineItem.count', -1) do
      delete :destroy, :id => @line_item.to_param
    end
    assert(!@cart.line_items.empty?,'Cart should not be empty')
    if @cart.line_items.empty?
      assert_redirected_to store_url
    else
      assert_redirected_to cart_path(@cart.id)
    end
  end

How to modify test/code so that the fixture can work with the session mechanism?