Model Not Recognized after Upgrading to Rails 3 - ruby-on-rails-3

I have just upgraded the application from Rails 2 to Rails 3 (ruby 1.9.2-head).
Most models are recognized after the upgrade, however one model called "Villa" is not recognized at all and returns an "undefined method" error if you try to call any methods on it (e.g Villa.find(1) ).
The simplified code for the model is, but I have changed this in every which way and it doesn't seem to solve the problem:
class Villa < ActiveRecord::Base
belongs_to :beach
has_many :villa_pictures, :order => "id ASC"
has_many :villa_rooms, :order => "id ASC"
has_many :villa_facilities
default_scope :conditions => ["active = ?", "true"]
end
From the console, typing in "Villa" will simply return => Villa, whereas the other models will return their table definition.
Thanks in advance.

The Villa constant might already be defined. You don't have any other classes or modules elsewhere? Or is your application called "villa"? That would define Villa in config/application.rb by default.

Related

undefined method `selector' for #<ActiveRecord::Relation:0xbb8710c> when using mongoid to save a record

undefined method `selector' for #<ActiveRecord::Relation:0xbb8710c>
I am getting this error while trying to execute the follwoing code:
c = Content.first
c.is_processed = true
c.save # or c.update_attributes(:is_processed => true)
My Content model looks like this:
class Content
include Mongoid::Document
field :username
field :name
filed :is_processed, :type => Boolean
belongs_to :language
has_many :translations
end
I am using Mongoid 2.4, bson_ext 1.5, Ruby 1.9.2, Rails 3.2.5
I found the mistake. My Content model had this line:
has_many :translations
I had migrated the content model from ActiveRecord to Mongoid. But, all the other tables are still in ActiveRecord. The has_many was referring to a ActiveRecord model. This caused the issue. I commented the line and now update is working fine.

Rails 3 association eager load vs opposite association

I have associated models like this:
class Batch
has_many :logs
class Log
belongs_to :batch
I'm using includes to load batches with logs:
b = Batch.includes(:logs)
Which runs 2 selects as expected (batches and logs).
Then I do
b.first.logs.first.batch
and this triggers another select on batches, even when they were actually loaded already.
I figured to "fix" it by doing includes(:logs => :batch) but I'm still thinking that something is wrong here because the same batches are loaded twice. What gives?
You can fix this with the :inverse_of setting, which lets ActiveRecord know that the two associations are the inverse of each other.
class Batch
has_many :logs, :inverse_of => :batch
end
class Log
belongs_to :batch, :inverse_of => :logs
end

Rails STI association with subclasses

