Reputation: 81
So here we go. I've got an Activerecord::Base model, let it be called a human.
class human < ActiveRecord::Base
has_one :Animal
end
Animal is an abstract class -
class animal < ActiveRecord::Base
self.abstract_class = true;
end
And I have a subclass of animal, let it be dog
class dog < Animal
in case I don't use abstract class, I can't add instance variables to 'Dog' (because it stores in 'Animal' table). In case I use abstract class, I can't add an 'Animal' to 'Human' - because rails doesn't know, how to store, for example, 'Dog'(ActiveRecord error: couldn't find table ''). This situation drives me crazy, and I just can't get over it. Am I missing something or just doin' it completely wrong?
Upvotes: 2
Views: 2445
Reputation: 601
By convention in Ruby, Animal would refer to a class (actually, it's a bit more involved - this link has some more detail). In your original post, "class dog" should be "class Dog" b/c the class name is a constant, and if you had a has_one association between human and animal, you could say human.animal = (some instance of animal), but human.Animal is likely to have strange effects if it doesn't just immediately crash. The STI approach that others are recommending will do exactly what you want, though you would set the 'type' value, not 'Animal' (please don't actually do this directly).
You should read up on the meaning of capitalization in Ruby and RoR, STI, active record associations, and polymorphic associations. Something like this should work (not tested, and it's bad normalization - you can use has_one associations and a pattern called delegation to set up a situation where generic animal traits are in one table, and 'human specific' traits are in another to avoid a bunch of NULL columns in your database):
# remember to set up your migrations to add a 'type' column to your Animal table
# if animals can own other animals who own other animals, you may want to look at
# acts_as_tree, which does trees in relational databases efficiently
class Animal < ActiveRecord::Base
self.abstract_class = true
end
class Dog < Animal
# this is bad normalization - but you can keep this simple by adding
# a human_id field in your animal table (don't forget to index)
# look into the 'belongs_to' / 'references' type available for DB migrations
belongs_to :human
end
class Human < Animal
has_one :dog, :autosave => true # or you could use 'has_many :dogs'
end
human = Human.new # => adds record to Animal table, with type = 'human'
dog = Dog.new
human.dog = dog
human.save
Upvotes: 2
Reputation: 3519
ActiveRecord by default gets the table name from the name of a model. You can override that, however. If you want Dogs in one table, Cats in other, etc. then you can do (in Rails 3.2):
class Dog < Animal
self.table_name = 'dogs'
end
class Cat < Animal
self.table_name = 'cats'
end
(You'll have to add migrations to create those tables.)
However, if you want all animals to exist in one table, you should look at Single-Table-Inheritance. See the ActiveRecord docs for more on that.
Upvotes: 0
Reputation: 87516
ActiveRecord has built-in support for polymorphic associations, so you could do that:
http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
Upvotes: 1