views:

556

answers:

1

I started learning Datamapper and what I liked about it was that I can write my models with real inheritance.

Now I wonder, if it is possible to be more advanced about this:

class Event
  include DataMapper::Resource
  property :id, Serial
  property :begin, DateTime
  property :type, Discriminator
end

class Talk<Event
  property :title, String
  belongs_to :meeting
end

class Meeting<Event
  has n, :talks
end

That code fails to create the :title column for the Talk and obviously, the discriminator column is of little value here, because from a database view, there should be separate tables for both Talk and Meeting.

So, in the end, I want Talk and Meeting to share the same properties as defined in Event but with possible additional properties and with a 0..1:n relation (A meeting can have several talks but there are talks without a meeting.) Is there a way to accomplish this without either repeating the column definitions and/or abandoning inheritance?

Edit

To give another example: The part that I like about the inheritance thing is, that general Events can be queried separately. So, when I want to know, if there is something at a certain :begin date, I don’t need to look in two or more tables but could just query the Event table. In a way, the following structure could fit my needs.

class Event
  include DataMapper::Resource
  property :id, Serial
  property :begin, DateTime
end

class Talk
  include DataMapper::Resource
  property :id, Serial
  property :title, String
  belongs_to :event
  belongs_to :meeting
end

class Meeting
  include DataMapper::Resource
  property :id, Serial
  belongs_to :event
  has n, :talks
end

However, in order to use this, I would need to manually create an Event every time, I want to create or edit a Talk. That is, I can’t do talk.begin or Talk.create(:begin => Time.now). Is there a way around this without patching all functions and merging the properties? I don’t want to be reminded of the underlying structure when using the model.

+2  A: 

If you want to replicate the attributes of Event into Talk and Meeting then you could move it into a module:

module EventFields
  def self.included(base)
    base.class_eval do
      include DataMapper::Resource
      property :id, DataMapper::Types::Serial
      property :begin, DateTime 
      # other fields here
    end
  end
end

class Talk
  include EventFields
  property :title, String
  belongs_to :meeting
end

class Meeting
  include EventFields
  has n, :talks
end

This will give you different tables but means duplication is reduced.

JamesAlmond
But this unfortunately means, there is no transparent way to query all Events at the same time, so I would have to duplicate for all types of events.
Debilski
True, but single table inheritance is only reasonably suitable for objects that behave differently but have the same underlying structure/table.You'll need to take the hit of storing all attributes (for instance in the Event class) and building the logic for the relationships inside each child class.Unfortunately, STI suggests there is some magic flexible data solution but it's rarely the case. As the models grow apart they'll become too tightly coupled and STI won't make sense any more.
JamesAlmond
You are right, I will have to make a compromise anyway for such structures. Hopefully, at some point, DataMapper::EmbeddedValue will be available to ease your suggestion.
Debilski