Migrate models between different environments in Rails - ruby-on-rails

I am writing a script that will be in charge of transferring data from production db to a new production db.
There is one Rails app connecting to each database. In the new app we have made some migrations that change the schema (remove columns, etc)
I tried to do this:
rails console old
2.0.0p247 :004 > tag = Tag.last.dup
=> #<Tag id: nil, description: " views", account_id: 46, screenshotBase64: "", user_id: 1, created_at: nil, updated_at: nil>
2.0.0p247 :005 > ActiveRecord::Base.establish_connection(:development)
2.0.0p247 :006 > tag.save
ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'screenshotBase64' in 'field list
As we removed the screenshotBase64 in the new db, it does not work.
Is there a way to do that with Rails ? Delete attributes from Rails model before save ?
Is there a better way to transfer all data from a user linked through its associations between two databases (changing ids in the new db) ?

Please have a try with this code
tag_attributes = Tag.last.attributes.dup
%w(id screenshotBase64).map{|method| tag_attributes.delete method}
ActiveRecord::Base.establish_connection(:development)
tag = Tag.create(tag_attributes)

Related

Could not find_by an object from database in Rails job

I have created a Ruby on Rails job (extending from ActiveJob::Base) that parses a CSV file and inserts its rows as records (Students) in a database. That is working as expected.
However, when I try to fetch another object from the DB (the Career for each Student, which is part of each CSV row as a pair (career_code, career_name)), I'm getting this error:
undefined method 'find_by' for Career:Class
(I have also tried using Career.where instead).
I find this quite strange, since I'm already saving my Student, which is also an ActiveRecord::Base child class.
Here's the relevant part of my job:
ActiveRecord::Base.transaction do
student_hash.keys.each do |k|
some_student = student_hash[k]
student = Student.new
student.csv_id = some_student.id
student.first_name = some_student.first_name
student.last_name = some_student.last_name
student.file_number = some_student.file_number
# student.career = Career.where(code: some_student.career_code)
student.career = Career.find_by code: some_student.career_code
puts student.save! # Why this works, and the above line doesn't?
end
end
And also, the desired output, as I can see it in the Rails console:
Loading development environment (Rails 4.2.4.rc1)
2.1.3 :001 > Career.where(code: 11)
Career Load (0.5ms) SELECT "careers".* FROM "careers" WHERE "careers"."code" = ? [["code", 11]]
=> #<ActiveRecord::Relation [#<Career id: 4, name: "Informática", created_at: "2015-09-30 22:05:07", updated_at: "2015-09-30 22:05:07", code: 11>]>
2.1.3 :002 > Career.where code: 11
Career Load (0.2ms) SELECT "careers".* FROM "careers" WHERE "careers"."code" = ? [["code", 11]]
=> #<ActiveRecord::Relation [#<Career id: 4, name: "Informática", created_at: "2015-09-30 22:05:07", updated_at: "2015-09-30 22:05:07", code: 11>]>
2.1.3 :003 > Career.find_by code: 11
Career Load (0.4ms) SELECT "careers".* FROM "careers" WHERE "careers"."code" = ? LIMIT 1 [["code", 11]]
=> #<Career id: 4, name: "Informática", created_at: "2015-09-30 22:05:07", updated_at: "2015-09-30 22:05:07", code: 11>
2.1.3 :004 >
Probably this is a really stupid question, but I'm quite a beginner using Rails. Do I need to import some sort of "Context" (this seems unlikely, since Student seems to be properly resolved)? I'm using Rails 4.2.4.rc1 by the way.
Thanks in advance
Any chance at that point in the code Career isn't an AR, but some other class? That would at least explain why the methods don't exist.

The mongoid function "update_attribute" causes all attributes to be re-ordered alphabetically

