Merge branch 'master' of git://github.com/couchrest/couchrest_model

This commit is contained in:
Lucas Renan 2011-05-23 08:48:10 -03:00
commit ccafde77ab
9 changed files with 184 additions and 73 deletions

View file

@ -153,7 +153,7 @@ module CouchRest
def #{attrib}(reload = false)
return @#{attrib} unless @#{attrib}.nil? or reload
ary = self.#{options[:foreign_key]}.collect{|i| #{options[:proxy]}.get(i)}
@#{attrib} = ::CouchRest::CollectionOfProxy.new(ary, self, '#{options[:foreign_key]}')
@#{attrib} = ::CouchRest::Model::CollectionOfProxy.new(ary, find_property('#{options[:foreign_key]}'), self)
end
EOS
end
@ -161,7 +161,7 @@ module CouchRest
def create_collection_of_setter(attrib, options)
class_eval <<-EOS, __FILE__, __LINE__ + 1
def #{attrib}=(value)
@#{attrib} = ::CouchRest::CollectionOfProxy.new(value, self, '#{options[:foreign_key]}')
@#{attrib} = ::CouchRest::Model::CollectionOfProxy.new(value, find_property('#{options[:foreign_key]}'), self)
end
EOS
end
@ -169,67 +169,63 @@ module CouchRest
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
# Special proxy for a collection of items so that adding and removing
# to the list automatically updates the associated property.
class CollectionOfProxy < CastedArray
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|
check_obj(obj)
casted_by[property.to_s] << obj.id
def initialize(array, property, parent)
(array ||= []).compact!
super(array, property, parent)
casted_by[casted_by_property.to_s] = [] # replace the original array!
array.compact.each do |obj|
check_obj(obj)
casted_by[casted_by_property.to_s] << obj.id
end
end
def << obj
check_obj(obj)
casted_by[casted_by_property.to_s] << obj.id
super(obj)
end
def push(obj)
check_obj(obj)
casted_by[casted_by_property.to_s].push obj.id
super(obj)
end
def unshift(obj)
check_obj(obj)
casted_by[casted_by_property.to_s].unshift obj.id
super(obj)
end
super(array)
end
def << obj
check_obj(obj)
casted_by[property.to_s] << obj.id
super(obj)
end
def push(obj)
check_obj(obj)
casted_by[property.to_s].push obj.id
super(obj)
end
def unshift(obj)
check_obj(obj)
casted_by[property.to_s].unshift obj.id
super(obj)
end
def []= index, obj
check_obj(obj)
casted_by[property.to_s][index] = obj.id
super(index, obj)
end
def []= index, obj
check_obj(obj)
casted_by[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
def pop
casted_by[casted_by_property.to_s].pop
super
end
def shift
casted_by[casted_by_property.to_s].shift
super
end
protected
protected
def check_obj(obj)
raise "Object cannot be added to #{casted_by.class.to_s}##{casted_by_property.to_s} collection unless saved" if obj.new?
end
def check_obj(obj)
raise "Object cannot be added to #{casted_by.class.to_s}##{property.to_s} collection unless saved" if obj.new?
end
end
end

View file

@ -50,6 +50,12 @@ module CouchRest::Model
super
end
def build(*args)
obj = casted_by_property.build(*args)
self.push(obj)
obj
end
protected
def instantiate_and_cast(obj, change = true)

View file

@ -64,6 +64,18 @@ module CouchRest::Model
end
end
# Initialize a new instance of a property's type ready to be
# used. If a proc is defined for the init method, it will be used instead of
# a normal call to the class.
def build(*args)
raise StandardError, "Cannot build property without a class" if @type_class.nil?
if @init_method.is_a?(Proc)
@init_method.call(*args)
else
@type_class.send(@init_method, *args)
end
end
private
def associate_casted_value_to_parent(parent, value)

View file

@ -14,8 +14,7 @@ module CouchRest
elsif [String, TrueClass, Integer, Float, BigDecimal, DateTime, Time, Date, Class].include?(klass)
send('typecast_to_'+klass.to_s.downcase, value)
else
# Allow the init_method to be defined as a Proc for advanced conversion
property.init_method.is_a?(Proc) ? property.init_method.call(value) : klass.send(property.init_method, value)
property.build(value)
end
end

View file

@ -3,7 +3,7 @@
module CouchRest
module Model
module Validations
# Validates if a field is unique
class UniquenessValidator < ActiveModel::EachValidator
@ -11,29 +11,33 @@ module CouchRest
# or add one if necessary.
def setup(model)
@model = model
if options[:view].blank?
attributes.each do |attribute|
opts = merge_view_options(attribute)
if model.respond_to?(:has_view?) && !model.has_view?(opts[:view_name])
opts[:keys] << {:allow_nil => true}
model.view_by(*opts[:keys])
end
end
end
end
def validate_each(document, attribute, value)
keys = [attribute]
unless options[:scope].nil?
keys = (options[:scope].is_a?(Array) ? options[:scope] : [options[:scope]]) + keys
end
values = keys.map{|k| document.send(k)}
values = values.first if values.length == 1
opts = merge_view_options(attribute)
view_name = options[:view].nil? ? "by_#{keys.join('_and_')}" : options[:view]
values = opts[:keys].map{|k| document.send(k)}
values = values.first if values.length == 1
model = (document.respond_to?(:model_proxy) && document.model_proxy ? document.model_proxy : @model)
# Determine the base of the search
base = options[:proxy].nil? ? model : document.instance_eval(options[:proxy])
base = opts[:proxy].nil? ? model : document.instance_eval(opts[:proxy])
if base.respond_to?(:has_view?) && !base.has_view?(view_name)
raise "View #{document.class.name}.#{options[:view]} does not exist!" unless options[:view].nil?
keys << {:allow_nil => true}
model.view_by(*keys)
if base.respond_to?(:has_view?) && !base.has_view?(opts[:view_name])
raise "View #{document.class.name}.#{opts[:view_name]} does not exist for validation!"
end
rows = base.view(view_name, :key => values, :limit => 2, :include_docs => false)['rows']
rows = base.view(opts[:view_name], :key => values, :limit => 2, :include_docs => false)['rows']
return if rows.empty?
unless document.new?
@ -47,6 +51,17 @@ module CouchRest
end
end
private
def merge_view_options(attr)
keys = [attr]
keys.unshift(*options[:scope]) unless options[:scope].nil?
view_name = options[:view].nil? ? "by_#{keys.join('_and_')}" : options[:view]
options.merge({:keys => keys, :view_name => view_name})
end
end
end