Different result in rails console and in puts - ruby-on-rails

When I use puts(#participantt = Participant.where(id: 1)) then in the console I get
Participant Load (0.3ms) SELECT "participants".* FROM "participants" WHERE "participants"."id" = $1 [["id", 1]]
↳ app/controllers/interviews_controller.rb:119:in `puts'
#<Participant:0x000000000c778bf0>
But if I type #participantt = Participant.where(id: 1) in rails console then I get
Participant Load (0.7ms) SELECT "participants".* FROM "participants" WHERE "participants"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Participant id: 1, name: "Ram", email: "Ram#g.com", created_at: "2020-05-08 08:19:00", updated_at: "2020-05-08 08:19:00">]>
Why is this so?

puts calls to_s on before printing result of expression(which will be an object) passed to it. It generally prints class name with it's object id.
Here, result of #participant = Participant.where(id: 1) is the Participant with id and get stored in instance variable #participant
Passing #participant to puts will first call #participant.to_s before printing it.
In case of just #participant = Participant.where(id: 1), the console shows the result which is typical REPL utility does. And if you do puts #participant there after then you will again get same as what you get with puts in you question.

Related

How to avoid destroy deleting earlier related object

If the user already has a favorite model after log in, then I want the products that were added to their anonymous bookmarks to be overwritten in the favorite that is attached to their user object.
I find session_favorite (anonymous favorite), #favorite, which belongs to the user. I am rewriting line_item.favorite with the ID of session_favorite to the user's ID favorite. I reset the session and destroy that anonymous favorite, which in theory has become empty, since now line_items has the user's favorite ID.
But, for some reason, when destroying a session_favorite, line_items are also deleted. Why?
module CurrentFavorite
def set_favorite
if user_signed_in?
set_user_favorite
else
if session[:favorite]
#favorite = Favorite.find(session[:favorite])
else
#favorite = Favorite.create
session[:favorite] = #favorite.id
end
end
end
def set_user_favorite
if session[:favorite]
if current_user.favorite.nil?
#favorite = Favorite.find(session[:favorite])
session[:favorite] = nil
#favorite.update(user: current_user)
else
#block with error
session_favorite = Favorite.find(session[:favorite])
#favorite = Favorite.find_by(user: current_user)
session_favorite.line_items.each do |line_item|
line_item.update(favorite: #favorite)
end
session[:favorite] = nil
session_favorite.destroy
#favorite
end
else
if current_user.favorite.nil?
#favorite = current_user.build_favorite
#favorite.save
else
#favorite = Favorite.find_by(user: current_user)
end
end
end
end
LineItem Update (0.7ms) UPDATE "line_items" SET "favorite_id" = ?, "updated_at" = ? WHERE "line_items"."id" = ? [["favorite_id", 1], ["updated_at", "2020-06-07 05:19:54.653064"], ["id", 13]]
↳ app/models/concerns/current_favorite.rb:31
(12.9ms) commit transaction
↳ app/models/concerns/current_favorite.rb:31
(0.0ms) begin transaction
↳ app/models/concerns/current_favorite.rb:31
Movie Load (0.1ms) SELECT "movies".* FROM "movies" WHERE "movies"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
↳ app/models/concerns/current_favorite.rb:31
LineItem Update (0.7ms) UPDATE "line_items" SET "favorite_id" = ?, "updated_at" = ? WHERE "line_items"."id" = ? [["favorite_id", 1], ["updated_at", "2020-06-07 05:19:54.674229"], ["id", 14]]
↳ app/models/concerns/current_favorite.rb:31
(3.6ms) commit transaction
↳ app/models/concerns/current_favorite.rb:31
(0.0ms) begin transaction
↳ app/models/concerns/current_favorite.rb:34
LineItem Destroy (0.5ms) DELETE FROM "line_items" WHERE "line_items"."id" = ? [["id", 13]]
↳ app/models/concerns/current_favorite.rb:34
LineItem Destroy (0.1ms) DELETE FROM "line_items" WHERE "line_items"."id" = ? [["id", 14]]
↳ app/models/concerns/current_favorite.rb:34
Favorite Destroy (0.2ms) DELETE FROM "favorites" WHERE "favorites"."id" = ? [["id", 10]]
↳ app/models/concerns/current_favorite.rb:34
session_favorite still has the set of line_items associated with it. For performance reasons, Rails does not automatically reload the line_items before destroying the session_favorite so it doesn't recognize that the line_items are now associated with a different favorite.
To prevent the line_items being destroyed, refresh the association before doing the destroy:
session_favorite.line_items.reload
session_favorite.destroy

Rails don't rollback and don't update attribute either

In the terminal (later I need to add this to the migration) I want to update total_campaign_codes_amount which represents all campaign codes for each credit campaign. This field is a counter_cache and to do so in terminal I've got this script:
CreditCampaign.all.each do |cc|
cc.update!(
total_campaign_codes_amount: cc.campaign_codes.count
)
end
I didn't received any errors in my sql, everything should worked but these values are not saved to - total_campaign_codes_amount for each CreditCampaign shows 0.
CreditCampaign Update (0.2ms) UPDATE "credit_campaigns" SET "updated_at" = $1 WHERE "credit_campaigns"."id" = $2 [["updated_at", "2019-10-14 15:26:54.010779"], ["id", 1]]
(0.4ms) COMMIT
(0.2ms) SELECT COUNT(*) FROM "campaign_codes" WHERE "campaign_codes"."credit_campaign_id" = $1 [["credit_campaign_id", 2]]
(0.1ms) BEGIN
CreditCampaign Update (0.2ms) UPDATE "credit_campaigns" SET "updated_at" = $1 WHERE "credit_campaigns"."id" = $2 [["updated_at", "2019-10-14 15:26:54.013239"], ["id", 2]]
(0.2ms) COMMIT
=> [#<CreditCampaign id: 1, amount: 0.25e3, interest_rate: 0.12e2, installment_amount: 0.1e3, duration_in_months: 24, start_date: "2019-10-10", end_date: "2019-11-10", created_at: "2019-10-10 14:54:27", updated_at: "2019-10-14 15:26:54", name: "Credit Campaign #1", total_campaign_codes_amount: 15, used_campaign_codes_amount: 6>, #<CreditCampaign id: 2, amount: 0.1e1, interest_rate: 0.1e1, installment_amount: 0.1e1, duration_in_months: 1, start_date: "2011-01-01", end_date: "2020-01-01", created_at: "2019-10-14 13:40:02", updated_at: "2019-10-14 15:26:54", name: "test", total_campaign_codes_amount: 10, used_campaign_codes_amount: 0>]
I was trying to use update_attributes! instead of update! and find_each instead of all.each but without any results.
You can not update counter cache column in Rails, because it is added to the containing model's list of read-only attributes through attr_readonly. Instead you should use update_counters or reset_counters.

Rails join with where using AND instead of OR

I have a scope (where title is an array)
scope :with_label, ->(title) { joins(:labels).where(labels: { title: title }) }
By default this does an AND query. How can I get this to do an OR query? I can't seem to find any examples of this.
I believe this is the SQL that is generated:
Label Load (0.1ms) SELECT "labels".* FROM "labels" WHERE "labels"."project_id" = $1 AND "labels"."title" = $2 ORDER BY "labels"."title" ASC LIMIT 1 [["project_id", 8], ["title", "label1,label2,label3"]]
Rails/ActiveRecord only supports OR queries in Rails 5. In earlier versions, you need to use a SQL string to achieve an OR query:
Widget.where("name = ? OR name = ?", "foo", "bar") # postgres
When there are many possibilities, the same thing can be accomplished more efficiently with an IN query, which is generated in ActiveRecord by passing an array with more than one element to the #where method:
arr = ["foo", "bar", "baz"]
Widget.where(name: arr)
As for the SQL output you've posted:
Label Load (0.1ms) SELECT "labels".* FROM "labels" WHERE "labels"."project_id" = $1 AND "labels"."title" = $2 ORDER BY "labels"."title" ASC LIMIT 1 [["project_id", 8], ["title", "label1,label2,label3"]]
The AND here is coming from the fact that a query is being made against the product_id as well as the title, not because of anything to do with title.

Rails where clause is not working for some of the values

I am working on Easy Redmine PMS and I have written where query in rails Like:
Tracker.where(name: "+ Permits Required")
but it returns less values whereas when I have tried detect query for getting same data Like:
Tracker.all.detect{|i| i.name == "+ Permits Required"
And it is returning all the values. Also I noticed that when fetching the data directly in database then I get the value as Request Available whereas rails console returns Permits Required. I have tried this issue with postgresql and mysql both the databases and the issue is same. Here are the queries:
2.1.1 :001 > #tra = Tracker.find_by_name("+ Camps & Finished Food Store")
Tracker Load (32.8ms) SELECT "trackers".* FROM "trackers" WHERE "trackers"."name" = $1 LIMIT 1 [["name", "+ Camps & Finished Food Store"]]
=> nil
2.1.1 :002 > #tra = Tracker.all.detect{|i| i.name == ("+ Camps & Finished Food Store")}
Tracker Load (0.9ms) SELECT "trackers".* FROM "trackers"
AnonymousUser Load (27.4ms) SELECT "users".* FROM "users" WHERE "users"."type" IN ('AnonymousUser') ORDER BY "users"."id" ASC LIMIT 1
EasyTranslation Load (28.6ms) SELECT "easy_translations".* FROM "easy_translations" WHERE "easy_translations"."entity_id" = $1 AND "easy_translations"."entity_type" = $2 AND "easy_translations"."entity_column" = $3 AND "easy_translations"."lang" = $4 ORDER BY "easy_translations"."id" ASC LIMIT 1 [["entity_id", 3], ["entity_type", "Tracker"], ["entity_column", "name"], ["lang", "en"]]
EasyTranslation Load (1.5ms) SELECT "easy_translations".* FROM "easy_translations" WHERE "easy_translations"."entity_id" = $1 AND "easy_translations"."entity_type" = $2 AND "easy_translations"."entity_column" = $3 AND "easy_translations"."lang" = $4 ORDER BY "easy_translations"."id" ASC LIMIT 1 [["entity_id", 4], ["entity_type", "Tracker"], ["entity_column", "name"], ["lang", "en"]]
EasyTranslation Load (0.7ms) SELECT "easy_translations".* FROM "easy_translations" WHERE "easy_translations"."entity_id" = $1 AND "easy_translations"."entity_type" = $2 AND "easy_translations"."entity_column" = $3 AND "easy_translations"."lang" = $4 ORDER BY "easy_translations"."id" ASC LIMIT 1 [["entity_id", 5], ["entity_type", "Tracker"], ["entity_column", "name"], ["lang", "en"]]
2.1.1 :003 > #tra = Tracker.where(name: "+ Camps & Finished Food Store")
Tracker Load (0.8ms) SELECT "trackers".* FROM "trackers" WHERE "trackers"."name" = $1 [["name", "+ Camps & Finished Food Store"]]
Tracker.where(name: "Permits Required")

How to check for database changes of in-memory records?

I want to check if an ActiveRecord instance was changed database-wise. Something like:
p1 = Product.first
p1.name #=> "some name"
p2 = Product.first
p2.name = "other name"
p2.save #=> true
p1.database_changed? #=> true
I'm currently comparing the record's attributes to the persisted attributes:
class Product < ActiveRecord::Base
def database_changed?
Product.find(id).attributes != attributes
end
end
This seems to work, but I'm wondering if there is a built-in way to find database changes?
After Зелёный's comment I've reviewed ActiveModel::Dirty and realized that it almost does what I want. There's already an in-memory state (the record's attributes) and a database state (handled by ActiveModel::Dirty). I just need a method to update the database state, leaving the in-memory state unchanged:
def refresh
#changed_attributes = {}
fresh_object = self.class.unscoped { self.class.find(id) }
fresh_object.attributes.each do |attr, orig_value|
#changed_attributes[attr] = orig_value if _field_changed?(attr, orig_value, #attributes[attr])
end
self
end
#changed_attributes is ActiveModel::Dirty's hash to store changed values. We obviously have to reset it.
fresh_object is the same record, freshly fetched from the database (this line comes from reload, thanks emaillenin).
Within the loop, each (fresh) attribute is compared to the corresponding (in-memory) attribute. If they differ, it is added to the #changed_attributes hash. This line comes from ActiveRecord's dup method. (it's actually from a private method called by dup, and _field_changed is private, too. It might be better to use ActiveRecord's public API, but I was lazy)
Finally, refresh returns self for convenience, just like reload does.
Here's an example usage:
p1 = Product.first
p1.name #=> "some name"
p1.changed? #=> false
p2 = Product.first
p2.name = "other name"
p2.save #=> true
p1.refresh
p1.name #=> "some name"
p1.changed? #=> true
p1.name_changed? #=> true
p1.name_was #=> "other name"
p1.name = "other name"
p1.name_changed? #=> false
p1.changed? #=> true
p1.changes #=> {"updated_at"=> [Tue, 29 Jul 2014 21:58:57 CEST +02:00, Tue, 29 Jul 2014 15:49:54 CEST +02:00]}
def database_changed?
self.class.where(self.class.arel_table[:updated_at].gt(updated_at)).exists? self
end
The Rails way to to do this is to use reload method on the ActiveRecord object.
def database_changed?
attributes != reload.attributes
end
Terminal 1
2.1.2 :001 > c = Car.find(1)
Car Load (0.4ms) SELECT "cars".* FROM "cars" WHERE "cars"."id" = ? LIMIT 1 [["id", 1]]
=> #<Car id: 1, name: "Audi", model: "R8", created_at: "2014-07-29 11:14:43", updated_at: "2014-07-29 11:14:43">
2.1.2 :002 > c.database_changed?
Car Load (0.1ms) SELECT "cars".* FROM "cars" WHERE "cars"."id" = ? LIMIT 1 [["id", 1]]
=> false
2.1.2 :003 > c.database_changed?
Car Load (0.2ms) SELECT "cars".* FROM "cars" WHERE "cars"."id" = ? LIMIT 1 [["id", 1]]
=> false
2.1.2 :004 > c.database_changed?
Car Load (0.2ms) SELECT "cars".* FROM "cars" WHERE "cars"."id" = ? LIMIT 1 [["id", 1]]
=> true
Terminal 2
2.1.2 :001 > c = Car.find(1)
Car Load (0.2ms) SELECT "cars".* FROM "cars" WHERE "cars"."id" = ? LIMIT 1 [["id", 1]]
=> #<Car id: 1, name: "Audi", model: "R8", created_at: "2014-07-29 11:14:43", updated_at: "2014-07-29 11:14:43">
2.1.2 :002 > c.model = 'A5'
=> "A5"
2.1.2 :003 > c.save!
(0.2ms) begin transaction
SQL (0.3ms) UPDATE "cars" SET "model" = ?, "updated_at" = ? WHERE "cars"."id" = 1 [["model", "A5"], ["updated_at", "2014-07-29 11:15:32.845875"]]
(1.2ms) commit transaction
=> true
2.1.2 :004 >

Resources