Following up on this 1 year old post:
Mongodb mongoid model attributes sorted by alphabetical order not insertion order
I have a rails 3.2.8 application that uses mongoid v. 3.0.0. My application occasionally uses the "update_attribute" function on a database object (e.g. a product), but when it does all attributes gets reordered alphabetically.
When I duplicate this in the console, it looks like this:
1.9.3-p385 :001 > product = Product.find("5156bc9b83c3368121000008")
=> [#<Product _id: 5156bc9b83c3368121000008, _type: nil, product_number: "123", product_name: "Some product name", long_description: "<P>Some long product description.</P>", vendor_number: "abc", language_i_d: "1234", currency_i_d: "USD", category_number: "1", image_link: "http://some-external-website.com/image-path.jpg", original_id: "123456">]
1.9.3-p385 :002 > product.update_attribute(:image_link, "http://my-own-website.com/image-path.jpg")
=> true
1.9.3-p385 :003 > exit
I now fire up the console again (for some reason I need to exit and re-open the console before the new order is displayed):
1.9.3-p385 :001 > product = Product.find("5156bc9b83c3368121000008")
=> [#<Product _id: 5156bc9b83c3368121000008, _type: nil, category_number: "1", currency_i_d: "USD", image_link: "http://my-own-website.com/image-path.jpg", language_i_d: "1234", long_description: "<P>Some long product description.</P>", original_id: "123456", product_name: "Some product name", product_number: "123", vendor_number: "abc">]
Does anyone know how to avoid this reordering?
This isn't mongoid's fault. If an update causes a document to grow and mongo had to move the document as a result then mongodb itself may reorder the document's fields (see docs and a jira issue where this is described as normal)

Active Record has_many generates sql with foreign key IS NULL

I don't expect a model with NULL as foreign key to belong to anything!
I have the following rails app, modelling ants and ant hills (inspired by Jozef).
$ rails -v
Rails 3.2.8
$ rails new ant_hill
$ cd ant_hill
Create the ant hill and ant models. An ant can belong to an ant hill and an ant hill can have many ants.
$ rails generate model AntHill name:string
$ rails generate model Ant name:string ant_hill_id:integer
$ vim app/models/ant.rb
$ cat app/models/ant.rb
class Ant < ActiveRecord::Base
belongs_to :ant_hill
end
$ vim app/models/ant_hill.rb
$ cat app/models/ant_hill.rb
class AntHill < ActiveRecord::Base
has_many :ants
end
$ rake db:migrate
== CreateAntHills: migrating =================================================
-- create_table(:ant_hills)
-> 0.0013s
== CreateAntHills: migrated (0.0016s) ========================================
== CreateAnts: migrating =====================================================
-- create_table(:ants)
-> 0.0035s
== CreateAnts: migrated (0.0037s) ============================================
Run the following code in a console.
$ rails c
Loading development environment (Rails 3.2.8)
Create a couple of ants, persisted, that don't belong to any ant hill.
1.9.2-p290 :001 > Ant.create! name: "August"
=> #<Ant id: 1, name: "August", ant_hill_id: nil, created_at: "2012-09-27 12:01:06", updated_at: "2012-09-27 12:01:06">
1.9.2-p290 :002 > Ant.create! name: "Bertil"
=> #<Ant id: 2, name: "Bertil", ant_hill_id: nil, created_at: "2012-09-27 12:01:13", updated_at: "2012-09-27 12:01:13">
Now instantiate an ant hill, but don't save it just yet.
1.9.2-p290 :003 > ant_hill = AntHill.new name: "Storkullen"
=> #<AntHill id: nil, name: "Storkullen", created_at: nil, updated_at: nil>
I expect this ant hill to not have any ants and it doesn't.
1.9.2-p290 :004 > ant_hill.ants
=> []
I still expect the ant hill to not have any ants but now it has two.
1.9.2-p290 :005 > ant_hill.ants.count
(0.1ms) SELECT COUNT(*) FROM "ants" WHERE "ants"."ant_hill_id" IS NULL
=> 2
Same here, it should never generate a query containing "IS NULL" when dealing with foreign keys. I mean "belongs_to NULL" can't belong to anything, right?
1.9.2-p290 :006 > ant_hill.ants.all
Ant Load (0.4ms) SELECT "ants".* FROM "ants" WHERE "ants"."ant_hill_id" IS NULL
=> [#<Ant id: 1, name: "August", ant_hill_id: nil, created_at: "2012-09-27 12:01:06", updated_at: "2012-09-27 12:01:06">, #<Ant id: 2, name: "Bertil", ant_hill_id: nil, created_at: "2012-09-27 12:01:13", updated_at: "2012-09-27 12:01:13">]
After it is persisted it behaves as expected.
1.9.2-p290 :007 > ant_hill.save!
=> true
1.9.2-p290 :008 > ant_hill.ants.count
(0.4ms) SELECT COUNT(*) FROM "ants" WHERE "ants"."ant_hill_id" = 1
=> 0
1.9.2-p290 :009 > ant_hill.ants.all
Ant Load (0.4ms) SELECT "ants".* FROM "ants" WHERE "ants"."ant_hill_id" = 1
=> []
Any insight? Is this the expected behavior?
While it seems counterintuitive, I think this behavior makes sense given your examples. Take ant_hill.ants.count for example. Count is an ActiveRecord query method that hits the database, and you're essentially asking ActiveRecord to give you all the ants that do not belong to an ant hill. Rails is simply letting you do something you should not be able to do, and not complaining about it. Should this be raising an exception instead? Possibly.
If you really want to know how many ants belong to this ant_hill object, you should be using size. It queries the object when not persisted or when the association is already loaded, and queries the database otherwise.
ant_hill.ants.size
One way you can get around this oddity is to make ant_hill_id a required field by validating its presence.
TL;DR Avoid using ActiveRecord query interface if parent object is not persisted to database.

Nested objects and collection size

I run into a weird issue since I upgraded to Rails 3.2.
My application have some objects managed with nested_forms, but when i try to reach a collection's size after having built a new nested object, the nested object is taken in count.
For ex.:
1.9.3p0 :004 > e = Expense.last
Expense Load (22.6ms) (...)
=> #<Expense id: 1, (...)
1.9.3p0 :005 > e.comments.size
(0.3ms) SELECT COUNT(*) (...)
=> 0
1.9.3p0 :006 > e.comments.build
=> #<Comment id: nil, content: nil, commentable_id: 1, commentable_type: "Expense", created_at: nil, updated_at: nil, creator_id: nil>
1.9.3p0 :007 > e.comments.size
=> 1
In this case, I expect the e.expense.size to return 0 since the Commentobject has not been saved yet.
Building the comment creates ... 1 comment. It doesn't exist in the database (yet) so e.commment.count would return 0, but it exists in the application, so size returns 1. So that sort of makes sense.
But I do see there's a little potentially unexpected result here. If a comment doesn't exist in memory, it looks like Rails checks with the DB (the SELECT COUNT(*)...), whereas if it finds one in memory it does not.
I wonder what would happen if you had several existing comments already, then checked e.comments.size in that case?
Which version of Rails were you coming from that worked differently?

Is there a plugin/gem that lets you inherit errors from a child model in rails?

Let's assume you are using Rails, and have a form with nested models; for example, a form with Project and Tasks for the project.
Then assume that each task must have a name, however a user does not enter a name. When the user submits the form, and you use
project.update_attributes(params[:project])
=> raises error
But (as seen above) this raises an error. Are there any tools out there that will allow the errors from the task to percolate up to the project level, and NOT raise an error? Such a tool would greatly reduce duplication, and it would have to prevent both the project AND OTHER sub-tasks from being saved in the same call to 'update_attributes'.
On a side note, the problem I am trying to solve is the problem of not having to rewrite that percolation code for about 30 models,
Validation errors from associations should be available in the parent's #errors method. Example, if "Foo" has one "Bar":
ruby-1.9.2-p136 :001 > s = Foo.new
=> #<Foo id: nil, created_at: nil, updated_at: nil, enabled: true, alpha: nil>
ruby-1.9.2-p136 :002 > s.build_bar
=> #<Bar id: nil, created_at: nil, updated_at: nil, foo_id: nil, beta: nil>
ruby-1.9.2-p136 :003 > s.save
=> false
ruby-1.9.2-p136 :004 > s.errors.full_messages
=> ["Alpha is invalid", "Bar beta can't be blank"]
ruby-1.9.2-p136 :005 >
Furthermore, I don't think your models should be raising exceptions if there is a validation failure (if this is what you mean by "raise error"). You should just see #update_attributes return false, and then you can fetch the error list

Resources