I can't understand why this is happening, seems that id of article is being queried even though friendly.id has been found. ID being found is "0" for any article that i click, even though the correct article_id (59) is found when associated with a comment.
Processing by ArticlesController#show as HTML
Parameters: {"id"=>"javascript-8"}
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
Article Load (0.7ms) SELECT "articles".* FROM "articles" WHERE "articles"."slug" = $1 LIMIT $2 [["slug", "javascript-8"], ["LIMIT", 1]]
(1.9ms) BEGIN
Article Load (0.3ms) SELECT "articles".* FROM "articles" WHERE "articles"."id" = $1 LIMIT $2 [["id", 0], ["LIMIT", 1]]
(1.1ms) ROLLBACK
Rendering articles/show.html.erb within layouts/application
Rendered comments/_comment_form.html.erb (19.3ms)
Comment Load (0.7ms) SELECT "comments".* FROM "comments" WHERE "comments"."article_id" = 59 ORDER BY created_at DESC
Rendered comments/_comment.html.erb (25.2ms)
EDITED: Articles controller
class ArticlesController < ApplicationController
before_action :authenticate_user!
before_action :set_article, only: [:show, :edit, :update, :destroy, :toggle_vote]
impressionist :actions=>[:show]
def show
#article_categories = #article.categories
#comments = Comment.where(article_id: #article).order("created_at DESC")
if #article.status == "draft" && #article.user != current_user
redirect_to root_path
end
end
private
def set_article
#article = Article.friendly.find(params[:id])
end
end
friendly_id :foo, use: :slugged # you must do
MyClass.friendly.find('bar')
or...
friendly_id :foo, use: [:slugged, :finders] # you can now do
MyClass.find('bar')
In your case
def set_article
#article = Article.friendly.find(params[:id])
end
It was because i was using impressionist gem.
I should remove
impressionist :actions=>[:show]
at top of controller, and instead add this in show
def show
impressionist(#article)
end
As written in impressionist gem usage guide (https://github.com/charlotte-ruby/impressionist#usage), "f you're using friendly_id be sure to log impressionist this way, as params[:id] will return a string(url slug) while impressionable_id is a Integer column in database. "
Related
I'm using Rails 6 and ActionMailer. I'm working on sending a mail to the user everytime his post is commented on. Here's my code:
app/mailers/comment_mailer.rb
class CommentMailer < ApplicationMailer
def comment_mail
#user = params[:user]
mail(to: #user.email, subject: "Comments")
end
end
app/controllers/comments_controller.rb
class CommentsController < ApplicationController
before_action :authenticate_user!
def create
comment = Comment.new(comment_params)
comment.user_id = current_user.id
if comment.save
CommentMailer.with(user: #user).comment_mail.deliver_now
redirect_to post_path(comment.post.id)
end
end
app/views/comment_mailer/comment_mail.haml
%h4
Hi
- #user.name
%p Someone commented your post! Click the link below to see it:
(I haven't done the link step yet)
After following this tutorial: https://dev.to/morinoko/sending-emails-in-rails-with-action-mailer-and-gmail-35g4 I had this Error:
NoMethodError in CommentsController#create
undefined method `email' for nil:NilClass
My console:
NoMethodError (undefined method `email' for nil:NilClass):
app/mailers/comment_mailer.rb:5:in `comment_mail'
app/controllers/comments_controller.rb:8:in `create'
Started POST "/comments" for 172.17.0.1 at 2020-07-01 00:32:38 +0000
Cannot render console from 172.17.0.1! Allowed networks: 127.0.0.0/127.255.255.255, ::1
Processing by CommentsController#create as HTML
Parameters: {"authenticity_token"=>"Mf15U/OG9DX3SKwoBRHE/D/xENBocTtdcS07aUur+p/tGJAWSxYSP65kovzhLXHXBjvs/Wzp2dV4/1+L4nxrdQ==", "comment"=>{"content"=>"well said!", "post_id"=>"2"}, "commit"=>"Comment"}
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
(0.3ms) BEGIN
↳ app/controllers/comments_controller.rb:7:in `create'
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ app/controllers/comments_controller.rb:7:in `create'
Post Load (0.4ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]]
↳ app/controllers/comments_controller.rb:7:in `create'
Comment Create (0.7ms) INSERT INTO "comments" ("content", "user_id", "post_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["content", "well said!"], ["user_id", 1], ["post_id", 2], ["created_at", "2020-07-01 00:32:38.439626"], ["updated_at", "2020-07-01 00:32:38.439626"]]
↳ app/controllers/comments_controller.rb:7:in `create'
(1.1ms) COMMIT
↳ app/controllers/comments_controller.rb:7:in `create'
CommentMailer#comment_email: processed outbound mail in 1.4ms
Completed 500 Internal Server Error in 54ms (ActiveRecord: 3.6ms | Allocations: 29044)
I used binding.pry to review the comment_mail method and I had #user >> nil as return.
When you call CommentMailer.with(user: #user).comment_mail.deliver_now
You haven't actually defined #user you mentioned that you have a user attached to the post record so use that
comment.post.user
so now you have
CommentMailer.with(user: comment.post.user).comment_mail.deliver_now
Or you make user a parameter of comment_mail
class CommentMailer < ApplicationMailer
def comment_mail(user)
#user = user #and this makes #user available in your templates
mail(to: #user.email, subject: "Comments")
end
end
And as you would expect call it like so
CommentMailer.comment_mail(comment.post.user).deliver_now #or preferably deliver_later
Assuming that comment belong_to posts, or whatever is being commented on, and then also assuming posts belong_to a user. You need to populate that #user variable. Something like:
#user = comment.post.user
To get the user of the post being commented on.
I think this should then work
CommentMailer.comment_mail(#user).deliver_now
and then update the method itself
def comment_mail(user)
#user = user
mail(to: #user.email, subject: "Comments")
end
You should also be able to then use the #user object in the mail view template too if you want to.
When I submit a form with blank fields that are required I see the ROLLBACK and ActiveModel::Errors in the console but for some reason I can't render the errors in the view.
This is happening on both the new and edit templates.
Needless to say, creating a new record or editing an existing one with the required fields works fine.
This is what my code looks like:
Model: transactions.rb
class Transaction < ApplicationRecord
belongs_to :account
belongs_to :category
validates :date, :description, :amount, :category_id, :account_id, presence: true
end
Controller: transactions_controller.rb
def new
#transaction = Transaction.new
end
def create
#transaction = Transaction.create(transaction_params)
if #transaction.save
redirect_to account_path(#account), notice: "Transaction created"
else
render :new
end
end
def edit
if #transaction.amount > 0
#transaction_type = "income"
else
#transaction_type = "expense"
end
render :edit
end
def update
if #transaction.update(transaction_params)
redirect_to account_path(#account)
else
render :edit
end
end
private
def transaction_params
params.require(:transaction).permit(:date, :description, :amount, :category_id, :account_id)
end
View: new.html.erb (or edit.html.erb)
<ul>
<% #transaction.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
Log after submitting the form:
Started POST "/accounts/15/transactions" for 192.168.55.1 at 2019-09-07 03:00:24 +0000
Cannot render console from 192.168.55.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by TransactionsController#create as JS
Parameters: {"utf8"=>"✓", "authenticity_token"=>"CJd+/rB6LcUqQAz0ZvY0cf8Dcu+2qzUYLCQHLWE2GElZtJrKbKq2EODFeVvKG6NkE2MxtIIjRreqHAnKu2sJ9A==", "transaction"=>{"date(1i)"=>"2019", "date(2i)"=>"9", "date(3i)"=>"7", "description"=>"", "category_id"=>"1", "amount"=>"0"}, "transaction_type"=>"Expense", "commit"=>"Add Transaction", "account_id"=>"15"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /var/lib/gems/2.6.0/gems/activerecord-5.2.3/lib/active_record/log_subscriber.rb:98
Account Load (0.1ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."id" = $1 LIMIT $2 [["id", 15], ["LIMIT", 1]]
↳ app/controllers/transactions_controller.rb:64
(0.1ms) BEGIN
↳ app/controllers/transactions_controller.rb:24
Category Load (0.2ms) SELECT "categories".* FROM "categories" WHERE "categories"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ app/controllers/transactions_controller.rb:24
(0.2ms) ROLLBACK
↳ app/controllers/transactions_controller.rb:24
(0.1ms) BEGIN
↳ app/controllers/transactions_controller.rb:26
(0.1ms) ROLLBACK
↳ app/controllers/transactions_controller.rb:26
Rendering transactions/new.html.erb within layouts/application
If I add p #transaction.errors in the create method or go into the Rails console and run #transaction.errors I do get the errors:
irb(main):026:0> #transaction.errors
=> #<ActiveModel::Errors:0x0000000006447158 #base=#<Transaction id: nil, date: nil, description: nil, amount: nil, account_id: nil, created_at: nil, updated_at: nil, category_id:
nil>, #messages={:account=>["must exist", "can't be blank"], :category=>["must exist", "can't be blank"], :date=>["can't be blank"], :description=>["can't be blank"], :amount=>["can't be blank"]}, #details={:account=>[{:error=>:blank}, {:error=>:blank}], :category=>[{:error=>:blank}, {:error=>:blank}], :date=>[{:error=>:blank}], :description=>[{:error=>:blank}], :amount=>[{:error=>:blank}]}>
Something else I tried is adding these 2 lines to the new.html.erb:
<%= #transaction.errors %> renders: #<ActiveModel::Errors:0x00007ff650570500>
<%= #transaction.errors.full_messages %> renders: []
Processing by TransactionsController#create as JS, your form is being submitted using ajax, you need to render a js view or use a non-ajax request. Are you using form_with for your form? form_with defaults to remote forms, use form_for instead or update the view using a js response.
I have array selected_ids[] with items id from form, and see them in console when click on the button, but can't delete them in action.
My form:
<% for task in #tasks.where(active: true) %>
<li class="task">
<%= check_box_tag 'selected_ids[]', task.id, false, class: 'selectable' %>
<%= link_to task.title, task, class: "task-title text-dark" %>
</li>
<% end %>
</ul>
My action:
def delete_all
Task.where(id: params[:selected_ids]).destroy_all
#tasks = Task.where(user_id: current_user)
render "index"
end
My routes:
resources :tasks do
get :delete_all, on: :collection
end
Console, when click button:
Started DELETE "/tasks/delete_all" for 127.0.0.1 at 2019-01-18 10:44:18 +0200
Processing by TasksController#destroy as HTML
Parameters: {"utf8"=>"?", "authenticity_token"=>"3BlLqPDnC9IzaVqnCv6qO0KkKP7VNBU9yEnmm8eAKyb76f5eCEIYUq9Gxx4YNbtbcJo0AEi2c/ORs2E87sg0Aw==", "commit"=>"Delete selected", "selected_ids"=>["2", "1"], "id"=>"delete_all"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 1], ["LIMIT", 1]]
? app/controllers/tasks_controller.rb:2
Task Load (0.3ms) SELECT "tasks".* FROM "tasks" WHERE "tasks"."id" = ? ORDER BY "tasks"."id" ASC LIMIT ? [["id", 0], ["LIMIT", 1]]
? app/controllers/tasks_controller.rb:101
Rendering public/404.html within layouts/application
Rendered public/404.html within layouts/application (0.4ms)
Filter chain halted as :set_task rendered or redirected
Completed 404 Not Found in 682ms (Views: 676.8ms | ActiveRecord: 0.6ms)
You have a before_action called set_task in your controller. This is code that will run before delete_all. If you don't want it to run for delete_all, add , except: [:delete_all] to the before_action line:
example:
# change this line somewhere near the top of your controller
before_action :set_task, except: [:delete_all]
If the before_action is set somewhere else like ApplicationController, you can add this line to TasksController instead instead for the same effect:
skip_before_action :set_task, only: [:delete_all]
I'm running into a problem when trying to create a new object using nested resources in Rails. My routing is set up as:
resources :coins do
resources :questions
resources :events
end
When I attempt to create a new event, it does not save. Adding ! to event.save gave me an error that says "Validation failed: Coin must exist".
I don't have a validation set up for the coin (in the Event model at least, if that is what its referring to). Checking the log file shows the following, which as far as I can tell shows that the correct coin is selected:
Started GET "/coins/1/events/new" for 127.0.0.1 at 2018-01-24 18:52:19 -0500
Processing by EventsController#new as HTML
Parameters: {"coin_id"=>"1"}
[1m[36mCoin Load (0.1ms)[0m [1m[34mSELECT "coins".* FROM "coins" WHERE "coins"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
[1m[36mUser Load (0.2ms)[0m [1m[34mSELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
Rendering events/new.html.erb within layouts/application
Rendered events/new.html.erb within layouts/application (3.9ms)
Rendered layouts/_rails_defaults.html.erb (34.8ms)
Rendered layouts/_shim.html.erb (0.6ms)
Rendered layouts/_header.html.erb (6.2ms)
Completed 200 OK in 67ms (Views: 62.3ms | ActiveRecord: 0.3ms)
Started POST "/coins/1/events" for 127.0.0.1 at 2018-01-24 18:54:46 -0500
Processing by EventsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"dAXQ3uwxR2IN2rbAYD//gulHebIOdZWFPYCKnxcUKTuV4QnUp+SZYHMpZUsGOgEXQjOAnhFUO9MpJkIIAKcQlQ==", "event"=>{"content"=>"LKNA", "link"=>"asd"}, "commit"=>"Submit", "coin_id"=>"1"}
[1m[36mCoin Load (0.2ms)[0m [1m[34mSELECT "coins".* FROM "coins" WHERE "coins"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
[1m[36mUser Load (0.2ms)[0m [1m[34mSELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
[1m[35m (0.1ms)[0m [1m[36mbegin transaction[0m
[1m[35m (0.0ms)[0m [1m[31mrollback transaction[0m
Completed 401 Unauthorized in 66ms (ActiveRecord: 3.0ms)
ActiveRecord::RecordInvalid (Validation failed: Coin must exist):
app/controllers/events_controller.rb:23:in `create'
I have it set up the same way for my Questions model, as they are almost identical aside from names, and that one works with no issues. I'm at a loss as to what I'm doing incorrectly here.
controller:
class EventsController < ApplicationController
before_action :find_event, only: [:show, :edit, :update, :destroy ]
before_action :find_coin
def index
#events = Event.where(coin_id: #coin.id).order("created_at DESC")
end
def show
end
def new
#event = current_user.events.build
end
def create
#event = current_user.events.build(event_params)
if #event.save!
flash[:success] = "Event Saved"
redirect_to coin_event_path(#coin.id, #event.id)
else
flash[:notice] = "Event *NOT* Saved!!!"
render 'new'
#event.errors.full_messages
end
end
.
.
.
private
def find_event
#event = Event.find(params[:id])
end
def find_coin
#coin = Coin.find(params[:coin_id])
end
def event_params
params.require(:event).permit(:content, :link)
end
end
model:
class Event < ApplicationRecord
belongs_to :user
belongs_to :coin
end
coin model:
class Coin < ApplicationRecord
validates :link_name, :currency_name, presence: true
has_many :questions
has_many :events
end
If anyone has any idea what I may be doing wrong or any tips on how to figure it out, I would really appreciate the assistance.
Making an answer for anyone else who needs it and doesn't read comments :)
When you do:
#event = current_user.events.build(event_params)
You actually create an event related for the user, and it is not related to the coin anymore.
Try to set the coin as well for the event before saving:
#event.coin = #coin
Or you can do it the other way - build the event on the current coin, and then set the user:
#event = #coin.events.build(event_params)
#event.user = current_user
That way - the coin_id will be set automatically because you build an event on that coin.
I have solved the same issue adding 'optional: true' to belongs_to method.
belongs_to :user, optional: true
By default 'belongs_to' require the user_id.
You can use too: 'required: false'.
belongs_to :user, required: false
Good luck.
I have made a github repo that you can find here just for this question. I have 3 models:
class User < ActiveRecord::Base
has_many :user_countries
has_many :event_countries,
-> { where(user_countries: {:event => true}) },
:through => :user_countries,
:source => :country
has_many :research_countries,
-> { where(user_countries: {:research => true}) },
:through => :user_countries
:source => :country
end
class UserCountry < ActiveRecord::Base
belongs_to :country
belongs_to :user
end
class Country < ActiveRecord::Base
# ...
end
So a user should be able to choose event_countries and research_countries.
here's my users controller (nothing complicated):
class UsersController < ApplicationController
respond_to :html, :json
before_action :get_user, only: [:show, :edit, :update]
before_action :get_users, only: [:index]
def index
end
def show
end
def edit
end
def update
#user.update_attributes(user_params)
respond_with #user
end
private
def get_user
#user = User.find(params[:id])
end
def get_users
#users = User.all
end
def user_params
params.require(:user).permit(:first_name, :event_countries => [:id, :name])
end
end
And here's my user show page:
<%= best_in_place #user, :first_name %>
<p> event countries: </p>
<%= best_in_place #user, :event_countries, place_holder: "click here to edit", as: :select, collection: Country.all.map {|i| i.name} %>
<%= link_to "users index", users_path %>
So there's really nothing complicated here. I can also succesfully edit my users first name, best_in_place is working fine.
The question is: how do I edit the event_countries ? As you can see I tried to use the collection option with the countries but when I try to select a country I get the following:
Processing by UsersController#update as JSON
Parameters: {"user"=>{"event_countries"=>"3"}, "authenticity_token"=>"l5L5lXFmJFQ9kI/4klMmb5jDhjmtQXwn6amj1uwjSuE=", "id"=>"6"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 6]]
(0.1ms) begin transaction
(0.1ms) rollback transaction
Completed 500 Internal Server Error in 3ms
NoMethodError (undefined method `each' for nil:NilClass):
app/controllers/users_controller.rb:17:in `update'
I don't understand what it's doing, I know it must be a problem with the collection option. If you need to see any file please check my repo here:
https://github.com/spawnge/best_in_place_join_models_twice . I have a spent a lot of time on this any answer/suggestion would be greatly appreciated :)
update:
I have tried this:
<%= best_in_place #user, :event_country_ids, as: :select, collection: Country.all.map { |i| i.name }, place_holder: "click here to edit", html_attrs: { multiple: true } %>
and I have added :event_country_ids to my user params:
params.require(:user).permit(:first_name, :event_country_ids)
And now I can see all the countries but when I select one here's what I get:
Started PUT "/users/3" for 127.0.0.1 at 2014-12-18 01:19:27 +0000
Processing by UsersController#update as JSON
Parameters: {"user"=>{"event_country_ids"=>"1"}, "authenticity_token"=>"aZAFIHgzdSL2tlFcGtyuu+XIJW3HX2fwQGHcB9+iYpI=", "id"=>"3"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 3]]
(0.0ms) begin transaction
Country Load (0.0ms) SELECT "countries".* FROM "countries" WHERE "countries"."id" = ? LIMIT 1 [["id", 1]]
Country Load (0.0ms) SELECT "countries".* FROM "countries" INNER JOIN "user_countries" ON "countries"."id" = "user_countries"."country_id" WHERE "user_countries"."event" = 't' AND "user_countries"."user_id" = ? [["user_id", 3]]
SQL (0.1ms) DELETE FROM "user_countries" WHERE "user_countries"."user_id" = ? AND "user_countries"."country_id" = 2 [["user_id", 3]]
SQL (0.0ms) INSERT INTO "user_countries" ("country_id", "event", "user_id") VALUES (?, ?, ?) [["country_id", 1], ["event", "t"], ["user_id", 3]]
(20.9ms) commit transaction
Completed 204 No Content in 28ms (ActiveRecord: 21.3ms)
As you can see it seems that it insert the right content: INSERT INTO "user_countries" ("country_id", "event", "user_id") VALUES (?, ?, ?) [["country_id", 1], ["event", "t"], ["user_id", 3]] However I get the Completed 204 No Content just after that. I don't understand when I refresh the page the input is empty. Any suggestion ?
Update 2:
I checked in the console and it works, I can add event_countries to a user. However it doesn't display the user's event_countries when I refresh the page, I guess that's because I'm using the event_country_ids object.
I think the following code should work:
<%= best_in_place #user, :event_country_ids, as: :select,
collection: Country.all.each_with_object({}) { |i, memo| memo[i.id] = i.name },
place_holder: "click here to edit",
html_attrs: { multiple: true } %>
Assuming you want the user to be able to assign multiple event_countries.
Reference
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many, specifically the collection_singular_ids= method created by has_many.
https://github.com/bernat/best_in_place#select, the structure of the collection needs to be a hash. For each key => value pair, the key is what's submitted with the form and the value is what's displayed to the user.
http://ruby-doc.org/core-2.1.5/Enumerable.html#method-i-each_with_object, each_with_object is a part of the core Ruby library.