I think what you are looking for at a SQL conceptual level is a junction table (map, link, resolver, pivot are also common names for handling many to many relationships). These junction tables are generally intermediary tables; however, additional attributes can and are often added to them.
The intention of the stated pseudo schema is a bit murky, but I think what you intended is that items can require multiple skills; items can also require other items, each with their own required skills, possibly own requisite items, and so and so forth, to many levels deep. Beware of circular references in your [many-to-many] self-referencing relationships, such as what could happen in 'containerItemMaps'. The following pseudo schema reflects how I envision the OP intent:
items (itemId PK, itemName, weight, volume, price)
skillMaps ( (itemId, skillId) PK)
skills (skillId PK, skillName)
containerItemMaps ( (containerItemId, componentItemId) PK)
-- containerItemId is the parent/requiring item id
-- componentItemId is the child/required item id
ActiveRecord terminology suggests 'has_and_belongs_to_many' as the type of association a relationship in a data model to use in this situation. For more information you may want to look at the page at datamapper.org Associations. Specifically the sections titled 'Has, and belongs to many (Or Many-To-Many)' and 'Self referential many to many relationships'
Because I am not a ruby guy at this point, I can only muddle with ruby code to give an example, but this is my best approximation of what your item class would look like:
# revised
class Item
include DataMapper::Resource
property :id, Serial
property :name, String, :required => true
property :weight, Float
property :volume, Float
property :price, Float
has n, :componentMaps, :child_key => [:container_id]
has n, :components, self, :through => :componentMaps, :via => :component
has n, :skillMaps, :child_key => [:skill_id]
has n, :skills, :through => :skillMaps, :via => :skill
end
And the map table for self-referencing many to many items, eg.. required items:
#revised
class ComponentMap
include DataMapper::Resource
belongs_to :container, 'Item', :key => true
belongs_to :component, 'Item', :key => true
property :quantity, Integer, :default => 1
end
For completeness:
class SkillMap
include DataMapper::Resource
belongs_to :item, 'Item', :key => true
belongs_to :skill, 'Skill', :key => true
property :mastery, Enum[:none, :aprentice, :journeyman, :craftsman, :master ], :default => :none
end
class Skill
include DataMapper::Resource
property :id, Serial
property :name, String, :required => true
has n, :skillMap, :child_key =>[:skill_id]
end
Revisions:
Noting your concerns, I went and installed an interpruter and debugger to verify the code compiled and the emitted sql was more ideal. Originally, I only was going off cursory examination of documentation. The structures above should produce generally well-structured sql from the mappings.
No matter which fields and structures you use, and no matter which ORM you pick (datamapper or some other provider), you will want to run the code through the debugger and pay attention to the sql it emits as sometimes the mappings aren't necessarily what you might first expect.
A second note about the junction tables (skillMap and componentMap): note my inclusion of additional fields (quantity and mastery). These seem to be a natural fit for the kind of application originally described, even though not originally specified. In a recipe, some ingredients are common among many different combinations, however the quantity from recipe to recipe varies. For skills, like ingredients, the skill level needed to perform certain activities varies, and thus I added a mastery field to the junction table skillMap.
Of course, you probably want to add appropriate business rules and helper functions (for accessing the composition of collections programmattically such as adding and removing elements, adding and removing groups of elements, and so on).
Hopefully this demonstrates a bit better a reason why you may want to consider and use the junction table over a straight hash. Of course each specific application is different, and perhaps the ability to specify additional aspects of the relationship between items and skill and items and other items isnt needed in your case.
Having and utilizing the extra control in defining the relationships explicitly has many advantages over relying on a dynamic/magic mapping. In some cases, I feel it is really needed, and I think in the case of a many-to-many, this is demonstrated. For one-to-many, the relationship is easier to infer, and using a more dynamic method of generating the mappings (eg. has n, : <attribute group>, :through => Resource) would be acceptable.
Best Answer
ERb is not an XML templating system. It is a plain text templating system. It doesn't know anything about XML, therefore it cannot guarantee anything about the generated XML code.
is perfectly valid ERb but of course not valid XML. This cannot happen with Builder: