Ruby on Rails - custom PATCH action for form_for -


i'm working on spree e-commerce store built on ruby on rails , want custom action user can mark order complete straight checkout page without going through delivery etc. i've overridden checkout steps cannot 'checkout' button complete order sending order custom action in orders controller.

i'd think i've ticked off boxes: created patch action in routes.rb , checked rake routes make sure route exists. it's still telling me there no route.

the cart page won't load before submit anything, following error. i've spent day trying fix ideas great....

the error:

no route matches {:action=>"complete", :controller=>"spree/orders", :method=>:patch} 

routes.rb:

resources :orders    member      patch 'complete', to: 'orders#complete'    end  end 

rake routes:

        prefix verb   uri pattern                    controller#action        spree        /                              spree::core::engine complete_order patch  /orders/:id/complete(.:format) orders#complete         orders    /orders(.:format)              orders#index                post   /orders(.:format)              orders#create      new_order    /orders/new(.:format)          orders#new     edit_order    /orders/:id/edit(.:format)     orders#edit          order    /orders/:id(.:format)          orders#show                patch  /orders/:id(.:format)          orders#update                put    /orders/:id(.:format)          orders#update                delete /orders/:id(.:format)          orders#destroy 

html:

<%= form_for :order, url: {action: 'complete', method: :patch} |f| %>   <% f.submit %> <% end %> 

i haven't created controller yet be:

def complete   # mark order complete   # redirect confirmation page end 

would appreciate help. thanks

edit: here updated view (app/views/orders/edit.html.erb):

<% @body_id = 'cart' %> <div data-hook="cart_container">   <h1><%= spree.t(:shopping_cart) %></h1>    <% if @order.line_items.empty? %>     <div data-hook="empty_cart">       <div class="alert alert-info"><%= spree.t(:your_cart_is_empty) %></div>       <p><%= link_to spree.t(:continue_shopping), products_path, class: 'btn btn-default' %></p>     </div>   <% else %>     <div data-hook="outside_cart_form">       <%= form_for @order, url: update_cart_path, html: { id: 'update-cart' } |order_form| %>         <div data-hook="inside_cart_form">            <div data-hook="cart_items" class="table-responsive">             <%= render partial: 'form', locals: { order_form: order_form } %>           </div>          </div>       <% end %>     </div>      <div id="empty-cart" class="col-md-6" data-hook>       <%= form_tag empty_cart_path, method: :put %>         <p id="clear_cart_link" data-hook>           <%= submit_tag spree.t(:empty_cart), class: 'btn btn-default' %>           <%= spree.t(:or) %>           <%= link_to spree.t(:continue_shopping), products_path, class: 'continue' %>         </p>       <% end %>     </div>      <div id="complete-order">       complete order here - submit custom controller       <%= @order.id  %>       <%= form_for @order, url: complete_order_path(@order) |f| %>         <% f.submit %>       <% end %>       </div>    <% end %> </div> 

here whole controller:

module spree   class orderscontroller < spree::storecontroller     before_action :check_authorization     rescue_from activerecord::recordnotfound, :with => :render_404     helper 'spree/products', 'spree/orders'      respond_to :html      before_action :assign_order_with_lock, only: :update     skip_before_action :verify_authenticity_token, only: [:populate]      def show       @order = order.includes(line_items: [variant: [:option_values, :images, :product]], bill_address: :state, ship_address: :state).find_by_number!(params[:id])     end      def complete       @order = current_order     end      def update       if @order.contents.update_cart(order_params)         respond_with(@order) |format|           format.html             if params.has_key?(:checkout)               @order.next if @order.cart?               redirect_to checkout_state_path(@order.checkout_steps.first)             else               redirect_to cart_path             end           end         end       else         respond_with(@order)       end     end      # shows current incomplete order session     def edit       @order = current_order || order.incomplete.                                   includes(line_items: [variant: [:images, :option_values, :product]]).                                   find_or_initialize_by(guest_token: cookies.signed[:guest_token])       associate_user     end      # adds new item order (creating new order if none exists)     def populate       order    = current_order(create_order_if_necessary: true)       variant  = spree::variant.find(params[:variant_id])       quantity = params[:quantity].to_i       options  = params[:options] || {}        # 2,147,483,647 crazy. see issue #2695.       if quantity.between?(1, 2_147_483_647)         begin           order.contents.add(variant, quantity, options)         rescue activerecord::recordinvalid => e           error = e.record.errors.full_messages.join(", ")         end       else         error = spree.t(:please_enter_reasonable_quantity)       end        if error         flash[:error] = error         redirect_back_or_default(spree.root_path)       else         respond_with(order) |format|           format.html { redirect_to cart_path }         end       end     end      def empty       if @order = current_order         @order.empty!       end        redirect_to spree.cart_path     end      def accurate_title       if @order && @order.completed?         spree.t(:order_number, :number => @order.number)       else         spree.t(:shopping_cart)       end     end      def check_authorization       order = spree::order.find_by_number(params[:id]) || current_order        if order         authorize! :edit, order, cookies.signed[:guest_token]       else         authorize! :create, spree::order       end     end      private        def order_params         if params[:order]           params[:order].permit(*permitted_order_attributes)         else           {}         end       end        def assign_order_with_lock         @order = current_order(lock: true)         unless @order           flash[:error] = spree.t(:order_not_found)           redirect_to root_path , return         end       end   end end 

edit

it has become apparent since posted question in fact need declare routes in special way, despite rake routes showing them correct.

in routes.rb, add this:

spree::core::engine.routes.draw    # add custom  routes here, e.g.    '/terms-and-conditions' => 'home#terms', as: :terms end 

this allow use <%= link_to("terms", terms_path) %> helper.

see adding routes rails' spree e-commerce more details. wish documentation better on mentioned apart on far can tell.

this happens because not passing object form. there's no id parameter in route, , router fails make match.

your route defined member action, means expects id parameter. passing symbol instead.

<%= form_for :order <-- problem 

the clue in error message:

no route matches {:action=>"complete", :controller=>"spree/orders", :method=>:patch} 

notice how there's no id parameter in hash in error message?

to solve this, provide object form. example:

<%= form_for @order, url: complete_order_path(@order) |f| %> 

where @order instance variable set in controller.

on side note, can define routes so:

resources :orders   member     patch :complete   end   # or, since it's 1 route...   patch :complete, on: :member end 

notice can use symbols, , don't have specify controller because it's inferred resource name.

finally, don't need tell form method should patch. rails infers object passed in, in case @order. if it's new, method post, otherwise patch.


Comments

Popular posts from this blog

authentication - Mongodb revoke acccess to connect test database -

r - Update two sets of radiobuttons reactively - shiny -

ios - Realm over CoreData should I use NSFetchedResultController or a Dictionary? -