Why doesn't delayed job load my serialized arguments from the DB? - ruby-on-rails

When I schedule my delayed job, I do it in the usual way:
NotificationMailer.delay.notify(self, #current_user)
And if I peek at the object on the Delayed::Job queue (before the worker sees it), I can see my serialized parameter current_user in the handler:
[33] my_project ยป Delayed::Job.first
Delayed::Backend::ActiveRecord::Job Load (0.7ms) SELECT "delayed_jobs".* FROM "delayed_jobs" ORDER BY "delayed_jobs"."id" ASC LIMIT 1
=> #<Delayed::Backend::ActiveRecord::Job:0x0000010d546fb0> {
:id => 764161,
:priority => 0,
:attempts => 0,
:handler => "--- !ruby/object:Delayed::PerformableMailer\nobject: !ruby/class 'SurveyCompletionNotificationMailer'\nmethod_name: :notify\nargs:\n- !ruby/ActiveRecord:MyInstance\n attributes:\n id: 10786\n created_at: 2013-12-19 03:52:21.082910000 Z\n updated_at: 2014-07-23 19:12:27.967755000 Z\n delta: true\n- !ruby/ActiveRecord:User\n attributes:\n id: 30\n first_name: Andrew\n last_name: Anderson\n created_at: 2013-01-16 16:40:20.980147000 Z\n updated_at: 2013-01-16 16:40:20.980147000 Z\n title: Compensation Analyst\n primary_phone_number: '123456789'\n supervisor_id: \n status: modified\n disabled: \n prefix: \n suffix: ''\n department: ''\n general_info: ''\n contact_id: 4428\n emulating: true\n",
:last_error => nil,
:run_at => Wed, 23 Jul 2014 14:12:27 CDT -05:00,
:locked_at => nil,
:failed_at => nil,
:locked_by => nil,
:queue => nil,
:created_at => Wed, 23 Jul 2014 14:12:27 CDT -05:00,
:updated_at => Wed, 23 Jul 2014 14:12:27 CDT -05:00
}
But I find that Delayed Job will ignore the serialized argument data and reload the User record from the database. So if the User record changes between when the job is queued and when it executes, the new data will be used. This is not what I want. Why even bother the serialize the parameters if you're not going to use it? Am I missing something? Any way to get DJ to use the serialized argument data?

This is the behavior of DJ, it just uses the deserialized id to load the ActiveRecord object from DB (here is a related FAQ entry). If you want it to do something different, you would either need to monkeypatch ActiveRecord, or to pass the attributes as additional parameters to the mailer.

Related

Rails, how to merge two constraints with has_many :through

