lib/candy/piece.rb in candy-0.2.7 vs lib/candy/piece.rb in candy-0.2.8

- old
+ new

@@ -31,36 +31,45 @@ search_keys[key] = Wrapper.wrap(fields[key]) end collection.update search_keys, fields, :upsert => true end + + # Deep magic! Finds and returns a single object by the named attribute. + def method_missing(name, *args, &block) + if args.size == 1 or args.size == 2 # If we don't have a value, or have more than + search = {name => args.shift} # just a simple options hash, this must not be for us. + search.merge!(args.shift) if args[0] # We might have other conditions + first(search) + else + super + end + end + + # Creates the object with parent and attribute values set properly on the object and any children. + def embed(parent, attribute, *args) + this = self.piece(*args) + this.candy_adopt(parent, attribute) + end + + # Makes a new object with a given state that is _not_ immediately saved, but is # held in memory instead. The principal use for this is to embed it in other # documents. Except for the unsaved state, this functions identically to 'new' # and will pass all its arguments to the initializer. (Note that you can still # embed documents that _have_ been saved--but then you'll have the data in two # places.) - def embed(*args, &block) + def piece(*args) if args[-1].is_a?(Hash) args[-1].merge!(EMBED_KEY => true) else args.push({EMBED_KEY => true}) end self.new(*args) end - - # Deep magic! Finds and returns a single object by the named attribute. - def method_missing(name, *args, &block) - if args.size == 1 or args.size == 2 # If we don't have a value, or have more than - search = {name => args.shift} # just a simple options hash, this must not be for us. - search.merge!(args.shift) if args[0] # We might have other conditions - first(search) - else - super - end - end + private # Creates a method in the same namespace as the included class that points to # 'first', for easier semantics. def self.extended(receiver) Factory.magic_method(receiver, 'first', 'conditions={}') @@ -70,20 +79,19 @@ # HERE STARTETH THE MODULE PROPER. (The above are the class methods.) include Crunch include Embeddable - # Our initializer checks the LAST argument passed to it, and pops it off the chain if it's a hash. - # If the hash contains an '_id' field we assume we're being constructed from a MongoDB document; - # otherwise we assume we're a new document and insert ourselves into the database. + # Our initializer expects the last argument to be a hash of values. If the hash contains an '_id' + # field we assume we're being constructed from a MongoDB document and we unwrap the remaining + # values; otherwise we assume we're a new document and set any values in the hash as if they + # were assigned. Any other arguments are not our business and will be passed down the chain. def initialize(*args, &block) if args[-1].is_a?(Hash) data = args.pop - if @__candy_id = data.delete('_id') # We're an existing document - @__candy = self.from_candy(Wrapper.unwrap(data)) - elsif data.delete(EMBED_KEY) # We're being embedded: take any data, but don't save to Mongo - @__candy = data + if data.delete(EMBED_KEY) or @__candy_id = data.delete('_id') # We're an embedded or existing document + @__candy = self.from_candy(data) else data.each {|key, value| send("#{key}=", value)} # Assign all the data we're given end end super @@ -94,11 +102,11 @@ @__candy_id end # Pull our document from the database if we know our ID. def retrieve_document - Wrapper.unwrap(collection.find_one({'_id' => id})) if id + from_candy(collection.find_one({'_id' => id})) if id end # Returns the hash of memoized values. def candy @@ -167,14 +175,18 @@ # this stage, so it's best to use symbols for keys and leave internal arrays and hashes alone. def to_candy candy.merge(CLASS_KEY => self.class.name) end - # A hoook for specific object classes to set their internal state using the hash passed in by - # MongoDB. If you override this method, delete any hash keys you need for your own purposes - # and then call 'super' on the remainder. + # Unwraps the values passed to us from MongoDB, setting parent attributes on any embedded Candy + # objects. def from_candy(hash) - hash + unwrapped = {} + hash.each do |key, value| + field = Wrapper.unwrap_key(key) + unwrapped[field] = Wrapper.unwrap(value, self, field) + end + unwrapped end # Given a hash of property/value pairs, sets those values in Mongo using the atomic $set if # we have a document ID. Otherwise inserts them and sets the object's ID. \ No newline at end of file