Reputation: 205
so I'm setting up some models and they are based off of 2 abstract base classes (or rather they used to be classes). After running into a lot of trouble with Datamapper's handling of STI for my use case, which appears to be an open bug on their lighthouse page, I decided instead to just do modules to define all the properties to keep my models DRY. Unfortunately, I'm having a scoping issue, and what complicates matters worse is that I have to use 2 levels of inheritance. Here's my code:
module Part
def self.included(receiver)
receiver.class_eval do
include DataMapper::Resource
property :id, Serial
#other junk
end
end
end
module HardDrive
def self.included(receiver)
receiver.class_eval do
include Part
property :kind, Enum[:magnetic, :flash]
#buncha crap here
end
end
end
class Fujitsu
include HardDrive
property :rev, String
end
The error I get is:
uninitialized constant HardDrive::Enum (NameError)
from /usr/lib/ruby/gems/1.8/gems/activesupport-2.3.4/lib/active_support/dependencies.rb:80:in `const_missing'
from ./app/models/hard_drive.rb:6:in `included'
from ./app/models/hard_drive.rb:4:in `class_eval'
from ./app/models/hard_drive.rb:4:in `included'
from ./app/models/hard_drives/fujitsu.rb:2:in `include'
from ./app/models/hard_drives/fujitsu.rb:2
I'm at a loss here. Anyone know of how I could solve this or better yet, a smarter way I could do this?
Upvotes: 2
Views: 876
Reputation: 9398
It seems to me that Enum is defined under the DataMapper modules and the HardDrive scope does not resolve it. (Want to know why ?)
Just put DataMapper::Enum instead of Enum and it should work.
In a more general discussion, are you sure you really need these abstractions ? One drawback I see in your code is that you won't be able to query your database for parts and harddrives because the logic is stored in ruby modules instead of in the database.
Update (after comment from author)
The general answer is: forget about STI. While ORM are nice to have, the best part of them is SQL backend abstraction. While they give you the impression that you can have a persisten object model, the abstractions often leak and STI is a good example. I won't go in large details here but you can find resources online. Best is that you stay close enough to SQL modelling best practices, like one-one, one-many and many-many relationsships.
Here is an updated version. I didn't test it and the method names are probably wrong, but you will get the idea:
class Part
property :serial_number
has_one Manufacturer
end
class HardDisk
property :technology
property :platters
property :model
#...
is_one Part
end
class Manufacturer
property :name #Fujitsu, ...
property :website
#...
has_many HardDisk, [:trough=>Part]
end
Upvotes: 2