I want to constraint scope of attachments to current_user attachments or attachments that are not yet assigned to comment, to be able to find later
Attachment has_one :comment, optional: true
Attachment has_one :user, through :comment, comment - through task, task - through project
> user.attachments.to_sql
=> "SELECT \"attachments\".* FROM \"attachments\" INNER JOIN \"comments\" ON \"attachments\".\"comment_id\" = \"comments\".\"id\" INNER JOIN \"tasks\" ON \"comments\".\"task_id\" = \"tasks\".\"id\" INNER JOIN \"projects\" ON \"tasks\".\"project_id\" = \"projects\".\"id\" WHERE \"projects\".\"user_id\" = $1 ORDER BY \"comments\".\"created_at\" ASC, tasks.position"
How to make it work
> user.attachments.or(Attachment.where(comment_id: nil))
Relation passed to #or must be structurally compatible. Incompatible values: [:order, :joins, :references]
I have tried
without_comment = Attachment.joins(comment: [task: [:project]]).where(comment_id: nil).order('"comments"."created_at" ASC, tasks.position')
user.attachments.or(without_comment)
[35] pry(#<AttachmentResource>)> without_comment.to_sql
=> "SELECT \"attachments\".* FROM \"attachments\" INNER JOIN \"comments\" ON \"comments\".\"id\" = \"attachments\".\"comment_id\" INNER JOIN \"tasks\" ON \"tasks\".\"id\" = \"comments\".\"task_id\" INNER JOIN \"projects\" ON \"projects\".\"id\" = \"tasks\".\"project_id\" WHERE \"attachments\".\"comment_id\" IS NULL ORDER BY \"comments\".\"created_at\" ASC, tasks.position"
[36] pry(#<AttachmentResource>)> user.attachments.or(without_comment)
ArgumentError: Relation passed to #or must be structurally compatible. Incompatible values: [:order, :joins, :references]
from /home/bjorn/.rvm/gems/ruby-2.4.1#ngrx-todolist/gems/activerecord-5.1.1/lib/active_record/relation/query_methods.rb:655:in `or!'
I want to find all current_user attachments plus attachments without owner -> no parent comment
Query Attachment.joins(comment: [task: [:project]]).where('projects.user_id = ? OR attachments.comment_id IS NULL', user.id) doesnt include attachments without comment, why?
[10] pry(#<CommentResource>)> Attachment.all
=> [#<Attachment:0x00000008a6f3a8
id: 1,
file: "attachments.rb",
comment_id: 1,
created_at: Sun, 21 May 2017 14:18:21 UTC +00:00,
updated_at: Sun, 21 May 2017 14:18:21 UTC +00:00>,
#<Attachment:0x00000008a6f268
id: 2,
file: "attachments.rb",
comment_id: 1,
created_at: Sun, 21 May 2017 14:18:21 UTC +00:00,
updated_at: Sun, 21 May 2017 14:18:21 UTC +00:00>,
#<Attachment:0x00000008a6f128
id: 3,
file: "attachments.rb",
comment_id: nil,
created_at: Sun, 21 May 2017 14:29:51 UTC +00:00,
updated_at: Sun, 21 May 2017 14:29:51 UTC +00:00>]
[11] pry(#<CommentResource>)> Attachment.joins(comment: [task: [:project]]).where('projects.user_id = ? OR attachments.comment_id IS NULL', user.id).to_sql
=> "SELECT \"attachments\".* FROM \"attachments\" INNER JOIN \"comments\" ON \"comments\".\"id\" = \"attachments\".\"comment_id\" INNER JOIN \"tasks\" ON \"tasks\".\"id\" = \"comments\".\"task_id\" INNER JOIN \"projects\" ON \"projects\".\"id\" = \"tasks\".\"project_id\" WHERE (projects.user_id = 1 OR attachments.comment_id IS NULL)"
[12] pry(#<CommentResource>)> Attachment.joins(comment: [task: [:project]]).where('projects.user_id = ? OR attachments.comment_id IS NULL', user.id)
=> [#<Attachment:0x0000000891c7a8
id: 1,
file: "attachments.rb",
comment_id: 1,
created_at: Sun, 21 May 2017 14:18:21 UTC +00:00,
updated_at: Sun, 21 May 2017 14:18:21 UTC +00:00>,
#<Attachment:0x0000000891c668
id: 2,
file: "attachments.rb",
comment_id: 1,
created_at: Sun, 21 May 2017 14:18:21 UTC +00:00,
updated_at: Sun, 21 May 2017 14:18:21 UTC +00:00>]
Thanks to #fanta
class AttachmentPolicy < ApplicationPolicy
class Scope < Scope
def resolve
# user can see his attachments and attachments without owner
Attachment
.left_outer_joins(comment: [task: [:project]])
.where('projects.user_id = ? OR comment_id IS NULL', user.id)
end
end
def update?
# if attachment doesnt have owner - anyone can change it
record.comment.nil? ? true : owner?
end
end

Rails: find attachments without user OR without comment

Attachment has_one :comment, optional: true
Attachment has_one :user, through :comment, comment - through task, task - through project
I want to find all current_user attachments plus attachments without owner -> no parent comment
Query Attachment.joins(comment: [task: [:project]]).where('projects.user_id = ? OR attachments.comment_id IS NULL', user.id) doesnt include attachments without comment, why?
[10] pry(#<CommentResource>)> Attachment.all
=> [#<Attachment:0x00000008a6f3a8
id: 1,
file: "attachments.rb",
comment_id: 1,
created_at: Sun, 21 May 2017 14:18:21 UTC +00:00,
updated_at: Sun, 21 May 2017 14:18:21 UTC +00:00>,
#<Attachment:0x00000008a6f268
id: 2,
file: "attachments.rb",
comment_id: 1,
created_at: Sun, 21 May 2017 14:18:21 UTC +00:00,
updated_at: Sun, 21 May 2017 14:18:21 UTC +00:00>,
#<Attachment:0x00000008a6f128
id: 3,
file: "attachments.rb",
comment_id: nil,
created_at: Sun, 21 May 2017 14:29:51 UTC +00:00,
updated_at: Sun, 21 May 2017 14:29:51 UTC +00:00>]
[11] pry(#<CommentResource>)> Attachment.joins(comment: [task: [:project]]).where('projects.user_id = ? OR attachments.comment_id IS NULL', user.id).to_sql
=> "SELECT \"attachments\".* FROM \"attachments\" INNER JOIN \"comments\" ON \"comments\".\"id\" = \"attachments\".\"comment_id\" INNER JOIN \"tasks\" ON \"tasks\".\"id\" = \"comments\".\"task_id\" INNER JOIN \"projects\" ON \"projects\".\"id\" = \"tasks\".\"project_id\" WHERE (projects.user_id = 1 OR attachments.comment_id IS NULL)"
[12] pry(#<CommentResource>)> Attachment.joins(comment: [task: [:project]]).where('projects.user_id = ? OR attachments.comment_id IS NULL', user.id)
=> [#<Attachment:0x0000000891c7a8
id: 1,
file: "attachments.rb",
comment_id: 1,
created_at: Sun, 21 May 2017 14:18:21 UTC +00:00,
updated_at: Sun, 21 May 2017 14:18:21 UTC +00:00>,
#<Attachment:0x0000000891c668
id: 2,
file: "attachments.rb",
comment_id: 1,
created_at: Sun, 21 May 2017 14:18:21 UTC +00:00,
updated_at: Sun, 21 May 2017 14:18:21 UTC +00:00>]
The joins method on active record queries the records by using inner join, which means in your case it only returns attachments with comments. If you want to include attachments without comments, you need to left outer join.
http://guides.rubyonrails.org/active_record_querying.html#left-outer-joins

Rails Column in Schema but not Showing Up in Console

I recently ran a migration to add a "Rank" column as a float to a model called jokes (it's a reddit-type page on the app). It shows up in my schema, which means to me that the migration was successful, but when I try to use it to order things via the controller or when I look for that column in my rails console I don't see it.
Here's an example of the controller ordering that doesn't work:
#jokes = Joke.where(kids: true, approved: true).order("rank DESC")
And here's the console output which neglects the new "rank" column:
[54] pry(main)> Joke.last
Joke Load (4.7ms) SELECT "jokes".* FROM "jokes" ORDER BY "jokes"."id" DESC LIMIT 1
=> #<Joke:0x007fb52f927e58
id: 16,
title: "Test Joke",
body: "Tested",
kids: false,
mixed: false,
approved: true,
rejected: nil,
user_id: 1,
created_at: Sat, 13 Aug 2016 19:40:14 UTC +00:00,
updated_at: Sat, 13 Aug 2016 19:40:18 UTC +00:00>
Can anyone help me figure out what's going on here?

Rails 4: .save does not update updated_at for existing records?

I always thought .save and .save! would update the updated_at column of existing records. Is this not true? If so, then do I need to create a before_save filter to update it everytime I save?
Today is June 18th:
Loading development environment (Rails 4.1.1)
irb(main):001:0> o = Order.last
Order Load (0.4ms) SELECT `orders`.* FROM `orders` ORDER BY `orders`.`id` DESC LIMIT 1
=> #<Order id: 24, user_id: 1, order_date: nil, total_cents: 0, total_currency: "USD", shipping_cents: 0, shipping_currency: "USD", tax_cents: 0, tax_currency: "USD", subtotal_cents: 0, subtotal_currency: "USD", created_at: "2014-06-13 21:24:38", updated_at: "2014-06-13 21:24:38", status: "Incomplete", coupon_id: nil>
irb(main):002:0> o.save!
(0.4ms) BEGIN
(0.3ms) COMMIT
=> true
irb(main):003:0> o.updated_at
=> Fri, 13 Jun 2014 21:24:38 UTC +00:00
irb(main):004:0> o.reload
Order Load (0.7ms) SELECT `orders`.* FROM `orders` WHERE `orders`.`id` = 24 LIMIT 1
=> #<Order id: 24, user_id: 1, order_date: nil, total_cents: 0, total_currency: "USD", shipping_cents: 0, shipping_currency: "USD", tax_cents: 0, tax_currency: "USD", subtotal_cents: 0, subtotal_currency: "USD", created_at: "2014-06-13 21:24:38", updated_at: "2014-06-13 21:24:38", status: "Incomplete", coupon_id: nil>
irb(main):005:0> o.updated_at
=> Fri, 13 Jun 2014 21:24:38 UTC +00:00
The reason I care about this is because I am wrapping the internals of a function with ActiveRecord::Base.transaction so I want to test that they are not updated when something goes wrong:
Rspec test (that passes all the time when it's not supposed to, since updated_at isn't being changed regardless of success or failure):
it "rollsback the transaction" do
...
order.stub(:save!).and_raise(StandardError)
expect { payment_info.save }.not_to change { [billing_address.updated_at,
shipping_address.updated_at,
order.user.updated_at,
order.updated_at] }
end
And my method:
def process_billing_info!
ActiveRecord::Base.transaction do
billing_address.save!
shipping_address.save!
user.save!
order.update_all_fees!
end
rescue Exception => e
false
end
As you can see in your log, there is no UPDATE SQL query that is executed. Rails is not updating your record at all. This is because .save actually saves the record only if changes were made.
There is the method .touch (Documentation) that you can call in order to update the updated_at field without having to do changes to your record:
1.9.3p489 :005 > Intervention.first.touch
Intervention Load (12.9ms) SELECT "interventions".* FROM "interventions" LIMIT 1
SQL (20.5ms) UPDATE "interventions" SET "updated_at" = '2014-06-18 16:34:03.924648' WHERE "interventions"."id" = 1
=> true
Here we see the UPDATE SQL query.
In companion with #MrYoshiji's answer, here's another way to use .touch:
if o.save!
o.touch unless o.changed?
end

how to override model validation messages in rails 3

I have a model,group_question_answer.rb
class GroupQuestionAnswer < ActiveRecord::Base
belongs_to :group_question
validates_presence_of :answer
validates_presence_of :answer_question
end
for attribute answer and answer_question i get error message as Group question answers answer can't be blank
I need to show only answer cant be blank.i even tried adding :message=>"cant be blank",but still i dont get my required message.how can i remove model name and can just arrtibute error message ....
class GroupQuestionAnswer < ActiveRecord::Base
attr_accessible :answer
validate do |group_question_answer|
errors.add(:base, "answer can't be blank") if group_question_answer.answer.blank?
end
end
works perfectly
rails c
Loading development environment (Rails 3.2.9)
irb(main):001:0> q = GroupQuestionAnswer.create
(0.1ms) begin transaction
(0.1ms) rollback transaction
=> #<GroupQuestionAnswer id: nil, answer: nil, created_at: nil, updated_at: nil>
irb(main):002:0> q
=> #<GroupQuestionAnswer id: nil, answer: nil, created_at: nil, updated_at: nil>
irb(main):003:0> q.save
(0.1ms) begin transaction
(0.1ms) rollback transaction
=> false
irb(main):004:0> q.errors
=> #<ActiveModel::Errors:0x007fc2fb325fa8 #base=#<GroupQuestionAnswer id: nil, answer: nil, created_at: nil, updated_at: nil>, #messages={:base=>["answer can't be blank"]}>
irb(main):006:0> q.errors.messages
=> {:base=>["answer can't be blank"]}
=> {:base=>["answer can't be blank"]}
irb(main):007:0> q = GroupQuestionAnswer.create(answer: "123")
(0.1ms) begin transaction
SQL (9.0ms) INSERT INTO "group_question_answers" ("answer", "created_at", "updated_at") VALUES (?, ?, ?) [["answer", "123"], ["created_at", Fri, 28 Dec 2012 11:01:38 UTC +00:00], ["updated_at", Fri, 28 Dec 2012 11:01:38 UTC +00:00]]
(1.1ms) commit transaction
=> #<GroupQuestionAnswer id: 1, answer: "123", created_at: "2012-12-28 11:01:38", updated_at: "2012-12-28 11:01:38">
irb(main):008:0> q.errors.messages
=> {}
In my opinion validate method perfect way to fully customize rails validations and that does exactly what you ask for.
You can try like this:
validates :answer, presence: { message: '<Your message>'}
validates :answer_question, presence: { message: '<Your message>'}
Try adding them in your config/locales/en.yml file
As there you can do something like this,
en:
errors:
messages:
answer: answer can't be blank

Resources