Why can rails not find a route created by a helper? - ruby-on-rails

I created a member route in rails 4:
resources :line_items do
post 'decrement', on: :member
end
and gave it a matching method in the line_items controller:
def decrement
#cart = current_cart
#line_item = #cart.line_items.find_by_id(params[:id])
#line_item.decrement_quantity
respond_to do |format|
if #line_item.save
format.html { redirect_to shop_path, notice: 'Line item was successfully updated.' }
format.js {#current_item = #line_item}
format.json { head :ok }
else
format.html { render action: "edit" }
format.js {#current_item = #line_item}
format.json { render json: #line_item.errors, status: :unprocessable_entity }
end
end
end
But when I try to make a button:
<%= button_to 'X', decrement_line_item_path(item) %>
I get this error:
No route matches [POST] "/carts/25"
What gives?

Your error message is:
No route matches [POST] "/carts/25"
But I was expecting the route to be: /line_items/25/decrement, not /carts/25
Are your line_items routes already nested under carts? If so, you could unnest the decrement action like so:
# in config/routes.rb
resources :carts do
resources :line_items
end
resources :line_items, only: [] do
post 'decrement', on: :member
end

resources :line_items do
post 'decrement', on: :member
end
Upon CONTROLLER='line_items' rake routes, here is the following output
decrement_line_item POST /line_items/:id/decrement(.:format) line_items#decrement
line_items GET /line_items(.:format) line_items#index
POST /line_items(.:format) line_items#create
new_line_item GET /line_items/new(.:format) line_items#new
edit_line_item GET /line_items/:id/edit(.:format) line_items#edit
line_item GET /line_items/:id(.:format) line_items#show
PUT /line_items/:id(.:format) line_items#update
DELETE /line_items/:id(.:format) line_items#destroy
So in your html button part, you need add the POST method.
<%= button_to 'X', decrement_line_item_path(item), method: :post %>

Related

Rails nested route with shallow and empty path - can't edit entry

This is the first time I've used nested routes with shallow and I'm struggling to get my edit action to work (I'm also learning more Rails stuff).
So a group can have multiple group actions assigned to it, so my route is set up as
resources :groups do
get 'actions', to: 'group_actions#index', as: :actions
get 'actions/new', to: 'group_actions#new', as: :new_action
post 'actions', to: 'group_actions#create'
resources :group_actions, path: '', as: :actions, except: [:index, :new, :create], shallow: true, shallow_path: 'actions/:group_id'
end
The reason for the empty path is so we can easily manage conflicting group action names by appending the group_id before it, so an action URL would be...
/actions/17/action-name
for example. This of course causes an issue with the index/new actions within a nested route so I manually set those routes as above. This all seems to work fine, actions can be created, deleted, viewed etc but I can't edit them.
This gives me the following routes...
group_actions GET /groups/:group_id/actions(.:format) group_actions#index
group_new_action GET /groups/:group_id/actions/new(.:format)
group_actions#new
POST /groups/:group_id/actions(.:format) group_actions#create
edit_action GET /actions/:group_id/:id/edit(.:format) group_actions#edit
action GET /actions/:group_id/:id(.:format) group_actions#show
PATCH /actions/:group_id/:id(.:format)group_actions#update
PUT /actions/:group_id/:id(.:format) group_actions#update
DELETE /actions/:group_id/:id(.:format)
My GroupAction controller is as so...
class GroupActionsController < ApplicationController
before_action :authenticate_user!, :except => [:show, :index]
before_action :set_group, only: [:index, :show, :new, :create, :edit, :update, :destroy]
def index
#group_actions = GroupAction.all
end
def show
#group_action = GroupAction.find(params[:id])
end
def new
#group_action = #group.group_actions.build
end
def edit
#group_action = GroupAction.find(params[:id])
end
def create
#group_action = GroupAction.new(group_action_params)
respond_to do |format|
if #group_action.save
format.html { redirect_to group_actions_path(#group.slug), notice: 'Group Action successfully created.' }
format.json { render :show, status: :created, group: #group }
else
format.html { render :new }
format.json { render json: #group.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #group_action.update(group_params)
format.html { redirect_to #group_action, notice: 'Action was successfully updated.' }
format.json { render :show, status: :ok, location: #group_action }
GaTrack.event(:group, :updated, "#{#group_action.name}")
else
format.html { render :edit }
format.json { render json: #group_action.errors, status: :unprocessable_entity }
end
end
end
def destroy
#group_action = GroupAction.find(params[:id])
#group_action.destroy
respond_to do |format|
format.html { redirect_to group_actions_path(#group.slug), notice: 'Action was successfully deleted.' }
format.json { head :no_content }
end
end
private
def set_group
#group = Group.find(params[:group_id])
end
def group_action_params
params.require(:group_action).permit(:name, :description, :location, :start_date, :end_date, :how_to_participate, :intro_email, :update_email, :thank_you_email, :action_link, :is_public, :group_id)
end
end
I'm then trying to share my new/edit forms and this is where I'm not understanding the issue.
new.html.erb
<h1>New Group Action</h1>
<%= render 'form', group_action: #group_action %>
edit.html.erb
<h1>New Group Action</h1>
<%= render 'form', group_action: #group_action %>
When trying to edit an action I get the following error...
undefined method `group_action_path' for #<#
<Class:0x007f855e30b2c8>:0x007f855e3163a8>
Did you mean? group_actions_path
group_locations_path
group_new_action_path
group_actions_url
So I ended up wrapping the form in separate form_forso I could use the url option for editing, which works for now.
<%= form_for(#group_action, url: action_path) do |f| %>
<%= render 'form', f: f %>
<% end %>

Rails form_for with nested resources

I'm having issues with routing a rails app to use a new and edit form.
The routes use the username within the URL.
lists GET /lists(.:format) lists#index
POST /user/:username/lists(.:format) lists#create
new_list GET /user/:username/lists/new(.:format) lists#new
edit_list GET /user/:username/lists/:id/edit(.:format) lists#edit
list GET /user/:username/lists/:id(.:format) lists#show
PATCH /user/:username/lists/:id(.:format) lists#update
PUT /user/:username/lists/:id(.:format) lists#update
DELETE /user/:username/lists/:id(.:format) lists#destroy
root GET / static#welcome
show_user GET /user/:id(.:format) users#show
My routes.rb-
get 'lists', to: 'lists#index', as: :lists, via: 'get'
scope 'user/:username' do
resources :lists, except: [:index]
end
My shared form, for use with updating and creating-
<%= form_for [#user, #list], url: list_path(#user, #list) do |f| %>
Using the above settings, I can edit lists correctly, but if I navigate to lists#new, I get the following error-
No route matches {:action=>"show", :controller=>"lists", :id=>nil, :username=>"test"} missing required keys: [:id]
If I pluralize the path in my form to be url: lists_path(#user, #list), then the new page will now load, but when trying to create I get the below error-
No route matches [POST] "/lists.test"
How can I fix these routes so I can use the same shared form for both edits and creating?
Adding controller code-
class ListsController < ApplicationController
before_action :set_list, only: [:show, :edit, :update, :destroy]
def index
#lists = List.all
end
def show
end
# GET /lists/new
def new
#list = List.new
#user = User.find_by(username: params[:username])
end
def edit
#user = User.find_by(username: params[:username])
end
def create
#list = current_user.lists.new(list_params)
respond_to do |format|
if #list.save
format.html { redirect_to list_path(#list.user.username, #list.id), notice: 'List was successfully created.' }
else
format.html { render :new }
end
end
end
def update
respond_to do |format|
if #list.update(list_params)
format.html { redirect_to list_path(#list.user.username, #list.id), notice: 'List was successfully updated.' }
else
format.html { render :edit }
end
end
end
def destroy
#list.destroy
respond_to do |format|
format.html { redirect_to lists_url, notice: 'List was successfully destroyed.' }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_list
#list = List.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def list_params
params.require(:list).permit(:name, :category, :user_id)
end
end
Try this - Change the :url value in the form_for to [[#list], :username => #user.username]
Like so:
<%= form_for [#user, #list], url: [[#list], :username => #user.username] do |f| %>

Rails association new_path error

I have two models post and topic in my rails app
class Post < ActiveRecord::Base
#relation between topics and post
belongs_to :topic
#post is valid only if it's associated with a topic:
validates :topic_id, :presence => true
#can also require that the referenced topic itself be valid
#in order for the post to be valid:
validates_associated :topic
end
And
class Topic < ActiveRecord::Base
#relation between topics and post
has_many :posts
end
I am trying to create association between both of them.
I want multiple post corresponding to each topic
I have used nested routes
Rails.application.routes.draw do
# nested routes
resources :topics do
resources :posts
end
resources :userdetails
devise_for :users, :controllers => { :registrations => "registrations" }
My Post controller looks like
class PostsController < ApplicationController
# before_action :set_post, only: [:show, :edit, :update, :destroy]
before_filter :has_userdetail_and_topic, :only =>[:new, :create]
# GET /posts
# GET /posts.json
#for new association SAAS book
protected
def has_userdetail_and_topic
unless(#topic =Topic.find_by_id(params[:topic_id]))
flash[:warning] = 'post must be for an existing topic'
end
end
public
def new
#post = #topic.posts.build
###topic = Topic.find(params[:topic_id1])
end
def index
#posts = Post.all
end
# GET /posts/1
# GET /posts/1.json
def show
end
# GET /posts/new
# GET /posts/1/edit
def edit
end
# POST /posts
# POST /posts.json
def create
##topic.posts << #post
##current_user = current_user.id
#current_user.posts << #topic.posts.build(params[:post])
##post = Post.new(post_params )
##post.userdetail_id = current_user.id
#Association functional between topic and post
#Class variable used
###topic.posts << #post
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render :show, status: :created, location: #post }
else
format.html { render :new }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /posts/1
# PATCH/PUT /posts/1.json
def update
respond_to do |format|
if #post.update(post_params)
format.html { redirect_to #post, notice: 'Post was successfully updated.' }
format.json { render :show, status: :ok, location: #post }
else
format.html { render :edit }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# DELETE /posts/1
# DELETE /posts/1.json
def destroy
#post.destroy
respond_to do |format|
format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_post
#post = Post.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def post_params
params.require(:post).permit(:topic_id,:issue, :description, :rating, :userdetail_id)
end
end
I am trying to navigate from topics/index via code <td><%= link_to 'Write', new_topic_post_path(#topic) %> </td>
but when i try to go at localhost:3000/topics]
I am getting error
No route matches {:action=>"new", :controller=>"posts", :topic_id=>nil} missing required keys: [:topic_id]
Can any body tell me about this error, as i am new to rails please clearly specify answer.
And I have one more doubt, please tell me if i am doing association between topic and post incorrectly.I have confusion about this line of code -
#topic.posts << #post
What the error missing required keys: [:topic_id] is telling you is that you need to provide a hash with the key topic_id:
<%= link_to 'Write', new_topic_post_path(topic_id: #topic) %>
Passing a resource as to a route helper only works for the id param:
<%= link_to #topic, topic_path(#topic) %>
Is a kind of shorthand for:
<%= link_to #topic, topic_path(id: #topic.to_param) %>
Addition:
#prcu is also correct. The #topic record needs to be saved to the database. Records which are not saved do not have an id since the database assigns the id column when the record is inserted.
You also need to set the #topic instance variable in PostsController:
#topic = Topic.find(params[:id])
This is commonly done with a before filter:
before_filter :set_topic, only: [:new]
def set_topic
#topic = Topic.find(params[:id])
end
The same also need to be done in TopicsController#index.
#topic is not set or it's not persisted. You can not use topic not saved to db in this helper.

I am trying to duplicate an activerecord record, but receive NoMethodError - undefined method

Forgive my ignorance but I am quite new to RoR. I am working on a project where users are able to duplicate a post in order to edit this "cloned version" and to save it (with a new post id, of course).
First I tried to use the Amoeba gem described like here, but I failed.
Then I thought I found a better solution - Duplicating a record in Rails 3 - but when I am integrating the suggested code, I am receiving the following error:
NoMethodError in Posts#show
undefined method `clone_post_path' for #<#:0x0000010267b8c8>
Researching and tinkering for hours now, I would really appreciate any help!
I am using Rails 3.2.13.
In my posts_controller I have the following code:
def show
#post = Post.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #post }
end
end
def new
#post = current_user.posts.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #post }
end
end
def clone
#post = current_user.posts.find(params[:id]) # find original object
#post = current_user.posts.new(#post.attributes) # initialize duplicate (not saved)
render :new # render same view as "new", but with #post attributes already filled in
end
def create
#post = current_user.posts.new(params[:post])
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render json: #post, status: :created, location: #post }
else
format.html { render action: "new" }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
This is the post.rb model:
class Post < ActiveRecord::Base
attr_accessible :content, :title, :videos, :link, :description
validates :title, presence: true
belongs_to :user
end
In the show view I call this:
<%= link_to 'Create a clone', clone_post_path(#post) %>
What am I doing wrong?
Thank you so much in advance for any help!
UPDATE:
Adding
resources :posts do
get 'clone', on: :member
end
to the routes file worked.
Here is the routes file:
Tt::Application.routes.draw do
devise_for :users
get 'about' => 'pages#about'
resources :posts
root :to => 'pages#home'
post 'attachments' => 'images#create'
resources :posts do
get 'clone', on: :member
end
end
Unfortunately afterwards a new error occurred:
ActiveModel::MassAssignmentSecurity::Error in PostsController#clone
Can't mass-assign protected attributes: id, created_at, updated_at, image_file_name, image_content_type, image_file_size, image_updated_at, file, user_id
Make sure that your routes file includes the following:
resources :posts do
get 'clone', on: :member
end
Since the clone action is not a standard action you must account for it in your routes file so it knows what to do.

Confused about Ruby on Rails REST Routing

I am really confused about Ruby on Rails REST routing. Even though I have specified that after the success it should go to the confirm action it goes to the show action and pass the ID=confirm.
def create
#article = Article.new(params[:article])
respond_to do |format|
if #article.save
format.html { redirect_to :action => "confirm" }
else
format.html { render :action => "new" }
end
end
end
The error I get is the following:
ActiveRecord::RecordNotFound in ArticlesController#show
Couldn't find Article with ID=confirm
Rails.root: /Projects/highoncoding
Application Trace | Framework Trace | Full Trace
app/controllers/articles_controller.rb:31:in `show'
UPDATE 1:
Here is my Route.rb file:
resources :articles
get "articles/confirm"
# config/routes.rb
MyApp::Application.routes.draw do
resources :articles do
member do
get 'confirm'
end
end
end
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
def create
#article = Article.new(params[:article])
respond_to do |format|
if #article.save
# use a named route here
format.html { redirect_to confirm_article_url(#article) }
else
format.html { render :action => "new" }
end
end
end
end
you'll need to add the route so it looks like
match 'articles/confirm/', :controller => 'article', :action => 'confirm'
resources :articles
you need to have the :id in there or it will think that confirm is an id which is why you are seeing the error ID=confirm. make sure also that this is the first route. (at least before the resources for the articles controller.
You should probably add the confirm route directly in your routes file.
match 'articles/confirm' => 'articles#confirm'
resources only work for create/update/destroy/etc.

Resources