I'm getting some strange behaviour when fetching collections from a has_many association with rails 3 when using STI. I have:
class Branch < ActiveRecord::Base
has_many :employees, class_name: 'User::Employee'
has_many :admins, class_name: 'User::BranchAdmin'
end
class User < ActiveRecord::Base
end
class User::Employee < User
belongs_to :branch
end
class User::BranchAdmin < User::Employee
end
The desired behaviour is that branch.employees returns all employees including branch admins. The branch admins only seem to be 'loaded' under this collection when they have been accessed by branch.admins, this is output from the console:
Branch.first.employees.count
=> 2
Branch.first.admins.count
=> 1
Branch.first.employees.count
=> 3
This can be seen in the generated SQL, the first time:
SELECT COUNT(*) FROM "users" WHERE "users"."type" IN ('User::Employee') AND "users"."branch_id" = 1
and the second time:
SELECT COUNT(*) FROM "users" WHERE "users"."type" IN ('User::Employee', 'User::BranchAdmin') AND "users"."branch_id" = 1
I could solve this problem by just specifying:
class Branch < ActiveRecord::Base
has_many :employees, class_name: 'User'
has_many :admins, class_name: 'User::BranchAdmin'
end
since they all be found from their branch_id but this creates problems in the controller if I want to do branch.employees.build then the class will default to User and I have to hack at the type column somewhere. I have got around this for now with:
has_many :employees, class_name: 'User::Employee',
finder_sql: Proc.new{
%Q(SELECT users.* FROM users WHERE users.type IN ('User::Employee','User::BranchAdmin') AND users.branch_id = #{id})
},
counter_sql: Proc.new{
%Q(SELECT COUNT(*) FROM "users" WHERE "users"."type" IN ('User::Employee', 'User::BranchAdmin') AND "users"."branch_id" = #{id})
}
but I would really like to avoid this if possible. Anyone, any ideas?
EDIT:
The finder_sql and counter_sql haven't really solved it for me because it seems that parent associations don't use this and so organisation.employees that has_many :employees, through: :branches will again only include the User::Employee class in the selection.
Basically, the problem only exists in the development environment where classes are loaded as needed. (In production, classes are loaded and kept available.)
The problem comes in due to the interpreter not having seen yet that Admins are a type of Employee when you first run the Employee.find, etc. call.
(Notice that it later uses IN ('User::Employee', 'User::BranchAdmin'))
This happens with every use of model classes that are more than one level deep, but only in dev-mode.
Subclasses always autoload their parent hierarchy. Base classes don't autoload their child hierachies.
Hack-fix:
You can force the correct behaviour in dev-mode by explicitly requiring all your child classes from the base class rb file.
Can you use :conditions?
class Branch < ActiveRecord::Base
has_many :employees, class_name: 'User::Employee', :conditions => {:type => "User::Employee"}
has_many :admins, class_name: 'User::BranchAdmin', :conditions => {:type => "User::BranchAdmin"}
end
This would be my preferred method. One other way to do it might be to add a default scope to the polymorphic models.
class User::BranchAdmin < User::Employee
default_scope where("type = ?", name)
end
A similar problem continues to exist in Rails 6.
This link outlines the issue and workaround. It contains the following explanation and code snippet:
Active Record needs to have STI hierarchies fully loaded in order to generate correct SQL. Preloading in Zeitwerk was designed for this use case:
By preloading the leaves of the tree, autoloading will take care of the entire hierarchy upwards following superclasses.
These files are going to be preloaded on boot, and on each reload.
# config/initializers/preload_vehicle_sti.rb
autoloader = Rails.autoloaders.main
sti_leaves = %w(car motorbike truck)
sti_leaves.each do |leaf|
autoloader.preload("#{Rails.root}/app/models/#{leaf}.rb")
end
You may require a spring stop for the configuration changes to take.
Indeed, that was the plan in the early days of the gem, but it was abandoned soon (in 2019, before Rails 6 was out). Preloading has been deprecated for a long time, and has been deleted in the forthcoming Zeitwerk 2.5.
In a Rails application you can do it this way:
# config/initializers/preload_vehicle_sti.rb
Rails.application.config.to_prepare do
Car
Motorbike
Truck
end
That is, you "preload" just by using the constants in a to_prepare block.

How to "merge" more than one 'ActiveRecord::Associations' and one or more scope methods?

I am using Ruby on Rails 3.2.2 and MySQL. I would like to "merge" the result of more than one ActiveRecord::Associations and one or more scope methods. That is, I have:
class User < ActiveRecord::Base
has_many :a_user_relationships, :foreign_key => 'a_key'
has_many :b_user_relationships, :foreign_key => 'b_key'
has_many :a_articles, :through => :a_user_article_associations # Returns objects kind of 'Article'
has_many :b_articles, :through => :b_user_article_associations # Returns objects kind of 'Article'
end
class Article < ActiveRecord::Base
# Note: This is a scope method.
def self.public
where(:status => 'public')
end
end
Given the above code I would like to run some method (as-like the following) so to retieve all public "a" and "b" user articles by executing as few as possible database queries:
#user.all_articles
class User < ActiveRecord::Base
# Note: Code in the following method is incorrect, but maybe it helps
# understanding what I mean.
def all_articles
self.(a_articles & b_articles).public
(self.a_articles.public & self.b_articles.public)
end
end
Is it possible?
Since you have two separate associations, you'll need at least two queries.
If you always want articles from both here, is there anything stopping you from having a simple "articles" association in addition to or instead of these two separate ones? That way, you could retrieve your articles with self.articles.public.

Update the timestamp on a polymorphic association in Rails

I have a Photo model that can belong to two other models and this is captured through a polymorphic association:
has_many :photos, :as => photo_container
I would like to update the timestamp of the photo_container models when a new photo is created. I am doing this with an after_create callback in the Photo model:
self.photo_container.update_attribute(:updated_at, Time.now)
Since both my photo_container models have an update_attribute field it should be working fine, however, I am getting the following exception for the corresponding line in the callback:
NameError (uninitialized constant Photo::PhotoContainer)
Of course it works fine if I check the type of photo_container, load the model and change the timestamp but it is not clean/generic. Any idea?
Use the touch option in the belongs_to association. It should work fine. Something like:
class Picture < ActiveRecord::Base
belongs_to :imageable, :polymorphic => true, :touch=>true
end