module ThingTank::CharacterHandling
Public Class Methods
included(klass)
click to toggle source
# File lib/thingtank/character_handling.rb, line 3 def self.included(klass) klass.class_eval do def properties_of_character(klass) hsh = {} (klass.character_properties || []).each do |k| hsh.update(k.to_s => self[k.to_s]) unless self[k.to_s].nil? end hsh end def get_character(klass, db) hsh = properties_of_character(klass) hsh.update "_id" => self["_id"], "_rev" => self["_rev"] if (had?(klass) && !hsh.empty?) inst = klass.new hsh, :directly_set_attributes => true, :database => db @dependencies.save_character(inst) inst end # get property as pseudo doc to play with def with(key, &code) self[key] ||= {} case self[key] when String # assume we got a doc id doc = ThingTank.get(self[key]) (code.call(doc) ; doc.save) if code doc when Hash doc = self.class.new self[key], :directly_set_attributes => true, :database => FakeBase.new(self, nil) @dependencies.add_child(key, doc) @dependencies.refresh(false) (code.call(doc) ; doc.save) if code doc when Array with_all(key) else raise "not supported: #{self[key].inspect}" end end def with_all(key, props=nil) self[key] ||= [] props ||= [self[key]].flatten docs = props.collect { |prop| self.class.new prop, :directly_set_attributes => true, :database => FakeBase.new(self, nil) } @dependencies.add_child(key, docs) docs.each { |doc| yield doc ; doc.save } if block_given? docs end def with_nth(key,n) self[key] ||= [] props = [self[key]].flatten n = 0 if n == :first n = props.size-1 if n == :last n = 0 if n < 0 while n > props.size - 1 props << {} end docs = with_all(key, props) (yield docs[n] ; docs[n].save) if block_given? docs[n] end def with_first(key,&code) with_nth(key,:first, &code) end def with_last(key,&code) with_nth(key,:last, &code) end def property_to_character(key, klass) case self[key] when Hash with(key).to_character(klass) when Array with_all(key).collect { |doc| doc.to_character(klass) } end end # register a character def register_character(klass) self['characters'] ||= [] self['characters'] << klass.to_s unless self['characters'].include? klass.to_s end def unregister_character(character) if has?(character) self["characters"] ||= [] self["characters"].delete character.to_s end end def add_character(klass, key=nil, &code) if key key = key.to_s if val = self[key] self[key] = [val] unless val.is_a? Array self[key] << {} last_character(klass, key, &code) else self[key] = {} to_character(klass, key, &code) end else to_character(klass, &code) end end def _root @dependencies.find_root_doc end def delete_character(character_doc) klass = character_doc.class deleteable_attributes(klass).each { |k| delete(k) ; character_doc[k] = nil } @dependencies.already_saved(character_doc) # prevent an endless loop unregister_character(klass) @dependencies.remove_character(klass) @dependencies.refresh_parent() end def deleteable_attributes(klass) character_attrs = attributes_by_characters character = klass.to_s deleteable_attrs = klass.character_properties.select do |prop| character_attrs[prop].empty? || (character_attrs[prop].size == 1 && character_attrs[prop].first == character) end %w| _id _rev type update_me characters|.each{ |k| deleteable_attrs.delete k } return deleteable_attrs end def attributes_by_characters() attributes = {} self["characters"].each do |character| character.constantize.character_properties.each do |prop| attributes[prop] ||= [] attributes[prop] << character unless attributes[prop].include?(character) end end return attributes end def to_character(klass, key=nil, add_character=true, &code) @dependencies.add_character(klass) if add_character && key.nil? character = key ? property_to_character(key, klass) : FakeBase.new(self, klass, add_character).get() (code.call(character) ; character.flush_to_doc) if code # we don't do this. there is no proper way to update all given characters # one must not rely on a character properties after the doc has been manipulated # use Character#reload to get the latest from the doc object to the character and Character#reload! to reload the doc from the database and reload the character then #@dependencies.add_character_object(character) character end alias :as :to_character alias :has :to_character alias :act_as :to_character alias :be :to_character alias :have :to_character alias :are :to_character alias :is :to_character def save_character_attributes(character_doc) @dependencies.already_saved(character_doc) # prevent an endless loop attributes = character_doc.to_character_hash(true) unsavebales = attributes.keys - (character_doc.class.character_properties || []) raise "character #{character_doc.class} tried to save properties that it does not have: #{unsavebales.inspect}" unless unsavebales.empty? self.update_attributes attributes @dependencies.refresh_parent() end # if the doc when coming from db already had this character def had?(klass) return false unless self["characters"] and self["characters"].include? klass.name return true end # has the document the character, is it supposed to have it and does it have the necessary properties def has?(klass) return true if @dependencies.has_character?(klass) return false unless self["characters"] and self["characters"].include? klass.name could_be? klass end alias :is? :has? # could the document have the character def could_have?(klass) to_character(klass, nil, false).valid? end alias :could_be? :could_have? # characters that are not valid def invalid_characters (self["characters"] || []).select { |character| !self.as(character.constantize).valid? } end end end
Public Instance Methods
_root()
click to toggle source
# File lib/thingtank/character_handling.rb, line 112 def _root @dependencies.find_root_doc end
add_character(klass, key=nil, &code)
click to toggle source
# File lib/thingtank/character_handling.rb, line 96 def add_character(klass, key=nil, &code) if key key = key.to_s if val = self[key] self[key] = [val] unless val.is_a? Array self[key] << {} last_character(klass, key, &code) else self[key] = {} to_character(klass, key, &code) end else to_character(klass, &code) end end
attributes_by_characters()
click to toggle source
# File lib/thingtank/character_handling.rb, line 135 def attributes_by_characters() attributes = {} self["characters"].each do |character| character.constantize.character_properties.each do |prop| attributes[prop] ||= [] attributes[prop] << character unless attributes[prop].include?(character) end end return attributes end
could_have?(klass)
click to toggle source
could the document have the character
# File lib/thingtank/character_handling.rb, line 189 def could_have?(klass) to_character(klass, nil, false).valid? end
delete_character(character_doc)
click to toggle source
# File lib/thingtank/character_handling.rb, line 116 def delete_character(character_doc) klass = character_doc.class deleteable_attributes(klass).each { |k| delete(k) ; character_doc[k] = nil } @dependencies.already_saved(character_doc) # prevent an endless loop unregister_character(klass) @dependencies.remove_character(klass) @dependencies.refresh_parent() end
deleteable_attributes(klass)
click to toggle source
# File lib/thingtank/character_handling.rb, line 125 def deleteable_attributes(klass) character_attrs = attributes_by_characters character = klass.to_s deleteable_attrs = klass.character_properties.select do |prop| character_attrs[prop].empty? || (character_attrs[prop].size == 1 && character_attrs[prop].first == character) end %w| _id _rev type update_me characters|.each{ |k| deleteable_attrs.delete k } return deleteable_attrs end
get_character(klass, db)
click to toggle source
# File lib/thingtank/character_handling.rb, line 14 def get_character(klass, db) hsh = properties_of_character(klass) hsh.update "_id" => self["_id"], "_rev" => self["_rev"] if (had?(klass) && !hsh.empty?) inst = klass.new hsh, :directly_set_attributes => true, :database => db @dependencies.save_character(inst) inst end
had?(klass)
click to toggle source
if the doc when coming from db already had this character
# File lib/thingtank/character_handling.rb, line 175 def had?(klass) return false unless self["characters"] and self["characters"].include? klass.name return true end
has?(klass)
click to toggle source
has the document the character, is it supposed to have it and does it have the necessary properties
# File lib/thingtank/character_handling.rb, line 181 def has?(klass) return true if @dependencies.has_character?(klass) return false unless self["characters"] and self["characters"].include? klass.name could_be? klass end
invalid_characters()
click to toggle source
characters that are not valid
# File lib/thingtank/character_handling.rb, line 195 def invalid_characters (self["characters"] || []).select { |character| !self.as(character.constantize).valid? } end
properties_of_character(klass)
click to toggle source
# File lib/thingtank/character_handling.rb, line 6 def properties_of_character(klass) hsh = {} (klass.character_properties || []).each do |k| hsh.update(k.to_s => self[k.to_s]) unless self[k.to_s].nil? end hsh end
property_to_character(key, klass)
click to toggle source
# File lib/thingtank/character_handling.rb, line 74 def property_to_character(key, klass) case self[key] when Hash with(key).to_character(klass) when Array with_all(key).collect { |doc| doc.to_character(klass) } end end
register_character(klass)
click to toggle source
register a character
# File lib/thingtank/character_handling.rb, line 84 def register_character(klass) self['characters'] ||= [] self['characters'] << klass.to_s unless self['characters'].include? klass.to_s end
save_character_attributes(character_doc)
click to toggle source
# File lib/thingtank/character_handling.rb, line 165 def save_character_attributes(character_doc) @dependencies.already_saved(character_doc) # prevent an endless loop attributes = character_doc.to_character_hash(true) unsavebales = attributes.keys - (character_doc.class.character_properties || []) raise "character #{character_doc.class} tried to save properties that it does not have: #{unsavebales.inspect}" unless unsavebales.empty? self.update_attributes attributes @dependencies.refresh_parent() end
to_character(klass, key=nil, add_character=true, &code)
click to toggle source
# File lib/thingtank/character_handling.rb, line 146 def to_character(klass, key=nil, add_character=true, &code) @dependencies.add_character(klass) if add_character && key.nil? character = key ? property_to_character(key, klass) : FakeBase.new(self, klass, add_character).get() (code.call(character) ; character.flush_to_doc) if code # we don't do this. there is no proper way to update all given characters # one must not rely on a character properties after the doc has been manipulated # use Character#reload to get the latest from the doc object to the character and Character#reload! to reload the doc from the database and reload the character then #@dependencies.add_character_object(character) character end
unregister_character(character)
click to toggle source
# File lib/thingtank/character_handling.rb, line 89 def unregister_character(character) if has?(character) self["characters"] ||= [] self["characters"].delete character.to_s end end
with(key, &code)
click to toggle source
get property as pseudo doc to play with
# File lib/thingtank/character_handling.rb, line 23 def with(key, &code) self[key] ||= {} case self[key] when String # assume we got a doc id doc = ThingTank.get(self[key]) (code.call(doc) ; doc.save) if code doc when Hash doc = self.class.new self[key], :directly_set_attributes => true, :database => FakeBase.new(self, nil) @dependencies.add_child(key, doc) @dependencies.refresh(false) (code.call(doc) ; doc.save) if code doc when Array with_all(key) else raise "not supported: #{self[key].inspect}" end end
with_all(key, props=nil) { |doc ;| ... }
click to toggle source
# File lib/thingtank/character_handling.rb, line 43 def with_all(key, props=nil) self[key] ||= [] props ||= [self[key]].flatten docs = props.collect { |prop| self.class.new prop, :directly_set_attributes => true, :database => FakeBase.new(self, nil) } @dependencies.add_child(key, docs) docs.each { |doc| yield doc ; doc.save } if block_given? docs end
with_first(key,&code)
click to toggle source
# File lib/thingtank/character_handling.rb, line 66 def with_first(key,&code) with_nth(key,:first, &code) end
with_last(key,&code)
click to toggle source
# File lib/thingtank/character_handling.rb, line 70 def with_last(key,&code) with_nth(key,:last, &code) end
with_nth(key,n) { |docs ;| ... }
click to toggle source
# File lib/thingtank/character_handling.rb, line 52 def with_nth(key,n) self[key] ||= [] props = [self[key]].flatten n = 0 if n == :first n = props.size-1 if n == :last n = 0 if n < 0 while n > props.size - 1 props << {} end docs = with_all(key, props) (yield docs[n] ; docs[n].save) if block_given? docs[n] end