Why does Rails 3 nested route ignore nesting resource? - ruby-on-rails

Associations:
location has_many :comments
comment belongs_to :location
For some reason, this GET:
/locations/5/comments.json
is acting like this GET:
/comments.json
Started GET "/locations/5/comments.json" for 127.0.0.1 at 2012-04-10 21:18:00 -0700
Processing by CommentsController#index as JSON
Parameters: {"location_id"=>"5"}
Comment Load (0.1ms) SELECT "comments".* FROM "comments"
Completed 200 OK in 21ms (Views: 1.0ms | ActiveRecord: 0.7ms)
Note the SQL query: SELECT "comments".* FROM "comments"
The route is set up like this:
resources :locations do
resources :comments
end
Rake routes confirms the route:
location_comments GET /locations/:location_id/comments(.:format) {:action=>"index", :controller=>"comments"}
Here is the index action:
def index
#comments = Comment.all
respond_to do |format|
format.json { render json: #comments }
end
end
Is this the right action? It is consistent with the result, but I am not sure what else should be here. I've never had a problem with nested resources before, so I've never looked into the details.

try this:
def index
#location = Location.find(params[:location_id])
#comments = #location.comments
respond_to do |format|
format.json { render json: #comments }
end
end

Related

ActiveRecord::RecordNotFound at /gigs/2/status Couldn't find Gig with 'id'=

I added a route to the default restful routes in my app,so in my controller I have an action called status, it's verb is Patch, but I get the above error each time I try to update an attribute via the status action, Note: the default update action in the Restful route has no issues.
Routes.rb
Rails.application.routes.draw do
devise_for :users
resources :projects do
resources :gigs, shallow: true do
patch :status
end
end
end
Gigs Controller
class GigsController < ApplicationController
before_action :set_gig, only: [:show, :edit, :update, :destroy,:status]
def status
respond_to do |format|
if #gig.update(:done, true)
format.html { redirect_to #gig, notice: 'Gig was successfully updated.' }
format.json { render :show, status: :ok, location: #gig }
else
format.html { render :edit }
format.json { render json: #gig.errors, status: :unprocessable_entity }
end
end
end
private
def set_gig
#gig = Gig.find(params[:id])
end
def gig_params
params.require(:gig).permit(:name, :description, :done,:timeline)
end
end
and here is the link I added to the index.html.erb to execute the update
<td><%= link_to 'Mark as Done', gig_status_path(gig), method: :patch %></td>
Here is the log
Started PATCH "/gigs/2/status" for 127.0.0.1 at 2018-04-07 12:26:20 +0100
Processing by GigsController#status as HTML
Parameters: {"authenticity_token"=>"iNYFcsiwtNnCAC9goTGtfrqHFnBKufpgQ+61/pwZLVV6Nw82MBTART5ozVQUVsk74UFiWDknWrtoqYLN9D/2YQ==", "gig_id"=>"2"}
[1m[36mGig Load (0.0ms)[0m [1m[34mSELECT "gigs".* FROM "gigs" WHERE "gigs"."id" = $1 LIMIT $2[0m [["id", nil], ["LIMIT", 1]]
Completed 404 Not Found in 0ms (ActiveRecord: 0.0ms)
ActiveRecord::RecordNotFound - Couldn't find Gig with 'id'=:
app/controllers/gigs_controller.rb:93:in `set_gig'
Started POST "/__better_errors/df8ad027b952f89c/variables" for 127.0.0.1 at 2018-04-07 12:26:20 +0100
You can check URI Pattern by using command rake routes for status action.
Prefix Verb URI Pattern Controller#Action
gig_status PATCH /gigs/:gig_id/status(.:format) gigs#status
project_gigs GET /projects/:project_id/gigs(.:format) gigs#index
POST /projects/:project_id/gigs(.:format) gigs#create
new_project_gig GET /projects/:project_id/gigs/new(.:format) gigs#new
edit_gig GET /gigs/:id/edit(.:format) gigs#edit
gig GET /gigs/:id(.:format) gigs#show
PATCH /gigs/:id(.:format) gigs#update
PUT /gigs/:id(.:format) gigs#update
so you need to use gig_id in case of status action
Modify method set_gig as follows:
def set_gig
#gig = Gig.find(params[:gig_id])
end

Ruby on Rails: New post not saving on the blog

I'm working on an exercise, creating a blog with ruby on rails. I have the form ready to post an article, but once I click on the submit button, I am redirected to the homepage but the article doesn't save. Here is the following code
class ArticlesController < ApplicationController
def index
#articles = Article.paginate(:page => params[:page], per_page: 5).order('created_at DESC')
end
def show
#article = Article.find(params[:id])
end
def new
#article = Article.new
end
def create
#article = Article.new(title: params.permit[:title], body: params.permit[:body])
if #article.save
redirect_to articles, :notice => "Your post has been saved"
else
render :create
end
end
end
Here is the view create.html.haml
.container
.row
.col-xs-9-
.panel-title
%h2 Ecrivez votre article
= form_for #article do |f|
= f.text_field :title
= f.text_area :body, size: "60x12"
= f.submit "Publier"
Then the route.rb, I don't know if it can help
TP2::Application.routes.draw do
resources :articles, only: [:index]
get 'articles' => 'articles#index'
get 'articles/:id' => 'articles#show'
get 'articles/new'
get 'post' => 'articles#create'
post 'articles' => 'articles#index'
And to finish here is what the console show when I try to post an article
Started GET "/post" for 127.0.0.1 at 2016-04-10 14:24:56 +0200
Processing by ArticlesController#create as HTML
(0.2ms) BEGIN
(0.2ms) ROLLBACK
Rendered articles/create.html.haml within layouts/application (1.4ms)
Completed 200 OK in 9ms (Views: 4.9ms | ActiveRecord: 0.4ms)
Started POST "/articles" for 127.0.0.1 at 2016-04-10 14:25:10 +0200
Processing by ArticlesController#index as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"FMQmKvZ1t98ZE21VaiBQhm0jKJ9x9BwkXFbh4obfi3Qea0Zax5dgGirfpgcAiQA464GMD2+Qv/eGYrmvEoTZBQ==", "article"=>{"title"=>"Post", "body"=>"New Article test"}, "commit"=>"Publier"}
Article Load (0.6ms) SELECT "articles".* FROM "articles" ORDER BY created_at DESC LIMIT 5 OFFSET 0
(0.4ms) SELECT COUNT(*) FROM "articles"
Rendered articles/index.html.haml within layouts/application (3.4ms)
Completed 200 OK in 7ms (Views: 5.3ms | ActiveRecord: 1.0ms)
I don't understand why the new article won't save. Does anyone understand why ?
I would simplify the routes:
Rails.application.routes.draw do
root to: 'articles#index' # or where you want for the first page
resources :articles #will give you the correct path and verbs
end
And the articles_controller.rb
class ArticlesController < ApplicationController
...
def create
#article = Article.new(article_params)
respond_to do |format|
if #article.save
format.html { redirect_to #article, notice: "Article created!" }
else
format.html { render action: 'new' }
end
end
end
private
def article_params
params.require(:article).permit(:title, :body)
end
end
Because you are learning new stuff, this is a way you should use to debug your code:
put a binding.pry (a breakpoint) on the line before #article.save (or use another debugger, you can find it on Github)
reload your page, input the fields and click Save
go to the rails console, issue #article.save on the console (or #article.valid?), it should return false
puts #article.errors, so you can what are the validation issues
Good luck :)

Ruby on Rails 4: Can't edit posts or comments

Here is how my app is setup.
#app/models/category.rb
class Category < ActiveRecord::Base
belongs_to :user
has_many :forums
end
#app/models/forum.rb
class Forum < ActiveRecord::Base
belongs_to :user
belongs_to :category
has_many :posts
end
#app/models/post.rb
class post < ActiveRecord::Base
belongs_to :user
belongs_to :forum
has_many :comments
end
#app/models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :post
end
I'm having issues with my comment records. I can display them and create them, but can't edit/delete them as I can't figure out which link to set.
Here's my config/routes.rb
Rails.application.routes.draw do
devise_for :users, :path => '', :path_names => {:sign_up => 'register', :sign_in => 'login', :sign_out => 'logout'}
resources :categories
resources :forums do
resources :posts do
resources :comments
end
end
root 'categories#index'
end
Here is my comments controller.
class CommentsController < ApplicationController
def create
#forum = Forum.find(params[:forum_id])
#post = Post.find(params[:post_id])
#comment = #post.comments.create(params[:comment].permit(:comment))
#comment.user_id = current_user.id if current_user
#comment.save
if #comment.save
redirect_to [#forum, #post]
else
render 'new'
end
end
def edit
#forum = Forum.find(params[:forum_id])
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
end
def update
#forum = Forum.find(params(:forum_id])
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
if #comment.update(params[:comment].permit(:comment))
redirect_to post_path(#post)
else
render 'edit'
end
end
def destroy
#forum = Forum.find(params[:foumd_id])
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
#comment.destroy
redirect_to post_path(#post)
end
end
_comments.html.haml
.comment.clearfix
.content
%p.comment_content= comment.comment
%p.comment_author= comment.user.email
.buttons
= link_to "Edit", edit_forum_post_comment_path[#forum, #post]
I'm also having issues with editing my posts, I can create them no problem but can't edit them...
Posts controller
class PostsController < ApplicationController
before_action :find_post, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
#posts = Post.all
end
def show
end
def new
#forum = Forum.find(params[:forum_id])
#post = current_user.posts.build
end
def create
#forum = Forum.find(params[:forum_id])
#post = current_user.posts.build(post_params)
if #post.save
redirect_to [#forum, #post]
else
render 'new'
end
end
def edit
end
def update
#forum = Forum.find(params[:forum_id])
if #post.update(post_params)
redirect_to [#forum, #post]
else
render 'edit'
end
end
def destroy
#post.destroy
redirect_to root_path
end
private
def find_post
#post = Post.find(params[:id])
end
def post_params
params.require(:post).permit(:title, :content)
end
end
Logs
I, [2015-11-15T16:10:25.073062 #18541] INFO -- : Started GET "/forums/1/posts/1/" for 24.220.125.144 at 2015-11-15 16:10:25 -0600
I, [2015-11-15T16:10:25.122253 #18541] INFO -- : Processing by PostsController#show as HTML
I, [2015-11-15T16:10:25.122482 #18541] INFO -- : Parameters: {"forum_id"=>"1", "id"=>"1"}
D, [2015-11-15T16:10:25.148865 #18541] DEBUG -- : ^[[1m^[[36mPost Load (0.5ms)^[[0m ^[[1mSELECT `posts`.* FROM `posts` WHERE `posts$
D, [2015-11-15T16:10:25.185328 #18541] DEBUG -- : ^[[1m^[[35m (0.4ms)^[[0m SELECT COUNT(*) FROM `comments` WHERE `comments`.`post_id$
D, [2015-11-15T16:10:25.187051 #18541] DEBUG -- : ^[[1m^[[36mComment Load (0.3ms)^[[0m ^[[1mSELECT `comments`.* FROM `comments` WHER$
D, [2015-11-15T16:10:25.210686 #18541] DEBUG -- : ^[[1m^[[35mUser Load (0.4ms)^[[0m SELECT `users`.* FROM `users` WHERE `users`.`id$
I, [2015-11-15T16:10:25.232349 #18541] INFO -- : Rendered comments/_comment.html.haml (35.4ms)
I, [2015-11-15T16:10:25.232600 #18541] INFO -- : Rendered posts/show.html.haml within layouts/application (69.7ms)
I, [2015-11-15T16:10:25.233044 #18541] INFO -- : Completed 500 Internal Server Error in 110ms (ActiveRecord: 7.1ms)
F, [2015-11-15T16:10:25.235567 #18541] FATAL -- :
ActionView::Template::Error (No route matches {:action=>"edit", :controller=>"comments", :forum_id=>"1", :id=>"1"} missing required key$
3: %p.comment_content= comment.comment
4: %p.comment_author= comment.user.email
5: .buttons
6: = link_to "Edit", edit_forum_post_comment_path[#forum, #post]
app/views/comments/_comment.html.haml:6:in `_app_views_comments__comment_html_haml___1270861655862681835_22931440'
app/views/posts/show.html.haml:5:in `_app_views_posts_show_html_haml__2051422644921131492_20982460'
I, [2015-11-15T16:10:28.372822 #18541] INFO -- : Started GET "/forums/1/posts/1/edit" for 24.220.125.144 at 2015-11-15 16:10:28 -0600
I, [2015-11-15T16:10:28.375577 #18541] INFO -- : Processing by PostsController#edit as HTML
I, [2015-11-15T16:10:28.375656 #18541] INFO -- : Parameters: {"forum_id"=>"1", "id"=>"1"}
D, [2015-11-15T16:10:28.377175 #18541] DEBUG -- : ^[[1m^[[36mPost Load (0.4ms)^[[0m ^[[1mSELECT `posts`.* FROM `posts` WHERE `posts$
D, [2015-11-15T16:10:28.379458 #18541] DEBUG -- : ^[[1m^[[35mUser Load (0.3ms)^[[0m SELECT `users`.* FROM `users` WHERE `users`.`id$
I, [2015-11-15T16:10:28.403726 #18541] INFO -- : Rendered posts/_form.html.haml (20.6ms)
I, [2015-11-15T16:10:28.403924 #18541] INFO -- : Rendered posts/edit.html.haml within layouts/application (22.8ms)
I, [2015-11-15T16:10:28.404187 #18541] INFO -- : Completed 500 Internal Server Error in 28ms (ActiveRecord: 0.7ms)
F, [2015-11-15T16:10:28.405890 #18541] FATAL -- :
ActionView::Template::Error (undefined method `post_path' for #:0x00000003e04f18>):
1: = simple_form_for [#forum,#post] do |f|
2: = f.input :title
3: = f.input :content
4: = f.submit
app/views/posts/_form.html.haml:1:in `_app_views_posts__form_html_haml__3317057176452412006_32596640'
app/views/posts/edit.html.haml:3:in `_app_views_posts_edit_html_haml__3955498671314368903_32545500'
The error is as follows:
ActionView::Template::Error (No route matches {:action=>"edit", :controller=>"comments", :forum_id=>"1", :id=>"1"} missing required key$
ActionView::Template::Error (undefined method `post_path' for #:0x00000003e04f18>):
When asking questions / debugging in general, you need to start with the error / problem.
You've put up so much code most people will turn their noses up.
--
The error suggests a problem with your routes:
#config/routes.rb
Rails.application.routes.draw do
resources :categories
resources :forums do
resources :posts do
resources :comments
end
end
root 'categories#index'
end
The issue appears to be the way you're nesting your forms / posts / comments:
The first error is because you've not passed the post_id:
<%= link_to "Edit Comment", edit_forum_post_comment_path(#forum, #post, #comment) %>
The second error is because (I think) you've called your path without having the #forum variable populated. You should make sure that #forum is populated as follows:
#forum = Forum.find params[:forum_id]
#post = Post.new
Alternatively, you could build the post object on the #forum object, like so:
#forum = Forum.find params[:forum_id]
#post = #forum.posts.build
This should allow you to call <%= form_for #post do |f| %>
As a secondary to this, you need to look up the limits to nesting, as it deals directly with your problem:
Resources should never be nested more than 1 level deep.
Whilst I'd have to spend some time thinking about the best way to fix your issue, I'd do something like the following:
#config/routes.rb
resources :forums do
resources :posts, :comments
end
This will set the comments directly below the forums resource, allowing you to skip having to associate a forum and post each time you want to do anything with them...
<%= link_to "Edit Comment", edit_forums_comments_path(#forum, #comment) %>

Once I implement friendly_id, how do I get all actions to play nicely?

I have implemented friendly_id on my PostsController.
This is how my PostsController looks:
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
load_and_authorize_resource
def index
#posts = Post.all.order("created_at desc")
end
def show
end
def new
#post = Post.new(parent_id: params[:parent_id])
end
def edit
end
def create
#post = current_user.posts.new(post_params)
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
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
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
def set_post
#post = Post.friendly.find(params[:id])
# If an old id or a numeric id was used to find the record, then
# the request path will not match the post_path, and we should do
# a 301 redirect that uses the current friendly id.
if request.path != post_path(#post)
return redirect_to #post, :status => :moved_permanently
end
end
def post_params
params.require(:post).permit(:status, :title, :photo, :file, :body, :parent_id)
end
end
There are a few things happening here.
The redirect code that checks for legacy URLs and does a 301 redirect interferes with the other actions of the controller - e.g. edit, as can be seen here:
if request.path != post_path(#post)
return redirect_to #post, :status => :moved_permanently
end
When I try to edit a post, this is what happens:
Started GET "/pnpyo-saddened-at-passing-of-roger-clarke/edit" for 127.0.0.1 at 2014-09-02 04:46:49 -0500
Processing by PostsController#edit as HTML
Parameters: {"id"=>"pnpyo-saddened-at-passing-of-roger-clarke"}
Post Load (1.2ms) SELECT "posts".* FROM "posts" WHERE "posts"."slug" = 'pnpyo-saddened-at-passing-of-roger-clarke' ORDER BY "posts"."id" ASC LIMIT 1
Redirected to http://localhost:3000/pnpyo-saddened-at-passing-of-roger-clarke
Filter chain halted as :set_post rendered or redirected
Completed 301 Moved Permanently in 13ms (ActiveRecord: 1.2ms)
Also, another issue that comes up is that I have the ancestry gem installed. Whenever I try to create a child post, by using a URL similar to this: http://localhost:3000/new?parent_id=pnpyo-saddened-at-passing-of-roger-clarke
This is the error generated:
Started GET "/new?parent_id=pnpyo-saddened-at-passing-of-roger-clarke" for 127.0.0.1 at 2014-09-02 04:47:52 -0500
Processing by PostsController#new as HTML
Parameters: {"parent_id"=>"pnpyo-saddened-at-passing-of-roger-clarke"}
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
(0.4ms) SELECT COUNT(*) FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = $1 AND (((roles.name = 'admin') AND (roles.resource_type IS NULL) AND (roles.resource_id IS NULL))) [["user_id", 1]]
Post Load (0.3ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = $1 LIMIT 1 [["id", 0]]
Completed 404 Not Found in 35ms
ActiveRecord::RecordNotFound - Couldn't find Post with 'id'=pnpyo-saddened-at-passing-of-roger-clarke:
() app/controllers/posts_controller.rb:19:in `new'
That line 19 is the new action in the PostsController...i.e.:
#post = Post.new(parent_id: params[:parent_id])
Both issues are connected to the friendly_id implementation.
How do I solve both of these issues once and for all?
Friendly_id
First and foremost, you have to appreciate that friendly_id isn't such a big deal.
It works by changing your .find ActiveRecord method, as well as giving you the ability to use the route helpers with your slugs.
This means any confusion you have shouldn't really be about friendly_id - it should be more to do with your system if anything (if you have it set up correctly, friendly_id shouldn't have to feature in any of your calls etc)
Fix
There are several issues you need to consider:
Redirect
ID
Firstly, don't handle your redirect in your controller.
I believe I answered a question to you previously - you need to handle the redirect in the routes part of your application. Why? That's where the routing occurs.
Trying to handle the redirect in the controller goes against MVC principles (IMO) - you'll be better handling the redirect as follows:
#config/routes.rb
resources :posts, path: ''
get 'posts/:id' => redirect("/%{id}")
#get '/:id', to: 'posts#show' -> NOT NEEDED. Will be covered by the resources directive
#get 'posts/:id', to: 'posts#show' -> NOT NEEDED. Will be covered by redirect
#app/models/post.rb
class Post < ActiveRecord::Base
extend FriendlyId
friendly_id :slug, use: [:slugged, :finders]
end
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
def show
#post = Post.find params[:id]
end
end
This will fix all your problems.
If you use the friendly_id finders module, you don't need to distinguish any custom :friendly_id params or anything. It will just ping your database for the id and slug - no big deal.
You're overcomplicating this massively - you just need to redirect any /posts/*** to /:id (which will go to the posts controller anyway). This way, your :id will still be handled in the same way as it would without the friendly_id integration

Sortable Table Row with Nested Resource

In my rails app, a Timesheet has_many Entries and an Entry belongs_to a Timesheet.
class Timesheet < ActiveRecord::Base
has_many :entries, order: 'position', dependent: :destroy
end
class Entry < ActiveRecord::Base
belongs_to :timesheet
end
I'm following Railscast 147 for sortable lists (the updated version). In the development log I notice that my params hash correctly updates the sort order, but on reload it doesn't save the positions correctly. Furthermore, the request is being processed by the create action instead of my custom sort action. Here's my controller.
class EntriesController < ApplicationController
before_filter :signed_in_user
before_filter :find_timesheet
def index
#entries = #timesheet.entries.order("position")
#entry = #timesheet.entries.build
end
def create
#entry = #timesheet.entries.build(params[:entry])
#entry.position = #timesheet.entries.count + 1
if #entry.save
#flash[:notice] = "Entry created"
#redirect_to timesheet_entries_path
respond_to do |format|
format.html { redirect_to timesheet_entries_path }
format.js
end
else
flash[:alert] = "Entry could not be added"
render 'new'
end
end
def destroy
#entry = #timesheet.entries.find(params[:id])
#entry.destroy
respond_to do |format|
format.html { redirect_to timesheet_entries_path, flash[:notice] = "Entry destroyed" }
format.js
end
end
def sort
params[:entry].each_with_index do |id, index|
#timesheet.entries.update_all({position: index+1}, {id: id})
end
render nothing: true
end
private
def find_timesheet
#timesheet = Timesheet.find(params[:timesheet_id])
end
end
and my routes.rb file.
Sledsheet::Application.routes.draw do
resources :timesheets do
resources :entries, only: [:index, :create, :destroy] do
collection { post :sort }
end
end
end
The entries.js.coffee
jQuery ->
$("#entries tbody").sortable(
helper: fixHelper
update: ->
$.post($(this).data('update-url'), $(this).sortable('serialize'))
).disableSelection()
The output from the development log
Started POST "/timesheets/8/entries" for 127.0.0.1 at 2012-06-04 20:14:18 -0400
Processing by EntriesController#create as */*
Parameters: {"entry"=>["60", "59", "61"], "timesheet_id"=>"8"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."remember_token" = 'qDs53hgOWfRMbNN9JKau3w' LIMIT 1
Timesheet Load (0.1ms) SELECT "timesheets".* FROM "timesheets" WHERE "timesheets"."id" = ? ORDER BY date DESC LIMIT 1 [["id", "8"]]
Completed 500 Internal Server Error in 2ms
NoMethodError (undefined method `stringify_keys' for "60":String):
app/controllers/entries_controller.rb:11:in `create'
I googled the error about the undefined method, but I'm confused why the create action would be called in this case anyway? I do have a new_entry form on the page, that creates a new entry via Ajax. Perhaps this is interfering with the sort? Any help would be appreciated!
The reason why there's no 'stringify_keys' method is because you're passing an array to the create action and not the sort action.
What do you have for data-update-url in your erb.html file?
Should be sort_entries_path.

Resources