Rails console save not saving and is selecting from related table instead - ruby-on-rails

When testing some things in the rails console I noticed this strange thing happening when I call a record and attempt to save it.
2.1.5 :026 > p = WorkOrder.first
WorkOrder Load (0.4ms) SELECT `work_orders`.* FROM `work_orders` ORDER BY `work_orders`.`id` ASC LIMIT 1
=> #<WorkOrder id: 3, client_id: 4, created_at: "2015-06-17 17:12:07", updated_at: "2015-06-17 17:12:07", dueDate: "2015-07-17", number: "0221506-003", project_type_id: 2, monthlySequenceNumber: "003", projectDescription: "Project", status_id: 1, labels_id: nil>
2.1.5 :027 > p.save
(0.2ms) BEGIN
ProjectType Load (0.5ms) SELECT `project_types`.* FROM `project_types` WHERE `project_types`.`id` = 2 LIMIT 1
(0.1ms) COMMIT
=> true
Why does it appear to be performing a select on the associated object? Also the records are not being committed back to the database. What am I missing that causes it to behave in such a seemingly strange way?
EDIT:
What prompted me to start try to save records that I pulled from the database was that I had an identical issue doing something like
p.delete
and then
p.save
which would return true, but would only perform that strange select on the Project Type

Why does it appear to be performing a select on the associated object?
This is possibly caused by validation code or callbacks in the WorkOrder model.
Also the records are not being committed back to the database. What am I missing that causes it to behave in such a seemingly strange way?
You have not modified the record, so the only field I would expect to be updated would be updated_at. It is possible to disable the timestamp feature for ActiveRecord. Have you done that? (reference Is there a way to avoid automatically updating Rails timestamp fields?)
UPDATE
The same thing happens when I tested p.delete followed by p.save, the result is true. This could be a bug, but I have not researched it enough to determine that yet.
After a quick look in the ActiveRecord source I think that what happens is that since you have first deleted your record (p), 0 rows in the database match that record's id (p.id). That means that when you run p.save 0 rows get updated (update instead of insert because the record is considered persisted). That number of rows gets compared with false here so that 0 != false returns true.

Due to the fact that you haven't changed any attribute, just try p.touch instead of p.save. This should save the record anyway.

Related

Rails Console - Why is "collection.delete(element)" deleting the element itself from database

I want to remove an element from a has_many relationship collection without destroying the element.
This works for me with this method:
current_user.flats.delete(Flat.find(7))
When I try to do a similar thing on the rails console, it destroys the whole object in the database:
irb(main):018:0> current_user.houses.delete(House.find(10))
SQL (13.4ms) DELETE FROM "cities_houses" WHERE "cities_houses"."city_id" = ? [["city_id", 10]]
SQL (0.8ms) DELETE FROM "houses" WHERE "houses"."id" = ? [["id", 10]]
As you can see, it removes the whole house object from it's own table.
What makes even less sense: It tries to remove an entry on the join table "cities_houses" using the given house_id (10) as parameter for the city_id to remove the element?
I don't get in general why it tries to update this join table. My command had nothing to do with it...
I'm using Rails version: 5.1.1 and Ruby version: 2.4.1 (x86_64-linux)
I found a solution to solve the problem!
This doesn't work, when dependent: :destroy is enabled.
current_user.houses.delete(House.find(10))
The solution is pretty obvious: for a has_many/belongs_to-association, you can just update the value user_id of the flat to nil. I knew this way, but what I've tried first didn't worked:
House.find(10).user_id = nil
House.find(10).save
The updated value will be changed and the change is immediately forgotten, if it is not stored in a variable.
This works:
house = House.find(10)
house.user_id = nil
house.save
Another solution, without loading the house model first:
House.where(id: 10).update_all(user_id: nil)

Rails Postgres hstore. DB not being updated with changed values

I'm trying to understand how to use hstore and it seems the database isn't updated if I try to modify a value in my hash.
In my rails console I do
u = User.new
u.hash_column = {'key' => 'first'}
u.save
I get a message in my console
(0.4ms) BEGIN
SQL (2.0ms) UPDATE "users" SET "hash_column" = $1, "updated_at" = $2 WHERE ...
(18.0ms) COMMIT
and when I check in the DB the column has the correct data
now when I try
u.hash_column['key'] = 'second'
the model seems correct in the console
puts u.hash_column
gives
{"key"=>"second"}
however when I try to save this
u.save
in my console I just get
(0.3ms) BEGIN
(0.2ms) COMMIT
with no update statement and when I check the DB the data hasn't changed.
if I change another attribute on the user model and save it hash_column still doesn't get updated. The only way I can change the hash_column in the database seems to be to assign to the complete hash like
u.hash_column = {'key' => 'second'}
is this how it is meant to work or am I doing something wrong?
This is due to the way that rails currently tracks changes. It will only track changes when you use the attributes setter (u.hash_column). If you update a value in the hash, you have to notify Rails that the value changed by calling u.hash_column_will_change!. This marks the value as dirty and the next time save is called, it will be persisted.
This also effects arrays and strings and dates. If you call u.string_value.gsub! or u.array_column << value those changes will not be persisted without calling the <column>_will_change! method for those columns

ActiveRecord requerying depending on order of operations

New to Ruby and Rails so forgive me if my terminology is off a bit.
I am working on optimizing some inherited code and watching the logs I am seeing queries repeat themselves due to lines like this in a .rabl file:
node(:program) { |user| (!user.programs.first.nil?) ? user.programs.first.name : '' }
user and program are both active record objects
Moving to the rails console, I can replicate the problem, but I can also get the expected behavior, which is only one query:
>u = User.find(1234)
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE [...]
> (!u.programs.first.nil?) ? u.programs.first.name : ''
Program Load (0.2ms) SELECT `programs`.* FROM `programs` [...]
Program Load (0.3ms) SELECT `programs`.* FROM `programs` [...]
=> "Output"
Note that repeating the ternary statement in the console will always give me 2 queries.
I can get the expected behavior like so:
> newu = User.find(12345)
User Load (3.8ms) SELECT `users`.* FROM `users` WHERE [...]
> newu.programs
Program Load (0.6ms) SELECT `programs`.* FROM `programs` [...]
> (!newu.programs.first.nil?) ? newu.programs.first.name : ''
=> "Output"
Repeating the ternary statement now won't requery at all.
So the question is: why does calling newu.programs change the behavior? Shouldn't calling u.programs.first.nil? also act to load all the program records in the same way?
With an association, first is not sugar for [0].
If the association is loaded, then it just returns the first element of the array. If it is not loaded, it makes a database query to load just that one element. It can't stick that in the association cache (at least not without being smarter), so the next query to first does the query again (this will use the query cache if turned on)
What Rails is assuming is that if the association is big, and you are only using one element of it then it would be silly to load the whole thing. This can be a little annoying when this isn't the case and you are just using the one item, but you're using it repeatedly.
To avoid this you can either assign the item to a local variable so that you do genuinely only call first once, or do
newu.programs[0]
which will load the whole association (once only) and return the first element.
Rails does the same thing with include?. Instead of loading the whole collection, it will run a query that tests whether a specific item is in the collection (unless the collection is loaded)

Marshal not serializing or deserializing nested serialized attributes properly

This is kind of a tough problem to explain, because I don't really know what is going on. I've made a repository with instructions of how to reproduce:
https://github.com/bricker/cache_nested_serialized_attributes
Basically:
User has_many Posts
Post has serialized attribute :metadata
And to reproduce:
users = User.first(2)
users.first.posts.first
users.last.posts.first
dump = Marshal.dump(users)
Marshal.load(dump)
=> [#<User id: 1, name: "bricker", created_at: "2013-04-24 06:26:03", updated_at: "2013-04-24 06:26:03">,
:#new_record]
You can see the unexpected output in that final line. It only seems to occur under those specific conditions. Just calling users.first.posts works fine. Not calling .posts at all works fine. It's only when I load a specific post from a user before dumping that this happens.
The main difference I notice between Rails 4 (which works properly) and Rails 3.2.13 (which doesn't work) is that when calling Marshal.dump(users) in Rails 3, the Posts are reloaded:
dump = Marshal.dump(users)
Post Load (0.2ms) SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = 1
Post Load (0.2ms) SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = 2
... which doesn't happen in Rails 4.
I wish I could explain better but it's such an obscure problem. Please ask questions if you need clarification, and look at or clone the repo.
This is tested and confirmed on a vanilla Rails 3.2.13 (see repository). This behavior does not happen in Rails 4.
Thanks!
This issue is fixed on Rails master, and a simple patch is pending review on 3-2-stable. I've applied the patch manually in an initializer and it fixes the problem.
See here for the patch: https://github.com/rails/rails/issues/10322#issuecomment-16913855

Activerecord can't find model by attribute, even though the query is correct

I have a Model called Invitation which has an attribute called code. The application's goal is to find the correct invitation with a code that is entered somewhere.
My problem is, however, that even though the input is correct, ActiveRecord can't seem to find any results while querying in the database. I've created this small test to illustrate the problem:
ruby-1.9.2-p290 :003 > code = Invitation.first.code
Invitation Load (0.4ms) SELECT "invitations".* FROM "invitations" LIMIT 1
=> "86f50776bf"
So at this point I've loaded this invitation's code in a variable
ruby-1.9.2-p290 :004 > i = Invitation.where(:code => code)
Invitation Load (0.2ms) SELECT "invitations".* FROM "invitations" WHERE "invitations"."code" = '86f50776bf'
=> []
And the response of the query is an empty array, even though the code comes straight from the database. When using code == Invitation.first.code to see if the values are equal, it returns true. I already checked both the Ruby and database's data types, they're all Strings.
What can cause this? and how can I fix this?
Based on your comment, it could be the case that the column is not VARCHAR but CHAR, or it contains trailing spaces that are being trimmed off by the ActiveRecord ORM layer or the database driver. 'foo' and 'foo ' are not equivalent, but they are LIKE enough to match.
You may want to switch that column to variable length, or to adjust your query to test: RTRIM(code)=?
I found the solution when I stumbled upon this answer:
In Ruby 1.9, all strings are now encoded. By default, all strings should be UTF-8, however, SecureRandom.hex(30) returns an encoding of ASCII-8BIT.
Adding .force_encoding('UTF-8') to the key when it's being executed solves the problem :)
#Marco,
How you declare the code variable? As String?
Example:
code = "86f50776bf"
or
code = '86f50776bf'
?

Resources