class Player
include DataMapper::Resource
property :id, Serial
# …
has n, :items
end
class Item
include DataMapper::Resource
property :id, Serial
belongs_to :player
end
class Player
include DataMapper::Resource
property :id, Serial
# …
has 1, :inventory
end
class Inventory
# …
end
class Item
#…
end
class Player
include DataMapper::Resource
property :id, Serial
property :name, String
# …
# Query items short circuit.
def items; inventory.items; end
has 1, :inventory
end
class Inventory
include DataMapper::Resource
property :id, Serial
has n, items
belongs_to :player
def count(opts={}); items.count(opts); end # return the number of items in the inventory.
def heaviest; items.max(:weight); end # Maybe so you can have them drop the heaviest item?
def weight; items.sum(:weight); end # Total weight.
end
class Item
include DataMapper::Resource
property :id, Serial
property :tag, String
property :weight, Integer # just for an example field and the purpose of the Inventory container.
belongs_to :inventory
end
DM.finalize!
# somewhere after init of datamapper database and auto db migration.
# create a player
# query for the first player matching the query, otherwise instance and save it with the query parameters + arg2.
Retnur = Player.first_or_create({:name=>"Retnur"},
{:inventory=>Inventory.new()})
# now we have Retnur's model to access including an inventory.
# Create a new item and place it in the inventory.
Retnur.items.create {:tag=>"sword"}
# Query inventory and list all items by tag.
Retnur.items.all.each { |obj| puts obj.tag }
# By this point you would only see "sword"
# So destroy all Retnur's inventory. i.e. full table drop/purge
Retnur.items.destroy! # bang for no validations, it's faster if you don't need them/none defined.
# This was a naive example. More commonly you'd see something like this:
# Lookup item with vnum 10 place it in inventory.
Retnur.items << Item.first(:vnum=>10)
Retnur.save # persisted
# so to query all items.
Item.all
# to query only items Retnur has
Retnur.items.all
# and if Chris existed you could find intersections and all sorts of neat things easily.
(Chris.items.all - Retnur.items.all).each {|item| puts item.tag}
# For a final example, which is going to be useful to you, and to give you an understanding of the
# modification and lazy query aspect consider needing to purge only inactive player files.
players = Player.all # a query for all players..not yet acted on. I.e. free.
# query all players (cost now paid once) and modify the query in place if they are active.
players.each { |ch| players = players.all(:id.not=>ch.id) if player.active? }
# And there's probably cuter ways to modify that query.
#instead of
players = players.all(:id.not=>ch.id)
# maybe this works:
players -= ch
#query now only contains players inactive.
players.destroy! # cost paid for the second time.
# Compare it to this:
# cost paid first time.
Player.all.each {|ch| ch.destroy! if !ch.active? } # cost paid n times
# where n is the number of inactive players.
# If you have a large store of player files this would be highly inefficient (although it works).
# For this particular example, it probably won't matter that much in the long run.
# But it is fairly important to realize that building the query is not the same as executing it.
Player.all # cost never paid because no each call.
Player.all.all.all.all.all # cost never paid.
Player.all.all.all.all.all.each {} # cost only paid once.
create table players (
player_id integer primary key not null,
…
);
create table items (
item_id integer primary key not null,
…
);
create table inventory (
player_id integer references players (player_id),
item_id integer references items (item_id),
count integer
);
EDIT : My other idea was to just map relationships between the item and owner and store them in the traditional way. I wasn't sure which would be better? I don't work with databases very often!