2010-06-20 22:01:11 +02:00
|
|
|
# encoding: utf-8
|
|
|
|
module CouchRest::Model
|
2009-01-30 03:45:01 +01:00
|
|
|
class Property
|
2010-06-16 21:04:53 +02:00
|
|
|
|
2010-06-20 22:01:11 +02:00
|
|
|
include ::CouchRest::Model::Typecast
|
2010-06-16 21:04:53 +02:00
|
|
|
|
2011-03-06 03:41:37 +01:00
|
|
|
attr_reader :name, :type, :type_class, :read_only, :alias, :default, :casted, :init_method, :options
|
2009-07-20 23:17:27 +02:00
|
|
|
|
2010-06-16 21:04:53 +02:00
|
|
|
# Attribute to define.
|
|
|
|
# All Properties are assumed casted unless the type is nil.
|
2009-02-06 02:06:12 +01:00
|
|
|
def initialize(name, type = nil, options = {})
|
2009-02-09 20:20:23 +01:00
|
|
|
@name = name.to_s
|
2010-06-16 21:04:53 +02:00
|
|
|
@casted = true
|
2009-02-13 05:28:07 +01:00
|
|
|
parse_type(type)
|
2009-01-30 03:45:01 +01:00
|
|
|
parse_options(options)
|
|
|
|
self
|
|
|
|
end
|
2009-07-20 23:17:27 +02:00
|
|
|
|
2010-06-16 21:04:53 +02:00
|
|
|
def to_s
|
|
|
|
name
|
|
|
|
end
|
|
|
|
|
|
|
|
# Cast the provided value using the properties details.
|
|
|
|
def cast(parent, value)
|
|
|
|
return value unless casted
|
|
|
|
if type.is_a?(Array)
|
2010-06-18 01:24:49 +02:00
|
|
|
if value.nil?
|
|
|
|
value = []
|
|
|
|
elsif [Hash, HashWithIndifferentAccess].include?(value.class)
|
|
|
|
# Assume provided as a Hash where key is index!
|
|
|
|
data = value
|
|
|
|
value = [ ]
|
|
|
|
data.keys.sort.each do |k|
|
|
|
|
value << data[k]
|
|
|
|
end
|
2010-09-01 23:13:52 +02:00
|
|
|
elsif !value.is_a?(Array)
|
2010-06-18 01:24:49 +02:00
|
|
|
raise "Expecting an array or keyed hash for property #{parent.class.name}##{self.name}"
|
|
|
|
end
|
2010-06-16 21:04:53 +02:00
|
|
|
arr = value.collect { |data| cast_value(parent, data) }
|
|
|
|
# allow casted_by calls to be passed up chain by wrapping in CastedArray
|
2011-04-20 16:44:49 +02:00
|
|
|
CastedArray.new(arr, self, parent)
|
2011-02-28 16:00:41 +01:00
|
|
|
elsif (type == Object || type == Hash) && (value.class == Hash)
|
|
|
|
# allow casted_by calls to be passed up chain by wrapping in CastedHash
|
2011-04-20 16:44:49 +02:00
|
|
|
CastedHash[value, self, parent]
|
2010-06-16 21:04:53 +02:00
|
|
|
elsif !value.nil?
|
2011-04-20 16:44:49 +02:00
|
|
|
cast_value(parent, value)
|
2010-06-16 21:04:53 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Cast an individual value, not an array
|
|
|
|
def cast_value(parent, value)
|
|
|
|
raise "An array inside an array cannot be casted, use CastedModel" if value.is_a?(Array)
|
|
|
|
value = typecast_value(value, self)
|
|
|
|
associate_casted_value_to_parent(parent, value)
|
|
|
|
end
|
|
|
|
|
|
|
|
def default_value
|
|
|
|
return if default.nil?
|
|
|
|
if default.class == Proc
|
|
|
|
default.call
|
|
|
|
else
|
2010-12-22 17:09:42 +01:00
|
|
|
# TODO identify cause of mutex errors
|
|
|
|
Marshal.load(Marshal.dump(default))
|
2010-06-16 21:04:53 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-05-21 14:16:39 +02:00
|
|
|
# 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
|
|
|
|
|
2009-01-30 03:45:01 +01:00
|
|
|
private
|
2009-07-20 23:17:27 +02:00
|
|
|
|
2010-06-16 21:04:53 +02:00
|
|
|
def associate_casted_value_to_parent(parent, value)
|
|
|
|
value.casted_by = parent if value.respond_to?(:casted_by)
|
2011-04-20 16:44:49 +02:00
|
|
|
value.casted_by_property = self if value.respond_to?(:casted_by_property)
|
2010-06-16 21:04:53 +02:00
|
|
|
value
|
|
|
|
end
|
|
|
|
|
2009-02-13 05:28:07 +01:00
|
|
|
def parse_type(type)
|
|
|
|
if type.nil?
|
2010-06-16 21:04:53 +02:00
|
|
|
@casted = false
|
2010-09-01 23:13:52 +02:00
|
|
|
@type = nil
|
2010-06-20 22:01:11 +02:00
|
|
|
@type_class = nil
|
2009-02-13 05:28:07 +01:00
|
|
|
else
|
2010-06-20 22:01:11 +02:00
|
|
|
base = type.is_a?(Array) ? type.first : type
|
|
|
|
base = Object if base.nil?
|
2010-09-01 23:13:52 +02:00
|
|
|
raise "Defining a property type as a #{type.class.name.humanize} is not supported in CouchRest Model!" if base.class != Class
|
2010-06-20 22:01:11 +02:00
|
|
|
@type_class = base
|
2010-06-16 22:02:12 +02:00
|
|
|
@type = type
|
2009-02-13 05:28:07 +01:00
|
|
|
end
|
|
|
|
end
|
2009-07-20 23:17:27 +02:00
|
|
|
|
2009-01-30 03:45:01 +01:00
|
|
|
def parse_options(options)
|
2009-02-06 03:57:11 +01:00
|
|
|
@validation_format = options.delete(:format) if options[:format]
|
|
|
|
@read_only = options.delete(:read_only) if options[:read_only]
|
|
|
|
@alias = options.delete(:alias) if options[:alias]
|
2009-04-26 04:31:19 +02:00
|
|
|
@default = options.delete(:default) unless options[:default].nil?
|
2009-10-24 03:42:48 +02:00
|
|
|
@init_method = options[:init_method] ? options.delete(:init_method) : 'new'
|
2009-02-06 02:06:12 +01:00
|
|
|
@options = options
|
2009-01-30 03:45:01 +01:00
|
|
|
end
|
2009-07-20 23:17:27 +02:00
|
|
|
|
2009-01-30 03:45:01 +01:00
|
|
|
end
|
2009-04-26 04:31:19 +02:00
|
|
|
end
|