lib/exegesis/document.rb in mattly-exegesis-0.0.10 vs lib/exegesis/document.rb in mattly-exegesis-0.2.0

- old
+ new

@@ -1,153 +1,93 @@ module Exegesis - class Document < CouchRest::Document + module Document - def self.inherited subklass - Exegesis.document_classes[subklass.name] = subklass + def self.included base + base.send :include, Exegesis::Model + base.extend ClassMethods + base.send :include, InstanceMethods + base.send :attr_accessor, :database end + + module ClassMethods + def timestamps! + define_method :set_timestamps do + @attributes['updated_at'] = Time.now + @attributes['created_at'] ||= Time.now + end + expose 'updated_at', :as => Time, :writer => false + expose 'created_at', :as => Time, :writer => false + end - def self.instantiate hash={} - Exegesis.document_classes[hash['.kind']].new(hash) + def unique_id meth=nil, &block + if block + @unique_id_method = block + elsif meth + @unique_id_method = meth + else + @unique_id_method ||= nil + end + end end - def self.expose *attrs - opts = if attrs.last.is_a?(Hash) - attrs.pop - else - {} + module InstanceMethods + def initialize hash={}, db=nil + super hash + @database = db end - [attrs].flatten.each do |attrib| - attrib = "#{attrib}" - if opts.has_key?(:writer) - if opts[:writer] - define_method("#{attrib}=") {|val| self[attrib] = opts[:writer].call(val) } - end + def == other + self.id == other.id + end + + def id + @attributes['_id'] + end + + def rev + @attributes['_rev'] + end + + def save + set_timestamps if respond_to?(:set_timestamps) + if self.class.unique_id && id.nil? + save_with_custom_unique_id else - define_method("#{attrib}=") {|val| self[attrib] = val } + save_document end - if opts[:as] - if opts[:as] == :reference - define_method(attrib) do |*reload| - reload = false if reload.empty? - instance_variable_set("@#{attrib}", nil) if reload - return instance_variable_get("@#{attrib}") if instance_variable_get("@#{attrib}") - instance_variable_set("@#{attrib}", load_reference(self[attrib])) - end - else - define_method(attrib) do - self[attrib] = if self[attrib].is_a?(Array) - self[attrib].map {|val| cast opts[:as], val }.compact - else - cast opts[:as], self[attrib] - end - end - end - else - define_method(attrib) { self[attrib] } - end end - end - - def self.default hash=nil - if hash - @default = hash - else - @default ||= superclass.respond_to?(:default) ? superclass.default : {} + + def update_attributes attrs={} + raise ArgumentError, 'must include a matching _rev attribute' unless rev == attrs.delete('_rev') + super attrs + save end - end - - def self.timestamps! - define_method :set_timestamps do - self['updated_at'] = Time.now - self['created_at'] ||= Time.now + + private + + def save_document + raise ArgumentError, "canont save without a database" unless database + database.save self.attributes end - expose 'updated_at', :as => Time, :writer => false - expose 'created_at', :as => Time, :writer => false - end - - def self.unique_id meth - define_method :set_unique_id do - self['_id'] = self.send(meth) - end - end - - alias :_rev :rev - alias_method :document_save, :save - - attr_accessor :parent - - def save - raise ChildError, "cannot save if a parent is set" if parent - set_timestamps if respond_to?(:set_timestamps) - if respond_to?(:set_unique_id) && id.nil? - @unique_id_attempt = 0 + + def save_with_custom_unique_id + attempt = 0 + value = '' begin - self['_id'] = set_unique_id - document_save + @attributes['_id'] = if self.class.unique_id.is_a?(Proc) + self.class.unique_id.call(self, attempt) + else + self.send(self.class.unique_id, attempt) + end + save_document rescue RestClient::RequestFailed => e - @unique_id_attempt += 1 + oldvalue = value + value = @attributes['_id'] + raise RestClient::RequestFailed if oldvalue == value || attempt > 100 + attempt += 1 retry end - else - document_save end end - def initialize keys={} - apply_default - super keys - self['.kind'] ||= self.class.to_s - end - - def update_attributes attrs={} - raise ArgumentError, 'must include a matching _rev attribute' unless rev == attrs.delete('_rev') - attrs.each_pair do |key, value| - self.send("#{key}=", value) rescue nil - attrs.delete(key) - end - save - end - - private - - def apply_default - self.class.default.each do |key, value| - self[key] = value - end - end - - def cast as, value - return nil if value.nil? - klass = if as == :given - if value.is_a?(Hash) - Exegesis.document_classes[value['.kind']] - else - nil #nfi what do to in this case; maybe just have it be a doc hash? - end - elsif as.is_a?(Class) - as - else - Exegesis.document_classes[nil] # whatever the default is? Hell idk. - end - - with = klass == Time ? :parse : :new - casted = klass.send with, value - casted.parent = self if casted.respond_to?(:parent) - casted - end - - def load_reference ids - raise ArgumentError, "a database is required for loading a reference" unless database || (parent && parent.database) - if ids.is_a?(Array) - ids.map {|val| Exegesis::Document.instantiate((database || parent && parent.database).get(val)) } - else - Exegesis::Document.instantiate((database || parent && parent.database).get(ids)) - end - end - end -end - -# $:.unshift File.dirname(__FILE__) -# require 'document/annotated_reference' -# require 'document/referencing' +end \ No newline at end of file