Renaming to CouchRest Model

Refactored basic directory structure.
Moved to ActiveSupport for Validations and Callbacks.
Cleaned up older code, and removed support for text property types.
This commit is contained in:
Sam Lown 2010-06-20 22:01:11 +02:00
parent 9f1eea8d32
commit c280b3a29b
70 changed files with 1725 additions and 3733 deletions

View file

@ -0,0 +1,207 @@
module CouchRest
module Model
module Associations
# Basic support for relationships between ExtendedDocuments
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
# Define an association that this object belongs to.
#
def belongs_to(attrib, *options)
opts = {
:foreign_key => attrib.to_s + '_id',
:class_name => attrib.to_s.camelcase,
:proxy => nil
}
case options.first
when Hash
opts.merge!(options.first)
end
begin
opts[:class] = opts[:class_name].constantize
rescue
raise "Unable to convert class name into Constant for #{self.name}##{attrib}"
end
prop = property(opts[:foreign_key], opts)
create_belongs_to_getter(attrib, prop, opts)
create_belongs_to_setter(attrib, prop, opts)
prop
end
# Provide access to a collection of objects where the associated
# property contains a list of the collection item ids.
#
# The following:
#
# collection_of :groups
#
# creates a pseudo property called "groups" which allows access
# to a CollectionOfProxy object. Adding, replacing or removing entries in this
# proxy will cause the matching property array, in this case "group_ids", to
# be kept in sync.
#
# Any manual changes made to the collection ids property (group_ids), unless replaced, will require
# a reload of the CollectionOfProxy for the two sets of data to be in sync:
#
# group_ids = ['123']
# groups == [Group.get('123')]
# group_ids << '321'
# groups == [Group.get('123')]
# groups(true) == [Group.get('123'), Group.get('321')]
#
# Of course, saving the parent record will store the collection ids as they are
# found.
#
# The CollectionOfProxy supports the following array functions, anything else will cause
# a mismatch between the collection objects and collection ids:
#
# groups << obj
# groups.push obj
# groups.unshift obj
# groups[0] = obj
# groups.pop == obj
# groups.shift == obj
#
# Addtional options match those of the the belongs_to method.
#
# NOTE: This method is *not* recommended for large collections or collections that change
# frequently! Use with prudence.
#
def collection_of(attrib, *options)
opts = {
:foreign_key => attrib.to_s.singularize + '_ids',
:class_name => attrib.to_s.singularize.camelcase,
:proxy => nil
}
case options.first
when Hash
opts.merge!(options.first)
end
begin
opts[:class] = opts[:class_name].constantize
rescue
raise "Unable to convert class name into Constant for #{self.name}##{attrib}"
end
opts[:readonly] = true
prop = property(opts[:foreign_key], [], opts)
create_collection_of_property_setter(attrib, prop, opts)
create_collection_of_getter(attrib, prop, opts)
create_collection_of_setter(attrib, prop, opts)
prop
end
private
def create_belongs_to_getter(attrib, property, options)
base = options[:proxy] || options[:class_name]
class_eval <<-EOS, __FILE__, __LINE__ + 1
def #{attrib}
@#{attrib} ||= #{base}.get(self.#{options[:foreign_key]})
end
EOS
end
def create_belongs_to_setter(attrib, property, options)
class_eval <<-EOS, __FILE__, __LINE__ + 1
def #{attrib}=(value)
self.#{options[:foreign_key]} = value.nil? ? nil : value.id
@#{attrib} = value
end
EOS
end
### collection_of support methods
def create_collection_of_property_setter(attrib, property, options)
# ensure CollectionOfProxy is nil, ready to be reloaded on request
class_eval <<-EOS, __FILE__, __LINE__ + 1
def #{options[:foreign_key]}=(value)
@#{attrib} = nil
write_attribute("#{options[:foreign_key]}", value)
end
EOS
end
def create_collection_of_getter(attrib, property, options)
base = options[:proxy] || options[:class_name]
class_eval <<-EOS, __FILE__, __LINE__ + 1
def #{attrib}(reload = false)
return @#{attrib} unless @#{attrib}.nil? or reload
ary = self.#{options[:foreign_key]}.collect{|i| #{base}.get(i)}
@#{attrib} = ::CouchRest::CollectionOfProxy.new(ary, self, '#{options[:foreign_key]}')
end
EOS
end
def create_collection_of_setter(attrib, property, options)
class_eval <<-EOS, __FILE__, __LINE__ + 1
def #{attrib}=(value)
@#{attrib} = ::CouchRest::CollectionOfProxy.new(value, self, '#{options[:foreign_key]}')
end
EOS
end
end
end
end
# Special proxy for a collection of items so that adding and removing
# to the list automatically updates the associated property.
class CollectionOfProxy < Array
attr_accessor :property
attr_accessor :casted_by
def initialize(array, casted_by, property)
self.property = property
self.casted_by = casted_by
(array ||= []).compact!
casted_by[property.to_s] = [] # replace the original array!
array.compact.each do |obj|
casted_by[property.to_s] << obj.id
end
super(array)
end
def << obj
casted_by[property.to_s] << obj.id
super(obj)
end
def push(obj)
casted_by[property.to_s].push obj.id
super(obj)
end
def unshift(obj)
casted_by[property.to_s].unshift obj.id
super(obj)
end
def []= index, obj
casted_by[property.to_s][index] = obj.id
super(index, obj)
end
def pop
casted_by[property.to_s].pop
super
end
def shift
casted_by[property.to_s].shift
super
end
end
end