lib/couchpillow/document.rb in couchpillow-0.4.3 vs lib/couchpillow/document.rb in couchpillow-0.4.4
- old
+ new
@@ -6,32 +6,38 @@
attr_reader :id
RESERVED_KEYS = %i[_id _type _created_at _updated_at]
- DEFAULT_TYPE = "default".freeze
+ DEFAULT_TYPE = "couchpillow".freeze
- attribute(:_created_at)
- .required
- .type(Time).auto_convert
- .default { Time.now.utc }
+ attribute :_created_at do
+ required
+ type Time
+ auto_convert
+ default { Time.now.utc }
+ end
- attribute(:_updated_at)
- .required
- .type(Time).auto_convert
- .default { Time.now.utc }
+ attribute :_updated_at do
+ required
+ type Time
+ auto_convert
+ default { Time.now.utc }
+ end
- def initialize hash = {}, id = "#{self.class._type}::#{SecureRandom.hex}"
+ def initialize hash = {}, id = "#{self.class.doc_type}::#{SecureRandom.hex}"
@data = self.class.symbolize(hash)
@id = id
time = Time.now.utc
@data[:_created_at] ||= time
@data[:_updated_at] = time
+ @futures = []
+
rename!
whitelist!
assign_defaults!
auto_convert!
end
@@ -45,35 +51,50 @@
def []= key, value
@data[key.to_s.to_sym] = value
end
- # @private
- #
- def timestamp!
- @data[:_updated_at] = Time.now.utc
- end
-
-
# Save this document to the server
#
def save! opts = {}
whitelist!
sort!
timestamp!
validate!
to_save = @data.merge({
- :_type => self.class._type
+ :_type => self.class.doc_type
})
- CouchPillow.db.set(@id, to_save, opts)
+
+ # write to all connections
+ result = self.class.default_db.set(@id, to_save, opts)
+
+ unless self.class.secondary_dbs.empty?
+ @futures << Celluloid::Future.new do
+ self.class.secondary_dbs.each do |db|
+ db.set(@id, to_save, opts)
+ end
+ end
+ end
+
+ result
end
# Delete this document from the server.
#
def delete!
- CouchPillow.db.delete @id
+ result = self.class.default_db.delete @id
+
+ unless self.class.secondary_dbs.empty?
+ @futures << Celluloid::Future.new do
+ self.class.secondary_dbs.each do |db|
+ db.delete @id
+ end
+ end
+ end
+
+ result
end
# Attempt to update this Document. Fails if this Document does not yet
# exist in the database.
@@ -82,13 +103,24 @@
whitelist!
sort!
timestamp!
validate!
to_save = @data.merge({
- :_type => self.class._type
+ :_type => self.class.doc_type
})
- CouchPillow.db.replace @id, to_save
+
+ result = self.class.default_db.replace @id, to_save
+
+ unless self.class.secondary_dbs.empty?
+ @futures << Celluloid::Future.new do
+ self.class.secondary_dbs.each do |db|
+ db.replace @id, to_save
+ end
+ end
+ end
+
+ result
end
# Updates the attributes in the document.
# Existing attributes will be overwritten and new ones will be added.
@@ -98,28 +130,57 @@
hash.each do |k,v|
@data[k.to_sym] = v
end
rename!
whitelist!
+ auto_convert!
end
+ # Check if this Document has the key
+ #
def has? key
@data.has_key?(key)
end
+ # Convert this Document to a JSON string
+ #
def to_json *a
to_hash.to_json(*a)
end
+ # Convert this Document to a Hash
+ #
def to_hash
- { :_id => @id, :_type => self.class._type }.merge!(@data)
+ { :_id => @id, :_type => self.class.doc_type }.merge!(@data)
end
+ # Helper to get the type of this Document.
+ # Can't really name this `type`. Need to avoid name conflict with Ruby's own `type` method.
+ #
+ def doc_type
+ self.class.doc_type
+ end
+
+
+ # Blocks until all pending tasks has completed.
+ # Returns the result of those tasks in an array.
+ #
+ def wait
+ result = []
+ until @futures.empty?
+ f = @futures.shift
+ result << f.value
+ end
+
+ result
+ end
+
+
# Rename the keys in this Document as specified by the {rename} directive.
#
def rename!
self.class.rename_keys.each do |from, to|
@data.has_key?(from) and
@@ -136,11 +197,11 @@
!self.class.attributes.has_key?(k)
end
end
- # Assign default value
+ # Assign default values.
#
def assign_defaults!
self.class.attributes.each do |k, attr|
@data[k] = attr.trigger_default_directive if !has?(k) && attr.has_default?
end
@@ -155,17 +216,16 @@
end
end
# Go through each attribute, and validate the values.
- # Validation also perform auto-conversion if auto-conversion is enabled
- # for that attribute.
+ # Validation also perform auto-conversion if auto-conversion is enabled for that attribute.
#
def validate!
self.class.attributes.each do |k, attr|
if has?(k)
- @data[k] = attr.validate(@data[k]) if has?(k)
+ @data[k] = attr.validate(@data[k])
else
@data[k] = attr.trigger_default_directive if attr.has_default?
raise ValidationError, "Attribute '#{k}' is required" if attr.required? && !has?(k)
end
end
@@ -177,28 +237,18 @@
def sort!
@data = @data.sort.to_h
end
- def _type
- self.class._type
- end
-
-
- def _id
- @id
- end
-
-
# Get a Document given an id.
#
# @return nil if not found or Document is of a different type.
#
def self.get id
- result = CouchPillow.db.get(id) and
+ result = default_db.get(id) and
type = result[:_type] || result["_type"] and
- type == _type and
+ type == doc_type and
new(result, id) or
nil
end
@@ -218,19 +268,56 @@
def self.type value
@type = value.to_s
end
+ # Set a DB connection. Overrides the default CouchPillow.db connection
+ # for the first time this method gets called. Subsequent calls will set
+ # secondary connections, which will only be used for write only.
+ #
+ # Example:
+ # db primary_db # use for both read and write
+ # db backup_db1 # write only
+ # db backup_db2 # write only
+ #
+ def self.db conn
+ # set the primary db connection
+ @primary_db ||= conn
+
+ # insert as backup db connections
+ if conn && @primary_db && conn != @primary_db
+ secondary_dbs << conn
+ end
+ end
+
+
private
- def self._type
+ # Timestamp this document
+ #
+ def timestamp!
+ @data[:_updated_at] = Time.now.utc
+ end
+
+
+ def self.doc_type
@type ||= DEFAULT_TYPE
end
def self.rename_keys
@rename_keys ||= []
+ end
+
+
+ def self.default_db
+ @default_db ||= (@primary_db || CouchPillow.db)
+ end
+
+
+ def self.secondary_dbs
+ @secondary_dbs ||= []
end
def self.symbolize hash
hash.inject({}) do |memo,(k,v)|