Skip to content

Instantly share code, notes, and snippets.

@mbbx6spp
Forked from onethirtyfive/gist:1083182
Created July 14, 2011 21:45
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mbbx6spp/1083536 to your computer and use it in GitHub Desktop.
Save mbbx6spp/1083536 to your computer and use it in GitHub Desktop.
Class-Table Inheritance Meta-model for DataMapper
# Module with mixins for common queries across descendants:
module Descendant
# I guess I could use ActiveSupport::Concern here...
def self.included(receiver)
receiver.extend ClassMethods
receiver.send :include, InstanceMethods
end
module ClassMethods
def self.included(receiver)
ut = receiver.to_s.underscore.to_sym # since you already depend on AS for #to_sym
# const_set is asking for trouble later on...need to rethink this
Unit::TYPES << receiver.const_set(:UNIT_TYPE, ut)
end
def named(name)
first(:unit => Unit.first(:name => name))
end
end
module InstanceMethods
def unit_type
begin
self.class.const_get(:UNIT_TYPE)
rescue NameError => e
raise 'Unit subtypes must include Descendant module'
end
end
end
end
# Units:
class Unit
include DataMapper::Resource
# Added to when included by a descendant of unit...
TYPES = []
property :id, Integer, :serial => true
property :name, String
has n, :stats # not modeled here, just a one-to-many association
def type(*args)
options = args.extract_options!
types = normalize(args.shift)
# Ensure all descendant types are in TYPES
invalid_type unless (types - TYPES).empty?
# Prepare descendant type query
args.unshift(:all)
types.collect do |type|
type.class.to_s.camelize.send(args)
end.flatten
end
private
def normalize(specifier)
case
when specifier === :all
TYPES.dup
when specifier.respond_to?(:to_sym)
[specifier.to_sym]
when specifier.respond_to?(:to_ary)
specifier.to_ary
else
invalid_type
end
end
def invalid_type
raise ArgumentError.new('specifier values must be :all, or exist in Unit::TYPES')
end
end
class Champion
include DataMapper::Resource
include Descendant
property :unit_id, Integer
delegate :name, :name=, :to => :unit
end
class Structure
include DataMapper::Resource
include Descendant
property :unit_id, Integer
delegate :name, :name=, :to => :unit
end
# Usage:
@champions_and_structures = Unit.type([:champion,:structure]) # all champions and structures
@champions = Unit.type(:champion)
@specific_champion = Champion.named('Shen')
@turret = Structure.named('Turret')
# Might work?
@champions_stats = Unit.type(:champion).collect(&:stats)
# I can't think of a good use case, but arguments passed into type will be forwarded
# to *each* Descendant class as #all options. Any further ordering, filtering, or whatever
# of the results will have to be done programmatically.
@contrived = Unit.type(:all, :conditions => [])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment