active record ignores new attribute in migration - ruby-on-rails

When I'm creating a new column through a migration helper and setting the value through ruby-active-record-model-code. even through the changes are valid and the ruby object data looks good the new attribute is not saved through ActiveRecord after all.
Thats what i'm doing in detail:
I'm adding the new column to my schema for the user model:
add_column :users, :billing_day_of_month, :integer
right after this, also in my migration file, I want to fill this new column following a simple ruby formula:
User.customers.each do |u|
day = nil
if u.last_charged_at
day = u.last_charged_at.day
else
day = u.plan_started_at.day
end
u.billing_day_of_month = day
puts "precheck #{u.id} #{u.valid?}"
if u.save
puts "saved #{u.id} -> #{u.billing_day_of_month}"
else
puts "not saved #{u.errors.inspect}"
end
end
All outputs look very promising:
precheck 399 true
saved 399 -> 22
precheck 414 true
saved 414 -> 9
precheck 203 true
saved 203 -> 8
precheck 439 true
saved 439 -> 21
Unfortunately, the values in the model are not being saved via ActiveRecord:
SQL (0.4ms) UPDATE "users" SET "updated_at" = $1 WHERE "users"."id" = $2 [["updated_at", "2016-01-15 13:01:04.966681"], ["id", 399]]
SQL (0.4ms) UPDATE "users" SET "updated_at" = $1 WHERE "users"."id" = $2 [["updated_at", "2016-01-15 13:01:04.969648"], ["id", 414]]
SQL (0.4ms) UPDATE "users" SET "updated_at" = $1 WHERE "users"."id" = $2 [["updated_at", "2016-01-15 13:01:04.972547"], ["id", 203]]
SQL (0.4ms) UPDATE "users" SET "updated_at" = $1 WHERE "users"."id" = $2 [["updated_at", "2016-01-15 13:01:04.975492"], ["id", 439]]
So I digged around and found that User.attribute_names is missing the new attribute name billing_day_of_month. When I use the rails console after this migration, the User.attribute_names does contain the new attribute name.
Btw at the time of migration, the schema.rb does contain the new column.
Anyone have a clue what I'm missing here? - I already used new cols within the same migration through a model and it always worked.

Try adding User.reset_column_information before the update loop. See http://api.rubyonrails.org/classes/ActiveRecord/ModelSchema/ClassMethods.html#method-i-reset_column_information for more details.

Related

Rails: Renaming column in migration fails

I have a simple migration which fails: The database is not affected, it still contains the old column name after migrating.
class RenameTypeToLicenseTypeInLicenses < ActiveRecord::Migration[5.1]
def change
rename_column :licenses, :type, :license_type
end
end
Renaming type to license_type is necessary because of this error:
The single-table inheritance mechanism failed to locate the subclass: 'creative commons'. This error is raised because the column 'type' is reserved for storing the class in case of inheritance. Please rename this column if you didn't intend it to be used for storing the inheritance class or overwrite License.inheritance_column to use another column for that information.
This is the output of rails db:migrate
igrating to RenameTypeToLicenseTypeInLicenses (20210422110849)
(0.2ms) BEGIN
== 20210422110849 RenameTypeToLicenseTypeInLicenses: migrating ================
== 20210422110849 RenameTypeToLicenseTypeInLicenses: migrated (0.0000s) =======
SQL (0.9ms) INSERT INTO "schema_migrations" ("version") VALUES ($1) RETURNING "version" [["version", "20210422110849"]]
(1.4ms) COMMIT
ActiveRecord::InternalMetadata Load (0.5ms) SELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 LIMIT $2 [["key", "environment"], ["LIMIT", 1]]
(0.3ms) BEGIN
(0.3ms) COMMIT
(0.4ms) SELECT pg_advisory_unlock(2195010657070977375)
(0.6ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
How can I rename the type column?
edit:
As there's only little data in the table, I tried another approach:
remove_column :licenses, :type, :string
add_column :licenses, :license_type, :string
It doesn't work either. Output is
[NAME COLLISION] `type` is a reserved key in LicenseResource.
(0.7ms) SELECT pg_try_advisory_lock(2195010657070977375);
(1.3ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
Migrating to ChangeTypeToLicenseTypeFromLicenses (20210422122638)
(0.5ms) BEGIN
== 20210422122638 ChangeTypeToLicenseTypeFromLicenses: migrating ==============
== 20210422122638 ChangeTypeToLicenseTypeFromLicenses: migrated (0.0000s) =====
SQL (1.0ms) INSERT INTO "schema_migrations" ("version") VALUES ($1) RETURNING "version" [["version", "20210422122638"]]
(4.6ms) COMMIT
ActiveRecord::InternalMetadata Load (0.4ms) SELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 LIMIT $2 [["key", "environment"], ["LIMIT", 1]]
(0.4ms) BEGIN
(0.4ms) COMMIT
(0.6ms) SELECT pg_advisory_unlock(2195010657070977375)
(0.9ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
Yeah, I wonder if ActiveRecord's migration code might be trying its best to keep type intact, on the assumption that it's currently being used for single table inheritance; it's oblivious to the fact you want to change it because it currently isn't 😊
There may be an idiomatic Rails way of telling the migration that what you're doing is fine, but to be honest I'd be tempted to drop down to the SQL level, thus bypassing Rails' erroneous protection:
class RenameTypeToLicenseTypeInLicenses < ActiveRecord::Migration[5.1]
def up
execute <<~SQL
ALTER TABLE licenses RENAME COLUMN type TO license_type;
SQL
end
def down
execute <<~SQL
ALTER TABLE licenses RENAME COLUMN license_type TO type;
SQL
end
end
As you're not using migration commands, you have to provide separate up/down code for both directions of the migration.
I believe this answer should help you: issue with column name 'type' in rails 3
type is one of those reserved words we shouldn't use. Check other reserved words here: https://riptutorial.com/ruby-on-rails/example/32446/reserved-word-list

Handling a massive query in Rails

What's the best way to handle a large result set with Rails and Postgres? I didn't have a problem until today, but now I'm trying to return a 124,000 record object of #network_hosts, which has effectively DoS'd my development server.
My activerecord orm isn't the prettiest, but I'm pretty sure cleaning it up isn't going to help in relation to performance.
#network_hosts = []
#host_count = 0
#company.locations.each do |l|
if l.grace_enabled == nil || l.grace_enabled == false
l.network_hosts.each do |h|
#host_count += 1
#network_hosts.push(h)
#network_hosts.sort! { |x,y| x.ip_address <=> y.ip_address }
#network_hosts = #network_hosts.first(5)
end
end
end
In the end, I need to be able to return #network_hosts to the controller for processing into the view.
Is this something that Sidekiq would be able to help with, or is it going to be just as long? If Sidekiq is the path to take, how do I handle not having the #network_hosts object upon page load since the job is running asyncronously?
I believe you want to (1) get rid of all that looping (you've got a lot of queries going on) and (2) do your sorting with your AR query instead of in the array.
Perhaps something like:
NetworkHost.
where(location: Location.where.not(grace_enabed: true).where(company: #company)).
order(ip_address: :asc).
tap do |network_hosts|
#network_hosts = network_hosts.limit(5)
#host_count = network_hosts.count
end
Something like that ought to do it in a single DB query.
I had to make some assumptions about how your associations are set up and that you're looking for locations where grace_enabled isn't true (nil or false).
I haven't tested this, so it may well be buggy. But, I think the direction is correct.
Something to remember, Rails won't execute any SQL queries until the result of the query is actually needed. (I'll be using User instead of NetworkHost so I can show you the console output as I go)
#users = User.where(first_name: 'Random');nil # No query run
=> nil
#users # query is now run because the results are needed (they are being output to the IRB window)
# User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."first_name" = $1 LIMIT $2 [["first_name", "Random"], ["LIMIT", 11]]
# => #<ActiveRecord::Relation [...]>
#users = User.where(first_name: 'Random') # query will be run because the results are needed for the output into the IRB window
# User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."first_name" = $1 LIMIT $2 [["first_name", "Random"], ["LIMIT", 11]]
# => #<ActiveRecord::Relation [...]>
Why is this important? It allows you to store the query you want to run in the instance variable and not execute it until you get to a view where you can use some of the nice methods of ActiveRecord::Batches. In particular, if you have some view (or export function, etc.) where you are iterating the #network_hosts, you can use find_each.
# Controller
#users = User.where(first_name: 'Random') # No query run
# view
#users.find_each(batch_size: 1) do |user|
puts "User's ID is #{user.id}"
end
# User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."first_name" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["first_name", "Random"], ["LIMIT", 1]]
# User's ID is 1
# User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."first_name" = $1 AND ("users"."id" > 1) ORDER BY "users"."id" ASC LIMIT $2 [["first_name", "Random"], ["LIMIT", 1]]
# User's ID is 2
# User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."first_name" = $1 AND ("users"."id" > 2) ORDER BY "users"."id" ASC LIMIT $2 [["first_name", "Random"], ["LIMIT", 1]]
# => nil
Your query is not executed until the view, where it will now load only 1,000 records (configurable) into memory at a time. Once it reaches the end of those 1,000 records, it will automatically run another query to fetch the next 1,000 records. So your memory is much more sane, at the cost of extra database queries (which are usually pretty quick)

Rails Devise user password is always invalid (tested in rails console)

I have one particular user in my rails app for which I can't log in anymore, the password is always invalid, even though I changed it manually. Here's what I do in rails console :
> me = User.find(10)
> me.password = '123456789'
> me.save
(0.3ms) BEGIN
User Exists (0.6ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'myemail#gmail.com' AND "users"."id" != 10) LIMIT 1
SQL (0.7ms) UPDATE "users" SET "encrypted_password" = $1, "updated_at" = $2 WHERE "users"."id" = $3 [["encrypted_password", "$2a$10$mrhWiOT3pu6YldtYRD/bC.wuqPthyfJhiqdGkYv14xCafVQNTodWG"], ["updated_at", "2016-08-08 10:43:34.715229"], ["id", 10]]
(31.3ms) COMMIT
=> true
> me.valid_password?('123456789')
=> nil
This is only with this particular user id 10. I do the exact same thing with any other user it works. What could be wrong ?
EDIT : I tried also with password confirmation but that's not the issue. As I said, the exact manipulation works fine with any user except this one of ID 10
EDIT 2 : I found the solution in this thread : Rails/Devise: valid_password? method returns nil
I think probably you need to set password_confirmation as well, Try below code.
> me = User.find(10)
> me.password = '123456789'
> me.password_confirmation = '123456789'
> me.save

Using pp (Pretty Print) in a method before save allows my model to save successfully, it fails otherwise

I have an object that looks like this:
It is used to allows users with resumes to 'apply to' (job)listings.
# == Schema Information
#
# Table name: jobapps
#
# id :integer not null, primary key
# denied :boolean
# listing_id :integer
# resume_id :integer
# created_at :datetime
# updated_at :datetime
#
class Jobapp < ActiveRecord::Base
belongs_to :listing
belongs_to :resume
validates_uniqueness_of :listing_id, scope: :resume_id
before_save :default_values
. . .
private
def default_values
if self.denied.nil?
self.denied = false
end
end
end
Whenever I create a new Jobapp object, the job always fails.
If I change the default_values method on the Jobapps model to:
def default_values
if denied.nil?
self.denied = false
pp self
end
end
The object is created and saved.
Console Output:
When I attempt to have a Resume object apply_to a Listing object, which should create a Jobapp object:
Started POST "/listings/1/apply?resume=8" for 127.0.0.1 at 2014-07-31 18:43:33 -0500
Processing by ListingsController#apply as HTML
Parameters: {"authenticity_token"=>"htbHjaZnkqRMfgSUAQA5zOXB3ax/MAaHRUx8jRWr/lw=", "resume"=>"8", "id"=>"1"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
Listing Load (0.2ms) SELECT "listings".* FROM "listings" WHERE "listings"."id" = $1 LIMIT 1 [["id", "1"]]
(0.2ms) SELECT COUNT(*) FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = $1 AND (((roles.name = 'admin') AND (roles.resource_type IS NULL) AND (roles.resource_id IS NULL))) [["user_id", 1]]
Resume Load (0.2ms) SELECT "resumes".* FROM "resumes" WHERE "resumes"."id" = $1 LIMIT 1 [["id", "8"]]
(0.2ms) BEGIN
Jobapp Exists (0.2ms) SELECT 1 AS one FROM "jobapps" WHERE ("jobapps"."listing_id" = 1 AND "jobapps"."resume_id" = 8) LIMIT 1
(0.1ms) ROLLBACK
Redirected to http://0.0.0.0:3000/listings/1
Completed 302 Found in 102ms (ActiveRecord: 1.5ms)
When I have the pp self line in the default_values method:
Started POST "/listings/1/apply?resume=8" for 127.0.0.1 at 2014-07-31 18:41:36 -0500
Processing by ListingsController#apply as HTML
Parameters: {"authenticity_token"=>"htbHjaZnkqRMfgSUAQA5zOXB3ax/MAaHRUx8jRWr/lw=", "resume"=>"8", "id"=>"1"}
User Load (1.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
Listing Load (0.2ms) SELECT "listings".* FROM "listings" WHERE "listings"."id" = $1 LIMIT 1 [["id", "1"]]
(2.3ms) SELECT COUNT(*) FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = $1 AND (((roles.name = 'admin') AND (roles.resource_type IS NULL) AND (roles.resource_id IS NULL))) [["user_id", 1]]
Resume Load (0.3ms) SELECT "resumes".* FROM "resumes" WHERE "resumes"."id" = $1 LIMIT 1 [["id", "8"]]
(0.1ms) BEGIN
Jobapp Exists (0.2ms) SELECT 1 AS one FROM "jobapps" WHERE ("jobapps"."listing_id" = 1 AND "jobapps"."resume_id" = 8) LIMIT 1
"here it is"
#<Jobapp id: nil, denied: false, listing_id: 1, resume_id: 8, created_at: nil, updated_at: nil>
SQL (0.3ms) INSERT INTO "jobapps" ("created_at", "denied", "listing_id", "resume_id", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["created_at", Thu, 31 Jul 2014 23:41:36 UTC +00:00], ["denied", false], ["listing_id", 1], ["resume_id", 8], ["updated_at", Thu, 31 Jul 2014 23:41:36 UTC +00:00]]
(0.5ms) COMMIT
Redirected to http://0.0.0.0:3000/listings/1
Completed 302 Found in 120ms (ActiveRecord: 4.9ms)
Some referenced methods:
Here is the apply method from the controller:
def apply
set_resume
if #resume.apply_to(#listing)
redirect_to #listing
else
redirect_to #listing, alert: 'Unable to apply for job'
end
end
Here is the Resume.apply_to method:
def apply_to(listing)
jobapp = Jobapp.new(resume: self, listing: listing)
jobapp.save
end
Notes:
It smells like I'm doing something weird with the conditional apply method (which is annoying) but I don't think that's the reason for the failure (it fails if I take out the odd conditional and simplify the Resume.apply_to method to simply create the object rather than using new then save.
Pretty Print ruby doc
From what I can tell, this is because self.denied = false returns false, and so your callback is returning false, which will cancel all future callbacks and associated action:
If a before_* callback returns false, all the later callbacks and the associated action are cancelled. If an after_* callback returns false, all the later callbacks are cancelled. Callbacks are generally run in the order they are defined, with the exception of callbacks defined as methods on the model, which are called last.
http://api.rubyonrails.org/v4.1.1/classes/ActiveRecord/Callbacks.html
Just make sure your default_values method returns true.
From the ActiveRecord Callbacks documentation:
If a before_* callback returns false, all the later callbacks and the associated action are cancelled.
And in your callback you have:
if self.denied.nil?
self.denied = false
end
So if you keep in mind that all blocks (including if blocks, methods, begin/end blocks, etc.) return the last value in the block... then you're affectively returning false from your callback. That is:
self.denied = false sets self.denied to false an then returns false
The if block surrounding this returns the last value in the block, or false
The method returns the last value in the method (block), or false
Therefore, your callback cancels the save operation.
Furthermore, when you introduced the pp self statement, then that will effectively output self and then return self, which is not false (obviously).
So to fix the non pp self version, I would recommend just returning true at the end since you're most likely not looking to abort the save after evaluating the contents of your callback method. Here it is, slightly simplifed using the idiomatic ||= operator.
def default_values
self.denied ||= false
true
end
The use of ||= above is the same as: self.denied || self.denied = false.

Race Condition with Authlogic and Rails 4.1.1

I am upgrading an app to rails 4.1.1 and authlogic 3.4.2 and encountered a problem with a race condition in an integration test.
I have a page that issues two ajax requests upon loading. Both requests cause authlogic to try to update the logged in user record's last_request_at column. I do not always get the same exception, but something is always raised when the app tries to update the same user record back to back.
This was not an issue on rails 3 because they used Rack::Lock in the test environment.
Here is a snippet of the logs:
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = 2 LIMIT 1 [50/1923]
User Load (0.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = 2 LIMIT 1
(1.5ms) BEGIN
(0.2ms) BEGIN
SQL (0.3ms) UPDATE "users" SET "last_request_at" = $1, "perishable_token" = $2, "updated_at" = $3 WHERE "users"."type" IN ('MedicalProfessional') AND "user
s"."id" = 2 [["last_request_at", "2014-05-18 00:19:46.564174"], ["perishable_token", "awlh6mBgHl2mdU2uboi"], ["updated_at", "2014-05-18 00:19:46.566315"]]
PG::Error: another command is already in progress
: UPDATE "users" SET "last_request_at" = $1, "perishable_token" = $2, "updated_at" = $3 WHERE "users"."type" IN ('MedicalProfessional') AND "users"."id" = 2
SQL (1.7ms) UPDATE "users" SET "last_request_at" = $1, "perishable_token" = $2, "updated_at" = $3 WHERE "users"."type" IN ('MedicalProfessional') AND "user
s"."id" = 2 [["last_request_at", "2014-05-18 00:19:46.562637"], ["perishable_token", "SoXbKuCMs0Zu3vtxKGqn"], ["updated_at", "2014-05-18 00:19:46.564956"]]
(0.2ms) ROLLBACK
Completed 500 Internal Server Error in 132ms
(0.5ms) COMMIT
ActiveRecord::StatementInvalid (PG::Error: another command is already in progress
: UPDATE "users" SET "last_request_at" = $1, "perishable_token" = $2, "updated_at" = $3 WHERE "users"."type" IN ('MedicalProfessional') AND "users"."id" = 2):
app/helpers/auth_helper.rb:3:in `current_user_session'
app/helpers/auth_helper.rb:7:in `current_user'
How should/can I resolve this issue?
Turns out someone else on my team had monkey patched ActiveRecord so that all threads use the same DB connection in order to speed up tests. He got the idea from this: http://blog.plataformatec.com.br/2011/12/three-tips-to-improve-the-performance-of-your-test-suite/
I was able to work around the issue by setting ActiveRecord::Base.shared_connection = nil at the start of the failing tests.

Resources