Skip to content

Instantly share code, notes, and snippets.

@boffbowsh
Created July 24, 2008 15:14
Show Gist options
  • Save boffbowsh/2172 to your computer and use it in GitHub Desktop.
Save boffbowsh/2172 to your computer and use it in GitHub Desktop.
Put has_metadata.rb in lib, then use has_metadata :os in your model
class CreateMetaKeys < ActiveRecord::Migration
def self.up
create_table :meta_meta_key do |t|
t.integer :data_type, :limit => 1
t.string :object_type, :associated_type, :key, :description
t.timestamps
end
MetaKey.reset_column_information
MetaKey.create! :key => "os", :description => "Operating System", :data_type => :String,
:object_type => "File"
end
end
def self.down
drop_table :meta_meta_key
end
end
class CreateMetastores < ActiveRecord::Migration
def self.up
create_table :meta_metastore do |t|
t.integer :key_id, :object_id
t.timestamps
t.string :value
end
end
def self.down
drop_table :meta_metastore
end
end
module ActiveRecord
module Acts
module Metadata
def self.included(base)
base.extend ClassMethods
end
module InstanceMethods
def find_meta_for_belongs_to key_id, association_class
return nil if self.id.nil?
assoc = association_class
data = assoc.find_by_sql <<-EOF
SELECT * FROM #{assoc.table_name} a_t
INNER JOIN #{Metastore.table_name} m_t ON m_t.value = a_t.#{assoc.primary_key}
WHERE m_t.object_id = #{self.id}
AND m_t.key_id = #{key_id}
EOF
data[0]
end
def find_meta_for_has_many key_id, association_class
return [] if self.id.nil?
assoc = association_class
assoc.find_by_sql <<-EOF
SELECT * FROM #{assoc.table_name} a_t WHERE a_t.#{assoc.primary_key} IN
( SELECT value FROM #{Metastore.table_name} m_t
WHERE m_t.object_id = #{self.id}
AND m_t.key_id = #{key_id} )
EOF
end
def find_meta_for_scalar key_id, data_type
return nil if self.id.nil?
metadata = get_meta_for key_id
cast_dispatch = {:String => :to_s, :Fixnum => :to_i, :Time => :to_time}
metadata.value.send cast_dispatch[data_type.to_sym]
end
def update_meta_belongs_to key_id, association_class, new_assoc
raise ActiveRecord::RecordNotSaved if self.id.nil?
raise ActiveRecord::AssociationTypeMismatch unless
new_assoc.is_a? association_class
metadata = get_meta_for key_id
metadata.value = new_assoc.id
metadata.save!
end
def update_meta_has_many key_id, association_class, new_assocs
raise ActiveRecord::RecordNotSaved if self.id.nil?
new_assocs = [new_assocs] unless new_assocs.is_an? Array
raise ActiveRecord::AssociationTypeMismatch unless
new_assocs.map {|assoc| assoc.is_a? association_class}.all?
metadata = get_meta_for key_id
metadata.value = new_assocs.map(&:id).join ','
metadata.save!
end
def update_meta_scalar key_id, new_value
raise ActiveRecord::RecordNotSaved if self.id.nil?
metadata = get_meta_for key_id
metadata.value = new_value
metadata.save!
end
def get_meta_for key_id
meta = Metastore.find :first,
:conditions => {:object_id => self.id,
:key_id => key_id}
meta = !meta.nil? ? meta : (Metastore.new :object_id => self.id,
:key_id => key_id)
end
end
module ClassMethods
def has_metadata *keys
send :include, InstanceMethods
class_eval "@@metadata_keys = #{keys.inspect}"
class_eval "def self.metadata_keys; @@metadata_keys; end"
keys.map(&:to_s).each do |key|
meta_key = MetaKey.find :first,
:conditions => { :object_type => yaml_tag_class_name,
:key => key }
raise InvalidMetaKey.new(key) unless meta_key.is_a?(MetaKey)
class_eval "attr_protected #{key.inspect}"
case meta_key.data_type
when :belongs_to
class_eval <<-EOF
def #{meta_key.key}
find_meta_for_belongs_to #{meta_key.id}, #{meta_key.associated_type}
end
def #{meta_key.key}= value
update_meta_belongs_to #{meta_key.id}, #{meta_key.associated_type}, value
end
EOF
when :has_many
class_eval <<-EOF
def #{meta_key.key}
find_meta_for_has_many #{meta_key.id}, #{meta_key.associated_type}
end
def #{meta_key.key}= value
update_meta_has_many #{meta_key.id}, #{meta_key.associated_type}, value
end
EOF
else
class_eval <<-EOF
def #{meta_key.key}
find_meta_for_scalar #{meta_key.id}, #{meta_key.data_type.inspect}
end
def #{meta_key.key}= value
update_meta_scalar #{meta_key.id}, value
end
EOF
end
end
end
end
class InvalidMetaKey < Exception
attr_reader :key
def initialize key
@key = key
end
def message
"Invalid key #{key}"
end
end
end
end
end
ActiveRecord::Base.send :include, ActiveRecord::Acts::Metadata
class MetaKey < ActiveRecord::Base
set_table_name "meta_meta_key"
validates_uniqueness_of :key, :scope => :object_type
acts_as_enum :data_type, [:String, :Fixnum, :Time, :belongs_to, :has_many]
has_many :metadatas
end
class Metadata < ActiveRecord::Base
set_table_name "meta_metadata"
belongs_to :meta_object, :polymorphic => true
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment