I'm trying to push values onto a serialized text field (acting as array).
In the controller I have
class DeliveriesController < ApplicationController
def new
#delivery = Delivery.new
end
def create
#user = current_user
#user.deliveries.create(params[:delivery])
#user.recent_addresses.shift if #user.recent_addresses.size >= 10
#user.recent_addresses.push(params[:delivery][:from_address])
#user.save
redirect_to root_path
end
end
User model
serialize :recent_addresses, Array
attr_accessible :recent_addresses
has_many :deliveries
The problem is that the user is not being saved with the new recent addresses. The from_address is being added within the controller but when I try save it rollsback and the recent addresses array is empty.
Parameters: {"delivery"=>{"from_address"=>"xyz"}, "commit"=>"Submit"}
SQL (0.6ms) INSERT INTO "deliveries" ("created_at", "from_address", "user_id") VALUES ($1, $2) RETURNING "id" [["created_at", Fri, 25 Oct 2013 13:21:50 UTC +00:00], ["from_address", "xyz"], ["user_id", 1]]
(0.8ms) COMMIT
(0.1ms) BEGIN
(0.2ms) ROLLBACK
Redirected to http://localhost:3000/
Related
On my test I'm seeing that after_update is been called after calling FactoryBot.create(:object). Is it normal? As far as I know, it should be called only when a record gets updated, no?
I can see someone reporting this as a bug, with a good explanation here.
To take the essentials from this, if your factory is adding an association (this is an assumption at this stage - if you could add a little more to your question, that'd be great), the code runs as follows:
Example factory
FactoryGirl.create(
:user,
:account => FactoryGirl.create(:account)
)
How this is invoked:
account = Account.new
account.save! # Since this is Ruby, it'll evaluate this line as part of the hash first, before creating the user
user = User.new
user.account = account
user.save! # The hash has been evaluated and we're assigning the account created from the hash
So, if you have an association in there, the account, in this case, would be created, then updated as the association is saved.
To setup your factory to overcome this, you can use the following:
factory :user do
factory :user_with_account do
after_create do |user|
FactoryGirl.create(:account, :user => user)
end
end
end
factory :account do
user
end
How does that apply to your setup? Have a shot and see if it provides a solution - let me know how you get on :)
after_update will only be called when the object is updated, however if your factory has associations or after_create actions, these will often cause the model to be updated, causing after_update to be triggered.
An example, using ActiveRecord 5:
class Client < ApplicationRecord
after_create :ping
after_update :pong
def ping
logger.info("---> after_create HOOK CALLED")
end
def pong
logger.info("---> after_update HOOK CALLED")
end
end
Creating and updating the object act as expected:
c = Client.create!(name: "test")
# (0.4ms) BEGIN
# Client Create (1.4ms) INSERT INTO "clients" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["name", "test"], ["created_at", "2018-05-24 17:06:24.076085"], ["updated_at", "2018-05-24 17:06:24.076085"]]
# ---> after_create HOOK CALLED
# (4.0ms) COMMIT
c.update! name: "test2"
# (0.8ms) BEGIN
# Client Update (2.3ms) UPDATE "clients" SET "name" = $1, "updated_at" = $2 WHERE "clients"."id" = $3 [["name", "test2"], ["updated_at", "2018-05-24 17:06:36.525448"], ["id", "a3d49153-2f25-48c3-8319-61c2fb6ea173"]]
# ---> after_update HOOK CALLED
# (0.9ms) COMMIT
]
And FactoryBot behaves the same:
FactoryBot.create(:client)
# (1.2ms) BEGIN
# Client Create (0.9ms) INSERT INTO "clients" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["name", "Montana tigers"], ["created_at", "2018-05-24 17:11:57.138995"], ["updated_at", "2018-05-24 17:11:57.138995"]]
# ---> after_create HOOK CALLED
# (1.1ms) COMMIT
I am uploading legacy articles to my Rails app. I am sending created_at as a parameter in my request as recommended in this answer. However, this attribute seemingly is not passed "through". I can puts(params[:created_at]) and see my custom created_at, yet in the logs the article is INSERTed with a created_at of the current timestamp.
Here is my articles controller:
class ArticlesController < ApplicationController
before_action :set_article, only: [:show, :update, :destroy]
...
# POST /articles
def create
#section = Section.friendly.find(params[:section_id])
# Can't let people publish by default
#article = #section.articles.build(
article_params.merge(is_published: false)
)
if #article.save
render json: #article, status: :created, location: #article
else
render json: #article.errors, status: :unprocessable_entity
end
end
end
My request is:
http POST :3000/articles title='example' section_id=1 content="<p>the section exists.</p>" slug="example" created_at="2017-06-109T17:57:55.149-05:00"
The logs:
Started POST "/articles" for 127.0.0.1 at 2017-11-24 12:05:06 -0500
Processing by ArticlesController#create as HTML
Parameters: {"title"=>"example", "section_id"=>"1", "content"=>"<p>the section exists.</p>", "slug"=>"example", "created_at"=>"2017-06-109T17:57:55.149-05:00", "article"=>{"title"=>"example", "slug"=>"example", "content"=>"<p>the section exists.</p>", "created_at"=>"2017-06-109T17:57:55.149-05:00", "section_id"=>"1"}}
Section Load (0.3ms) SELECT "sections".* FROM "sections" WHERE "sections"."slug" = $1 ORDER BY "sections"."id" ASC LIMIT $2 [["slug", "1"], ["LIMIT", 1]]
Section Load (0.4ms) SELECT "sections".* FROM "sections" WHERE "sections"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
(0.2ms) BEGIN
SQL (0.6ms) INSERT INTO "articles" ("title", "slug", "content", "is_published", "created_at", "updated_at", "section_id") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["title", "example"], ["slug", "example"], ["content", "<p>the section exists.</p>"], ["is_published", "f"], ["created_at", "2017-11-24 12:05:06.175751"], ["updated_at", "2017-11-24 12:05:06.175751"], ["section_id", 1]]
(0.8ms) COMMIT
Completed 201 Created in 38ms (Views: 1.1ms | ActiveRecord: 8.0ms)
In my schema.rb:
create_table "articles", force: :cascade do |t|
...
t.datetime "created_at", null: false
My model has no extra methods/callbacks that would ruin the request. It only contains relationships. I don't know if this is relevant, but I saw it in a GitHub issue somewhere: articles and users are in a many2many with an authorships model, and I use devise to authenticate users.
In conclusion, the app is receiving the created_at param just fine, but it is overridden with the default timestamp. If I set record_timestamps to false, the created_at just becomes nil.
Why is my created_at just seemingly ignored?
Rails 5.1, Ruby 2.4.2, Postgres 10.1
Answer given by max: there was a syntatical error in my timestamp. It was not valid, and was ignored by Rails.
I have a problem with nested attributes. Creating works but when I update, the error message shows me that the values in the relation are not set. I can't find the reason.
The main model
class Product < ActiveRecord::Base
has_many :product_options
accepts_nested_attributes_for :product_options,
:allow_destroy => true,
:reject_if => proc { | r | r["name"].blank? or r["value"].blank? }
end
The nested model
class ProductOption < ActiveRecord::Base
belongs_to :product
validates :name, :presence => true
validates :value, :presence => true
end
The controller is a bit shorted.
Items is a model where Product is related to as has_one
class Admin::ProductsController < Admin::ApplicationController
before_action :set_product, only: [ :new, :show, :edit, :update, :destroy ]
def create
#product = Product.new( product_params )
respond_to do |format|
if #product.save
#product.product_options.build
format.js { render :js => "alert( 'Daten gespeichert!' );" }
else
format.js { render :js => 'alert( "Fehler beim Speichern!" );' }
end
end
end
def update
respond_to do |format|
if #product.update( product_params )
format.js { render :js => "alert( 'Daten gespeichert!' );" }
else
require "pp"
pp #product.errors
format.js { render :js => 'alert( "Fehler beim Speichern!" );' }
end
end
end
private
# UPDATE: creating the product_options at this time
# produces the described error :)
def set_product
#item = Item.find_by_id( params[ :item_id ] ) if params[ :item_id ]
#product = #item.product ? #item.product : #item.build_product
# WRONG Place for generating new options
# 2.times { #product.product_options.build }
end
def product_params
params.require( :product ).permit( :item_id, :name, :title, :active, :product_options_attributes => [ :id, :name, :value, :_destroy ] )
end
end
The console output for the create is working and looks like
Started POST "/admin/items/653/product" for 127.0.0.1 at 2014-02-14 15:12:14 +0100
Processing by Admin::ProductsController#create as JS
Parameters: {"utf8"=>"✓", "product"=>{"item_id"=>"653", "name"=>"1", "title"=>"1", "active"=>"1", "product_options_attributes"=>{"0"=>{"name"=>"aaa", "value"=>"aaaa"}}}, "commit"=>"Create Product", "item_id"=>"653"}
User Load (1.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = 6 ORDER BY "users"."id" ASC LIMIT 1
(0.6ms) BEGIN
SQL (17.5ms) INSERT INTO "products" ("created_at", "item_id", "name", "title", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["created_at", Fri, 14 Feb 2014 14:12:14 UTC +00:00], ["item_id", 653], ["name", "1"], ["title", "1"], ["updated_at", Fri, 14 Feb 2014 14:12:14 UTC +00:00]]
SQL (1.3ms) INSERT INTO "product_options" ("created_at", "name", "product_id", "updated_at", "value") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["created_at", Fri, 14 Feb 2014 14:12:14 UTC +00:00], ["name", "aaa"], ["product_id", 28], ["updated_at", Fri, 14 Feb 2014 14:12:14 UTC +00:00], ["value", "aaaa"]]
Item Load (1.0ms) SELECT "items".* FROM "items" WHERE "items"."id" = $1 ORDER BY "items"."id" ASC LIMIT 1 [["id", 653]]
ProductOption Load (1.3ms) SELECT "product_options".* FROM "product_options" WHERE "product_options"."product_id" = $1 [["product_id", 28]]
Rendered admin/products/_show.html.erb (7.6ms)
Rendered admin/products/create.js.erb (9.2ms)
Completed 200 OK in 448ms (Views: 40.0ms | ActiveRecord: 27.1ms)
The the update. I doesn't work and gives an error that the nested fields are empty. It's the pp inside the update method
Started PATCH "/admin/items/653/product" for 127.0.0.1 at 2014-02-14 15:15:03 +0100
Processing by Admin::ProductsController#update as JS
Parameters: {"utf8"=>"✓", "product"=>{"item_id"=>"653", "name"=>"1", "title"=>"1", "active"=>"1", "product_options_attributes"=>{"0"=>{"name"=>"aaa", "value"=>"aaaa", "id"=>"9"}, "1"=>{"name"=>"bbb", "value"=>"bbbb"}}}, "commit"=>"Update Product", "item_id"=>"653"}
User Load (1.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 6 ORDER BY "users"."id" ASC LIMIT 1
Item Load (0.6ms) SELECT "items".* FROM "items" WHERE "items"."id" = 653 LIMIT 1
Product Load (0.9ms) SELECT "products".* FROM "products" WHERE "products"."item_id" = $1 ORDER BY "products"."id" ASC LIMIT 1 [["item_id", 653]]
(0.6ms) BEGIN
ProductOption Load (1.3ms) SELECT "product_options".* FROM "product_options" WHERE "product_options"."product_id" = $1 AND "product_options"."id" IN (9) [["product_id", 28]]
(0.5ms) ROLLBACK
#<ActiveModel::Errors:0x007f8bdeb9f818
#base=
#<Product id: 28, item_id: 653, content: nil, active: 1, created_at: "2014-02-14 14:12:14", updated_at: "2014-02-14 14:12:14", name: "1", title: "1", ordernumber: "">,
#messages=
{:"product_options.name"=>["can't be blank"],
:"product_options.value"=>["can't be blank"]}>
Completed 200 OK in 18ms (Views: 0.1ms | ActiveRecord: 5.1ms)
I think I know what is the problem with your code. The accepts_nested_attributes_for does not require you to build any of the associated models. If the appropriate params are passed in then the model automatically builds or updates the associations.
In your case in the update method what you do is the following:
You find the relevant product. So far so good (although you could actually use a specific product id in your form)
Then you build two product options (in #set_product). This is the problem.
And in the end you update the model based on the parameters.
Now the problem with the second step is that you basically build two empty associated instances. Those are not affected by the accepts_nested_attributes. As a result you are trying to save 2+2 product options (the ones you build and the ones created by the params). Obviously you get the validation error due to the fact the two of the models have no attributes set.
You can make sure my hypothesis is correct by removing the validators from ProductOption. On update you should get 4 associated product options persisted.
I have this in the Model:
after_create do |comment|
CommentMailer.comment_email(self).deliver
end
This in CommentMailer:
class CommentMailer < ActionMailer::Base
helper ActionView::Helpers::UrlHelper
include CommentHelper
helper :comment
def comment_email(user, comment, commentable)
mail(to: user.email,
subject: "You have left a comment",
from: "comments#lumeo.com",
bcc: "brian#lumeo.com")
end
end
And this in CommentHelper:
module CommentHelper
def find_commentable
#comment = Comment.find(params[:comment])
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
end
I'm getting this error:
Started POST "/requests/6/comments" for 127.0.0.1 at 2012-11-30 17:28:55 -0800
Processing by CommentsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"R62NH5/EE34FPapEqy7mfpa0wKz18GtSdhH8MGYq2Ec=", "comment"=>{"content"=>"post", "show"=>"true"}, "commit"=>"Create Comment", "request_id"=>"6"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 2 ORDER BY users.created_at DESC LIMIT 1
Request Load (0.3ms) SELECT "requests".* FROM "requests" WHERE "requests"."id" = $1 LIMIT 1 [["id", "6"]]
CACHE (0.0ms) SELECT "requests".* FROM "requests" WHERE "requests"."id" = $1 LIMIT 1 [["id", "6"]]
(0.1ms) BEGIN
SQL (0.4ms) INSERT INTO "comments" ("commentable_id", "commentable_type", "content", "created_at", "show", "updated_at", "user_id") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["commentable_id", 6], ["commentable_type", "Request"], ["content", "post"], ["created_at", Sat, 01 Dec 2012 01:28:55 UTC +00:00], ["show", true], ["updated_at", Sat, 01 Dec 2012 01:28:55 UTC +00:00], ["user_id", 2]]
(0.2ms) ROLLBACK
Completed 500 Internal Server Error in 136ms
ArgumentError (wrong number of arguments (1 for 3)):
app/mailers/comment_mailer.rb:5:in `comment_email'
app/models/comment.rb:27:in `block in <class:Comment>'
app/controllers/comments_controller.rb:22:in `create'
Looks like simple typos.
Line 7, as noted in the exception:
commentable = #comment.commentable
So, the issues:
You're calling #comment.commentabe, but #comment is nil
Hence the error: undefined method 'commentable' for nil:NilClass
#comment is nil in your mailer method because you're passing it in as comment NOT #comment, yet you're trying to reference it as #comment.
Also, why are you passing in commentable as a parameter, but on line 7 you're setting commentable again - this is redundant? Just use the already available commentable variable that you're passing in as a param. In fact, you seem to be doing this with several variables, yet I can't tell (because you don't show the mailer template) whether or not you're actually using them.
It could be that you could use something simpler like:
So, this should (probably) work:
def comment_email(user, comment, commentable)
mail(to: user.email,
subject: "You have left a comment",
from: "comments#lumeo.com",
bcc: "brian#lumeo.com")
end
If you post your mail template (so I can see what the body of the email looks like) I can help you get the variables into the template.
I used uuid_id gem to generate UUID for various models in my application(Rails 3.2.5 with Ruby 1.9.3p194). I correctly followed the steps given here
I am getting this error while creating the model tagged as uuid_it. It works fine if I remove uuid_it call from the model
Here is snapshot of the log
SQL (2.1ms) INSERT INTO "applications" ("created_at", "description", "name", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["created_at", Mon, 03 Sep 2012 10:17:59 UTC +00:00], ["description", "data mining"], ["name", "BlueBerry"], ["updated_at", Mon, 03 Sep 2012 10:17:59 UTC +00:00]]
Uuid Load (1.2ms) SELECT "uuids".* FROM "uuids" WHERE "uuids"."uuidable_id" = 5 AND "uuids"."uuidable_type" = 'Application' LIMIT 1
(0.2ms) ROLLBACK
Completed 500 Internal Server Error in 81ms
ActiveModel::MassAssignmentSecurity::Error (Can't mass-assign protected attributes: uuid):
app/controllers/applications_controller.rb:13:in `create'
where line 13 is application.create
EDIT
app/controllers/applications.rb
class ApplicationsController < ApplicationController
# some code here
def create
#application = Application.new(params[:application])
#application.creator = current_user
#application.organization = current_organization
if #application.save
redirect_to #application, notice: "Successfully created application."
else
render action: 'new'
end
end
# few lines here
end