Merge remote branch 'samlown/master' into official-master
This commit is contained in:
commit
ef6739774b
|
@ -20,7 +20,7 @@ module CouchRest
|
||||||
class_eval <<-END, __FILE__, __LINE__ + 1
|
class_eval <<-END, __FILE__, __LINE__ + 1
|
||||||
def self.find_all_#{collection_name}(options = {})
|
def self.find_all_#{collection_name}(options = {})
|
||||||
view_options = #{view_options.inspect} || {}
|
view_options = #{view_options.inspect} || {}
|
||||||
CollectionProxy.new(@database, "#{design_doc}", "#{view_name}", view_options.merge(options), Kernel.const_get('#{self}'))
|
CollectionProxy.new(database, "#{design_doc}", "#{view_name}", view_options.merge(options), Kernel.const_get('#{self}'))
|
||||||
end
|
end
|
||||||
END
|
END
|
||||||
end
|
end
|
||||||
|
@ -58,7 +58,7 @@ module CouchRest
|
||||||
|
|
||||||
def create_collection_proxy(options)
|
def create_collection_proxy(options)
|
||||||
design_doc, view_name, view_options = parse_view_options(options)
|
design_doc, view_name, view_options = parse_view_options(options)
|
||||||
CollectionProxy.new(@database, design_doc, view_name, view_options, self)
|
CollectionProxy.new(database, design_doc, view_name, view_options, self)
|
||||||
end
|
end
|
||||||
|
|
||||||
def parse_view_options(options)
|
def parse_view_options(options)
|
||||||
|
|
|
@ -1,24 +1,6 @@
|
||||||
require 'time'
|
require 'time'
|
||||||
require File.join(File.dirname(__FILE__), '..', 'more', 'property')
|
require File.join(File.dirname(__FILE__), '..', 'more', 'property')
|
||||||
|
require File.join(File.dirname(__FILE__), '..', 'more', 'typecast')
|
||||||
class Time
|
|
||||||
# returns a local time value much faster than Time.parse
|
|
||||||
def self.mktime_with_offset(string)
|
|
||||||
string =~ /(\d{4})[\-|\/](\d{2})[\-|\/](\d{2})[T|\s](\d{2}):(\d{2}):(\d{2})([\+|\s|\-])*(\d{2}):?(\d{2})/
|
|
||||||
# $1 = year
|
|
||||||
# $2 = month
|
|
||||||
# $3 = day
|
|
||||||
# $4 = hours
|
|
||||||
# $5 = minutes
|
|
||||||
# $6 = seconds
|
|
||||||
# $7 = time zone direction
|
|
||||||
# $8 = tz difference
|
|
||||||
# utc time with wrong TZ info:
|
|
||||||
time = mktime($1, RFC2822_MONTH_NAME[$2.to_i - 1], $3, $4, $5, $6, $7)
|
|
||||||
tz_difference = ("#{$7 == '-' ? '+' : '-'}#{$8}".to_i * 3600)
|
|
||||||
time + tz_difference + zone_offset(time.zone)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
module CouchRest
|
module CouchRest
|
||||||
module Mixins
|
module Mixins
|
||||||
|
@ -26,6 +8,8 @@ module CouchRest
|
||||||
|
|
||||||
class IncludeError < StandardError; end
|
class IncludeError < StandardError; end
|
||||||
|
|
||||||
|
include ::CouchRest::More::Typecast
|
||||||
|
|
||||||
def self.included(base)
|
def self.included(base)
|
||||||
base.class_eval <<-EOS, __FILE__, __LINE__ + 1
|
base.class_eval <<-EOS, __FILE__, __LINE__ + 1
|
||||||
extlib_inheritable_accessor(:properties) unless self.respond_to?(:properties)
|
extlib_inheritable_accessor(:properties) unless self.respond_to?(:properties)
|
||||||
|
@ -67,28 +51,24 @@ module CouchRest
|
||||||
return unless self[key]
|
return unless self[key]
|
||||||
if property.type.is_a?(Array)
|
if property.type.is_a?(Array)
|
||||||
klass = ::CouchRest.constantize(property.type[0])
|
klass = ::CouchRest.constantize(property.type[0])
|
||||||
arr = self[key].dup.collect do |value|
|
self[key] = [self[key]] unless self[key].is_a?(Array)
|
||||||
unless value.instance_of?(klass)
|
arr = self[key].collect do |value|
|
||||||
value = convert_property_value(property, klass, value)
|
value = typecast_value(value, klass, property.init_method)
|
||||||
end
|
|
||||||
associate_casted_to_parent(value, assigned)
|
associate_casted_to_parent(value, assigned)
|
||||||
value
|
value
|
||||||
end
|
end
|
||||||
self[key] = klass != String ? CastedArray.new(arr) : arr
|
self[key] = klass != String ? CastedArray.new(arr) : arr
|
||||||
self[key].casted_by = self if self[key].respond_to?(:casted_by)
|
self[key].casted_by = self if self[key].respond_to?(:casted_by)
|
||||||
else
|
else
|
||||||
if property.type == 'boolean'
|
if property.type.downcase == 'boolean'
|
||||||
klass = TrueClass
|
klass = TrueClass
|
||||||
else
|
else
|
||||||
klass = ::CouchRest.constantize(property.type)
|
klass = ::CouchRest.constantize(property.type)
|
||||||
end
|
end
|
||||||
|
|
||||||
unless self[key].instance_of?(klass)
|
self[key] = typecast_value(self[key], klass, property.init_method)
|
||||||
self[key] = convert_property_value(property, klass, self[property.name])
|
associate_casted_to_parent(self[key], assigned)
|
||||||
end
|
|
||||||
associate_casted_to_parent(self[property.name], assigned)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def associate_casted_to_parent(casted, assigned)
|
def associate_casted_to_parent(casted, assigned)
|
||||||
|
@ -96,21 +76,6 @@ module CouchRest
|
||||||
casted.document_saved = true if !assigned && casted.respond_to?(:document_saved)
|
casted.document_saved = true if !assigned && casted.respond_to?(:document_saved)
|
||||||
end
|
end
|
||||||
|
|
||||||
def convert_property_value(property, klass, value)
|
|
||||||
if ((property.init_method == 'new') && klass == Time)
|
|
||||||
# Using custom time parsing method because Ruby's default method is toooo slow
|
|
||||||
value.is_a?(String) ? Time.mktime_with_offset(value.dup) : value
|
|
||||||
# Float instances don't get initialized with #new
|
|
||||||
elsif ((property.init_method == 'new') && klass == Float)
|
|
||||||
cast_float(value)
|
|
||||||
# 'boolean' type is simply used to generate a property? accessor method
|
|
||||||
elsif ((property.init_method == 'new') && klass == TrueClass)
|
|
||||||
value
|
|
||||||
else
|
|
||||||
klass.send(property.init_method, value.dup)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def cast_property_by_name(property_name)
|
def cast_property_by_name(property_name)
|
||||||
return unless self.class.properties
|
return unless self.class.properties
|
||||||
property = self.class.properties.detect{|property| property.name == property_name}
|
property = self.class.properties.detect{|property| property.name == property_name}
|
||||||
|
@ -118,14 +83,7 @@ module CouchRest
|
||||||
cast_property(property, true)
|
cast_property(property, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
def cast_float(value)
|
|
||||||
begin
|
|
||||||
Float(value)
|
|
||||||
rescue
|
|
||||||
value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
module ClassMethods
|
module ClassMethods
|
||||||
|
|
||||||
def property(name, options={})
|
def property(name, options={})
|
||||||
|
@ -141,7 +99,7 @@ module CouchRest
|
||||||
# make sure to use a mutex.
|
# make sure to use a mutex.
|
||||||
def define_property(name, options={})
|
def define_property(name, options={})
|
||||||
# check if this property is going to casted
|
# check if this property is going to casted
|
||||||
options[:casted] = options[:cast_as] ? options[:cast_as] : false
|
options[:casted] = !!(options[:cast_as] || options[:type])
|
||||||
property = CouchRest::Property.new(name, (options.delete(:cast_as) || options.delete(:type)), options)
|
property = CouchRest::Property.new(name, (options.delete(:cast_as) || options.delete(:type)), options)
|
||||||
create_property_getter(property)
|
create_property_getter(property)
|
||||||
create_property_setter(property) unless property.read_only == true
|
create_property_setter(property) unless property.read_only == true
|
||||||
|
|
|
@ -14,7 +14,7 @@ module CouchRest
|
||||||
include CouchRest::Mixins::ExtendedAttachments
|
include CouchRest::Mixins::ExtendedAttachments
|
||||||
include CouchRest::Mixins::ClassProxy
|
include CouchRest::Mixins::ClassProxy
|
||||||
include CouchRest::Mixins::Collection
|
include CouchRest::Mixins::Collection
|
||||||
include CouchRest::Mixins::AttributeProtection
|
include CouchRest::Mixins::AttributeProtection
|
||||||
|
|
||||||
def self.subclasses
|
def self.subclasses
|
||||||
@subclasses ||= []
|
@subclasses ||= []
|
||||||
|
@ -58,6 +58,7 @@ module CouchRest
|
||||||
unless self['_id'] && self['_rev']
|
unless self['_id'] && self['_rev']
|
||||||
self['couchrest-type'] = self.class.to_s
|
self['couchrest-type'] = self.class.to_s
|
||||||
end
|
end
|
||||||
|
after_initialize if respond_to?(:after_initialize)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Defines an instance and save it directly to the database
|
# Defines an instance and save it directly to the database
|
||||||
|
@ -84,9 +85,9 @@ module CouchRest
|
||||||
# on the document whenever saving occurs. CouchRest uses a pretty
|
# on the document whenever saving occurs. CouchRest uses a pretty
|
||||||
# decent time format by default. See Time#to_json
|
# decent time format by default. See Time#to_json
|
||||||
def self.timestamps!
|
def self.timestamps!
|
||||||
class_eval <<-EOS, __FILE__, __LINE__ + 1
|
class_eval <<-EOS, __FILE__, __LINE__
|
||||||
property(:updated_at, :read_only => true, :cast_as => 'Time', :auto_validation => false)
|
property(:updated_at, :read_only => true, :type => 'Time', :auto_validation => false)
|
||||||
property(:created_at, :read_only => true, :cast_as => 'Time', :auto_validation => false)
|
property(:created_at, :read_only => true, :type => 'Time', :auto_validation => false)
|
||||||
|
|
||||||
set_callback :save, :before do |object|
|
set_callback :save, :before do |object|
|
||||||
object['updated_at'] = Time.now
|
object['updated_at'] = Time.now
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
module CouchRest
|
module CouchRest
|
||||||
|
|
||||||
# Basic attribute support for adding getter/setter + validation
|
# Basic attribute support for adding getter/setter + validation
|
||||||
class Property
|
class Property
|
||||||
attr_reader :name, :type, :read_only, :alias, :default, :casted, :init_method, :options
|
attr_reader :name, :type, :read_only, :alias, :default, :casted, :init_method, :options
|
||||||
|
|
||||||
# attribute to define
|
# attribute to define
|
||||||
def initialize(name, type = nil, options = {})
|
def initialize(name, type = nil, options = {})
|
||||||
@name = name.to_s
|
@name = name.to_s
|
||||||
|
@ -11,20 +11,19 @@ module CouchRest
|
||||||
parse_options(options)
|
parse_options(options)
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def parse_type(type)
|
def parse_type(type)
|
||||||
if type.nil?
|
if type.nil?
|
||||||
@type = 'String'
|
@type = 'String'
|
||||||
elsif type.is_a?(Array) && type.empty?
|
elsif type.is_a?(Array) && type.empty?
|
||||||
@type = 'Array'
|
@type = ['Object']
|
||||||
else
|
else
|
||||||
@type = type.is_a?(Array) ? [type.first.to_s] : type.to_s
|
@type = type.is_a?(Array) ? [type.first.to_s] : type.to_s
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def parse_options(options)
|
def parse_options(options)
|
||||||
return if options.empty?
|
return if options.empty?
|
||||||
@validation_format = options.delete(:format) if options[:format]
|
@validation_format = options.delete(:format) if options[:format]
|
||||||
|
@ -35,7 +34,7 @@ module CouchRest
|
||||||
@init_method = options[:init_method] ? options.delete(:init_method) : 'new'
|
@init_method = options[:init_method] ? options.delete(:init_method) : 'new'
|
||||||
@options = options
|
@options = options
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -56,4 +55,4 @@ class CastedArray < Array
|
||||||
obj.casted_by = self.casted_by if obj.respond_to?(:casted_by)
|
obj.casted_by = self.casted_by if obj.respond_to?(:casted_by)
|
||||||
super(index, obj)
|
super(index, obj)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
180
lib/couchrest/more/typecast.rb
Normal file
180
lib/couchrest/more/typecast.rb
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
require 'time'
|
||||||
|
require 'bigdecimal'
|
||||||
|
require 'bigdecimal/util'
|
||||||
|
require File.join(File.dirname(__FILE__), '..', 'more', 'property')
|
||||||
|
|
||||||
|
class Time
|
||||||
|
# returns a local time value much faster than Time.parse
|
||||||
|
def self.mktime_with_offset(string)
|
||||||
|
string =~ /(\d{4})[\-|\/](\d{2})[\-|\/](\d{2})[T|\s](\d{2}):(\d{2}):(\d{2})([\+|\s|\-])*(\d{2}):?(\d{2})/
|
||||||
|
# $1 = year
|
||||||
|
# $2 = month
|
||||||
|
# $3 = day
|
||||||
|
# $4 = hours
|
||||||
|
# $5 = minutes
|
||||||
|
# $6 = seconds
|
||||||
|
# $7 = time zone direction
|
||||||
|
# $8 = tz difference
|
||||||
|
# utc time with wrong TZ info:
|
||||||
|
time = mktime($1, RFC2822_MONTH_NAME[$2.to_i - 1], $3, $4, $5, $6, $7)
|
||||||
|
tz_difference = ("#{$7 == '-' ? '+' : '-'}#{$8}".to_i * 3600)
|
||||||
|
time + tz_difference + zone_offset(time.zone)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module CouchRest
|
||||||
|
module More
|
||||||
|
module Typecast
|
||||||
|
|
||||||
|
def typecast_value(value, klass, init_method)
|
||||||
|
return nil if value.nil?
|
||||||
|
|
||||||
|
if value.instance_of?(klass) || klass.to_s == 'Object'
|
||||||
|
value
|
||||||
|
elsif ['String', 'TrueClass', 'Integer', 'Float', 'BigDecimal', 'DateTime', 'Time', 'Date', 'Class'].include?(klass.to_s)
|
||||||
|
send('typecast_to_'+klass.to_s.downcase, value)
|
||||||
|
else
|
||||||
|
# Allow the init_method to be defined as a Proc for advanced conversion
|
||||||
|
init_method.is_a?(Proc) ? init_method.call(value) : klass.send(init_method, value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
# Typecast a value to an Integer
|
||||||
|
def typecast_to_integer(value)
|
||||||
|
value.kind_of?(Integer) ? value : typecast_to_numeric(value, :to_i)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Typecast a value to a String
|
||||||
|
def typecast_to_string(value)
|
||||||
|
value.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
# Typecast a value to a true or false
|
||||||
|
def typecast_to_trueclass(value)
|
||||||
|
if value.kind_of?(Integer)
|
||||||
|
return true if value == 1
|
||||||
|
return false if value == 0
|
||||||
|
elsif value.respond_to?(:to_s)
|
||||||
|
return true if %w[ true 1 t ].include?(value.to_s.downcase)
|
||||||
|
return false if %w[ false 0 f ].include?(value.to_s.downcase)
|
||||||
|
end
|
||||||
|
value
|
||||||
|
end
|
||||||
|
|
||||||
|
# Typecast a value to a BigDecimal
|
||||||
|
def typecast_to_bigdecimal(value)
|
||||||
|
return value if value.kind_of?(BigDecimal)
|
||||||
|
|
||||||
|
if value.kind_of?(Integer)
|
||||||
|
value.to_s.to_d
|
||||||
|
else
|
||||||
|
typecast_to_numeric(value, :to_d)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Typecast a value to a Float
|
||||||
|
def typecast_to_float(value)
|
||||||
|
return value if value.kind_of?(Float)
|
||||||
|
typecast_to_numeric(value, :to_f)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Match numeric string
|
||||||
|
def typecast_to_numeric(value, method)
|
||||||
|
if value.respond_to?(:to_str)
|
||||||
|
if value.to_str =~ /\A(-?(?:0|[1-9]\d*)(?:\.\d+)?|(?:\.\d+))\z/
|
||||||
|
$1.send(method)
|
||||||
|
else
|
||||||
|
value
|
||||||
|
end
|
||||||
|
elsif value.respond_to?(method)
|
||||||
|
value.send(method)
|
||||||
|
else
|
||||||
|
value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Typecasts an arbitrary value to a DateTime.
|
||||||
|
# Handles both Hashes and DateTime instances.
|
||||||
|
def typecast_to_datetime(value)
|
||||||
|
return value if value.kind_of?(DateTime)
|
||||||
|
|
||||||
|
if value.is_a?(Hash)
|
||||||
|
typecast_hash_to_datetime(value)
|
||||||
|
else
|
||||||
|
DateTime.parse(value.to_s)
|
||||||
|
end
|
||||||
|
rescue ArgumentError
|
||||||
|
value
|
||||||
|
end
|
||||||
|
|
||||||
|
# Typecasts an arbitrary value to a Date
|
||||||
|
# Handles both Hashes and Date instances.
|
||||||
|
def typecast_to_date(value)
|
||||||
|
return value if value.kind_of?(Date)
|
||||||
|
|
||||||
|
if value.is_a?(Hash)
|
||||||
|
typecast_hash_to_date(value)
|
||||||
|
else
|
||||||
|
Date.parse(value.to_s)
|
||||||
|
end
|
||||||
|
rescue ArgumentError
|
||||||
|
value
|
||||||
|
end
|
||||||
|
|
||||||
|
# Typecasts an arbitrary value to a Time
|
||||||
|
# Handles both Hashes and Time instances.
|
||||||
|
def typecast_to_time(value)
|
||||||
|
return value if value.kind_of?(Time)
|
||||||
|
|
||||||
|
if value.is_a?(Hash)
|
||||||
|
typecast_hash_to_time(value)
|
||||||
|
else
|
||||||
|
Time.mktime_with_offset(value.to_s)
|
||||||
|
end
|
||||||
|
rescue ArgumentError
|
||||||
|
value
|
||||||
|
rescue TypeError
|
||||||
|
value
|
||||||
|
end
|
||||||
|
|
||||||
|
# Creates a DateTime instance from a Hash with keys :year, :month, :day,
|
||||||
|
# :hour, :min, :sec
|
||||||
|
def typecast_hash_to_datetime(value)
|
||||||
|
DateTime.new(*extract_time(value))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Creates a Date instance from a Hash with keys :year, :month, :day
|
||||||
|
def typecast_hash_to_date(value)
|
||||||
|
Date.new(*extract_time(value)[0, 3])
|
||||||
|
end
|
||||||
|
|
||||||
|
# Creates a Time instance from a Hash with keys :year, :month, :day,
|
||||||
|
# :hour, :min, :sec
|
||||||
|
def typecast_hash_to_time(value)
|
||||||
|
Time.local(*extract_time(value))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Extracts the given args from the hash. If a value does not exist, it
|
||||||
|
# uses the value of Time.now.
|
||||||
|
def extract_time(value)
|
||||||
|
now = Time.now
|
||||||
|
|
||||||
|
[:year, :month, :day, :hour, :min, :sec].map do |segment|
|
||||||
|
typecast_to_numeric(value.fetch(segment, now.send(segment)), :to_i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Typecast a value to a Class
|
||||||
|
def typecast_to_class(value)
|
||||||
|
return value if value.kind_of?(Class)
|
||||||
|
::CouchRest.constantize(value.to_s)
|
||||||
|
rescue NameError
|
||||||
|
value
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -37,7 +37,7 @@ module CouchRest
|
||||||
|
|
||||||
def call(target)
|
def call(target)
|
||||||
value = target.validation_property_value(field_name)
|
value = target.validation_property_value(field_name)
|
||||||
property = target.validation_property(field_name)
|
property = target.validation_property(field_name.to_s)
|
||||||
return true if present?(value, property)
|
return true if present?(value, property)
|
||||||
|
|
||||||
error_message = @options[:message] || default_error(property)
|
error_message = @options[:message] || default_error(property)
|
||||||
|
@ -66,7 +66,7 @@ module CouchRest
|
||||||
# Returns false for other property types.
|
# Returns false for other property types.
|
||||||
# Returns false for non-properties.
|
# Returns false for non-properties.
|
||||||
def boolean_type?(property)
|
def boolean_type?(property)
|
||||||
property ? property.type == TrueClass : false
|
property ? property.type == 'Boolean' : false
|
||||||
end
|
end
|
||||||
|
|
||||||
end # class RequiredFieldValidator
|
end # class RequiredFieldValidator
|
||||||
|
|
|
@ -12,7 +12,7 @@ class WithCastedModelMixin < Hash
|
||||||
include CouchRest::CastedModel
|
include CouchRest::CastedModel
|
||||||
property :name
|
property :name
|
||||||
property :no_value
|
property :no_value
|
||||||
property :details, :default => {}
|
property :details, :type => 'Object', :default => {}
|
||||||
property :casted_attribute, :cast_as => 'WithCastedModelMixin'
|
property :casted_attribute, :cast_as => 'WithCastedModelMixin'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,11 @@ describe "ExtendedDocument" do
|
||||||
|
|
||||||
class WithDefaultValues < CouchRest::ExtendedDocument
|
class WithDefaultValues < CouchRest::ExtendedDocument
|
||||||
use_database TEST_SERVER.default_database
|
use_database TEST_SERVER.default_database
|
||||||
property :preset, :default => {:right => 10, :top_align => false}
|
property :preset, :type => 'Object', :default => {:right => 10, :top_align => false}
|
||||||
property :set_by_proc, :default => Proc.new{Time.now}, :cast_as => 'Time'
|
property :set_by_proc, :default => Proc.new{Time.now}, :cast_as => 'Time'
|
||||||
property :tags, :default => []
|
property :tags, :type => ['String'], :default => []
|
||||||
property :read_only_with_default, :default => 'generic', :read_only => true
|
property :read_only_with_default, :default => 'generic', :read_only => true
|
||||||
property :default_false, :default => false
|
property :default_false, :type => 'Boolean', :default => false
|
||||||
property :name
|
property :name
|
||||||
timestamps!
|
timestamps!
|
||||||
end
|
end
|
||||||
|
@ -104,6 +104,18 @@ describe "ExtendedDocument" do
|
||||||
self.other_arg = "foo-#{value}"
|
self.other_arg = "foo-#{value}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class WithAfterInitializeMethod < CouchRest::ExtendedDocument
|
||||||
|
use_database TEST_SERVER.default_database
|
||||||
|
|
||||||
|
property :some_value
|
||||||
|
|
||||||
|
def after_initialize
|
||||||
|
self.some_value ||= "value"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
before(:each) do
|
before(:each) do
|
||||||
@obj = WithDefaultValues.new
|
@obj = WithDefaultValues.new
|
||||||
|
@ -383,7 +395,7 @@ describe "ExtendedDocument" do
|
||||||
"professor" => {
|
"professor" => {
|
||||||
"name" => ["Mark", "Hinchliff"]
|
"name" => ["Mark", "Hinchliff"]
|
||||||
},
|
},
|
||||||
"final_test_at" => "2008/12/19 13:00:00 +0800"
|
"ends_at" => "2008/12/19 13:00:00 +0800"
|
||||||
}
|
}
|
||||||
r = Course.database.save_doc course_doc
|
r = Course.database.save_doc course_doc
|
||||||
@course = Course.get r['id']
|
@course = Course.get r['id']
|
||||||
|
@ -394,8 +406,8 @@ describe "ExtendedDocument" do
|
||||||
it "should instantiate the professor as a person" do
|
it "should instantiate the professor as a person" do
|
||||||
@course['professor'].last_name.should == "Hinchliff"
|
@course['professor'].last_name.should == "Hinchliff"
|
||||||
end
|
end
|
||||||
it "should instantiate the final_test_at as a Time" do
|
it "should instantiate the ends_at as a Time" do
|
||||||
@course['final_test_at'].should == Time.parse("2008/12/19 13:00:00 +0800")
|
@course['ends_at'].should == Time.parse("2008/12/19 13:00:00 +0800")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -698,6 +710,13 @@ describe "ExtendedDocument" do
|
||||||
@doc.other_arg.should == "foo-foo"
|
@doc.other_arg.should == "foo-foo"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "initialization" do
|
||||||
|
it "should call after_initialize method if available" do
|
||||||
|
@doc = WithAfterInitializeMethod.new
|
||||||
|
@doc['some_value'].should eql('value')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "recursive validation on an extended document" do
|
describe "recursive validation on an extended document" do
|
||||||
before :each do
|
before :each do
|
||||||
|
|
|
@ -7,6 +7,7 @@ require File.join(FIXTURE_PATH, 'more', 'service')
|
||||||
require File.join(FIXTURE_PATH, 'more', 'event')
|
require File.join(FIXTURE_PATH, 'more', 'event')
|
||||||
require File.join(FIXTURE_PATH, 'more', 'cat')
|
require File.join(FIXTURE_PATH, 'more', 'cat')
|
||||||
require File.join(FIXTURE_PATH, 'more', 'user')
|
require File.join(FIXTURE_PATH, 'more', 'user')
|
||||||
|
require File.join(FIXTURE_PATH, 'more', 'course')
|
||||||
|
|
||||||
|
|
||||||
describe "ExtendedDocument properties" do
|
describe "ExtendedDocument properties" do
|
||||||
|
@ -42,11 +43,11 @@ describe "ExtendedDocument properties" do
|
||||||
|
|
||||||
it "should let you use an alias for a casted attribute" do
|
it "should let you use an alias for a casted attribute" do
|
||||||
@card.cast_alias = Person.new(:name => "Aimonetti")
|
@card.cast_alias = Person.new(:name => "Aimonetti")
|
||||||
@card.cast_alias.name.should == "Aimonetti"
|
@card.cast_alias.name.should == ["Aimonetti"]
|
||||||
@card.calias.name.should == "Aimonetti"
|
@card.calias.name.should == ["Aimonetti"]
|
||||||
card = Card.new(:first_name => "matt", :cast_alias => {:name => "Aimonetti"})
|
card = Card.new(:first_name => "matt", :cast_alias => {:name => "Aimonetti"})
|
||||||
card.cast_alias.name.should == "Aimonetti"
|
card.cast_alias.name.should == ["Aimonetti"]
|
||||||
card.calias.name.should == "Aimonetti"
|
card.calias.name.should == ["Aimonetti"]
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should be auto timestamped" do
|
it "should be auto timestamped" do
|
||||||
|
@ -155,111 +156,449 @@ describe "ExtendedDocument properties" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "casting" do
|
describe "casting" do
|
||||||
describe "cast keys to any type" do
|
before(:each) do
|
||||||
before(:all) do
|
@course = Course.new(:title => 'Relaxation')
|
||||||
event_doc = { :subject => "Some event", :occurs_at => Time.now, :end_date => Date.today }
|
end
|
||||||
e = Event.database.save_doc event_doc
|
|
||||||
|
|
||||||
@event = Event.get e['id']
|
describe "when value is nil" do
|
||||||
|
it "leaves the value unchanged" do
|
||||||
|
@course.title = nil
|
||||||
|
@course['title'].should == nil
|
||||||
end
|
end
|
||||||
it "should cast occurs_at to Time" do
|
end
|
||||||
@event['occurs_at'].should be_an_instance_of(Time)
|
|
||||||
|
describe "when type primitive is an Object" do
|
||||||
|
it "it should not cast given value" do
|
||||||
|
@course.participants = [{}, 'q', 1]
|
||||||
|
@course['participants'].should eql([{}, 'q', 1])
|
||||||
end
|
end
|
||||||
it "should cast end_date to Date" do
|
|
||||||
@event['end_date'].should be_an_instance_of(Date)
|
it "should cast started_on to Date" do
|
||||||
|
@course.started_on = Date.today
|
||||||
|
@course['started_on'].should be_an_instance_of(Date)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "casting to Float object" do
|
describe "when type primitive is a String" do
|
||||||
class RootBeerFloat < CouchRest::ExtendedDocument
|
it "keeps string value unchanged" do
|
||||||
use_database DB
|
value = "1.0"
|
||||||
property :price, :cast_as => 'Float'
|
@course.title = value
|
||||||
|
@course['title'].should equal(value)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should convert a string into a float if casted as so" do
|
it "it casts to string representation of the value" do
|
||||||
RootBeerFloat.new(:price => '12.50').price.should == 12.50
|
@course.title = 1.0
|
||||||
RootBeerFloat.new(:price => '9').price.should == 9.0
|
@course['title'].should eql("1.0")
|
||||||
RootBeerFloat.new(:price => '-9').price.should == -9.0
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
it "should not convert a string if it's not a string that can be cast as a float" do
|
|
||||||
RootBeerFloat.new(:price => 'test').price.should == 'test'
|
describe 'when type primitive is a Float' do
|
||||||
|
it 'returns same value if a float' do
|
||||||
|
value = 24.0
|
||||||
|
@course.estimate = value
|
||||||
|
@course['estimate'].should equal(value)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should work fine when a float is being passed" do
|
it 'returns float representation of a zero string integer' do
|
||||||
RootBeerFloat.new(:price => 9.99).price.should == 9.99
|
@course.estimate = '0'
|
||||||
|
@course['estimate'].should eql(0.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns float representation of a positive string integer' do
|
||||||
|
@course.estimate = '24'
|
||||||
|
@course['estimate'].should eql(24.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns float representation of a negative string integer' do
|
||||||
|
@course.estimate = '-24'
|
||||||
|
@course['estimate'].should eql(-24.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns float representation of a zero string float' do
|
||||||
|
@course.estimate = '0.0'
|
||||||
|
@course['estimate'].should eql(0.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns float representation of a positive string float' do
|
||||||
|
@course.estimate = '24.35'
|
||||||
|
@course['estimate'].should eql(24.35)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns float representation of a negative string float' do
|
||||||
|
@course.estimate = '-24.35'
|
||||||
|
@course['estimate'].should eql(-24.35)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns float representation of a zero string float, with no leading digits' do
|
||||||
|
@course.estimate = '.0'
|
||||||
|
@course['estimate'].should eql(0.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns float representation of a positive string float, with no leading digits' do
|
||||||
|
@course.estimate = '.41'
|
||||||
|
@course['estimate'].should eql(0.41)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns float representation of a zero integer' do
|
||||||
|
@course.estimate = 0
|
||||||
|
@course['estimate'].should eql(0.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns float representation of a positive integer' do
|
||||||
|
@course.estimate = 24
|
||||||
|
@course['estimate'].should eql(24.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns float representation of a negative integer' do
|
||||||
|
@course.estimate = -24
|
||||||
|
@course['estimate'].should eql(-24.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns float representation of a zero decimal' do
|
||||||
|
@course.estimate = BigDecimal('0.0')
|
||||||
|
@course['estimate'].should eql(0.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns float representation of a positive decimal' do
|
||||||
|
@course.estimate = BigDecimal('24.35')
|
||||||
|
@course['estimate'].should eql(24.35)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns float representation of a negative decimal' do
|
||||||
|
@course.estimate = BigDecimal('-24.35')
|
||||||
|
@course['estimate'].should eql(-24.35)
|
||||||
|
end
|
||||||
|
|
||||||
|
[ Object.new, true, '00.0', '0.', '-.0', 'string' ].each do |value|
|
||||||
|
it "does not typecast non-numeric value #{value.inspect}" do
|
||||||
|
@course.estimate = value
|
||||||
|
@course['estimate'].should equal(value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'when type primitive is a Integer' do
|
||||||
|
it 'returns same value if an integer' do
|
||||||
|
value = 24
|
||||||
|
@course.hours = value
|
||||||
|
@course['hours'].should equal(value)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns integer representation of a zero string integer' do
|
||||||
|
@course.hours = '0'
|
||||||
|
@course['hours'].should eql(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns integer representation of a positive string integer' do
|
||||||
|
@course.hours = '24'
|
||||||
|
@course['hours'].should eql(24)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns integer representation of a negative string integer' do
|
||||||
|
@course.hours = '-24'
|
||||||
|
@course['hours'].should eql(-24)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns integer representation of a zero string float' do
|
||||||
|
@course.hours = '0.0'
|
||||||
|
@course['hours'].should eql(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns integer representation of a positive string float' do
|
||||||
|
@course.hours = '24.35'
|
||||||
|
@course['hours'].should eql(24)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns integer representation of a negative string float' do
|
||||||
|
@course.hours = '-24.35'
|
||||||
|
@course['hours'].should eql(-24)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns integer representation of a zero string float, with no leading digits' do
|
||||||
|
@course.hours = '.0'
|
||||||
|
@course['hours'].should eql(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns integer representation of a positive string float, with no leading digits' do
|
||||||
|
@course.hours = '.41'
|
||||||
|
@course['hours'].should eql(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns integer representation of a zero float' do
|
||||||
|
@course.hours = 0.0
|
||||||
|
@course['hours'].should eql(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns integer representation of a positive float' do
|
||||||
|
@course.hours = 24.35
|
||||||
|
@course['hours'].should eql(24)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns integer representation of a negative float' do
|
||||||
|
@course.hours = -24.35
|
||||||
|
@course['hours'].should eql(-24)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns integer representation of a zero decimal' do
|
||||||
|
@course.hours = '0.0'
|
||||||
|
@course['hours'].should eql(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns integer representation of a positive decimal' do
|
||||||
|
@course.hours = '24.35'
|
||||||
|
@course['hours'].should eql(24)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns integer representation of a negative decimal' do
|
||||||
|
@course.hours = '-24.35'
|
||||||
|
@course['hours'].should eql(-24)
|
||||||
|
end
|
||||||
|
|
||||||
|
[ Object.new, true, '00.0', '0.', '-.0', 'string' ].each do |value|
|
||||||
|
it "does not typecast non-numeric value #{value.inspect}" do
|
||||||
|
@course.hours = value
|
||||||
|
@course['hours'].should equal(value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'when type primitive is a BigDecimal' do
|
||||||
|
it 'returns same value if a decimal' do
|
||||||
|
value = BigDecimal('24.0')
|
||||||
|
@course.profit = value
|
||||||
|
@course['profit'].should equal(value)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns decimal representation of a zero string integer' do
|
||||||
|
@course.profit = '0'
|
||||||
|
@course['profit'].should eql(BigDecimal('0.0'))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns decimal representation of a positive string integer' do
|
||||||
|
@course.profit = '24'
|
||||||
|
@course['profit'].should eql(BigDecimal('24.0'))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns decimal representation of a negative string integer' do
|
||||||
|
@course.profit = '-24'
|
||||||
|
@course['profit'].should eql(BigDecimal('-24.0'))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns decimal representation of a zero string float' do
|
||||||
|
@course.profit = '0.0'
|
||||||
|
@course['profit'].should eql(BigDecimal('0.0'))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns decimal representation of a positive string float' do
|
||||||
|
@course.profit = '24.35'
|
||||||
|
@course['profit'].should eql(BigDecimal('24.35'))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns decimal representation of a negative string float' do
|
||||||
|
@course.profit = '-24.35'
|
||||||
|
@course['profit'].should eql(BigDecimal('-24.35'))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns decimal representation of a zero string float, with no leading digits' do
|
||||||
|
@course.profit = '.0'
|
||||||
|
@course['profit'].should eql(BigDecimal('0.0'))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns decimal representation of a positive string float, with no leading digits' do
|
||||||
|
@course.profit = '.41'
|
||||||
|
@course['profit'].should eql(BigDecimal('0.41'))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns decimal representation of a zero integer' do
|
||||||
|
@course.profit = 0
|
||||||
|
@course['profit'].should eql(BigDecimal('0.0'))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns decimal representation of a positive integer' do
|
||||||
|
@course.profit = 24
|
||||||
|
@course['profit'].should eql(BigDecimal('24.0'))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns decimal representation of a negative integer' do
|
||||||
|
@course.profit = -24
|
||||||
|
@course['profit'].should eql(BigDecimal('-24.0'))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns decimal representation of a zero float' do
|
||||||
|
@course.profit = 0.0
|
||||||
|
@course['profit'].should eql(BigDecimal('0.0'))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns decimal representation of a positive float' do
|
||||||
|
@course.profit = 24.35
|
||||||
|
@course['profit'].should eql(BigDecimal('24.35'))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns decimal representation of a negative float' do
|
||||||
|
@course.profit = -24.35
|
||||||
|
@course['profit'].should eql(BigDecimal('-24.35'))
|
||||||
|
end
|
||||||
|
|
||||||
|
[ Object.new, true, '00.0', '0.', '-.0', 'string' ].each do |value|
|
||||||
|
it "does not typecast non-numeric value #{value.inspect}" do
|
||||||
|
@course.profit = value
|
||||||
|
@course['profit'].should equal(value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'when type primitive is a DateTime' do
|
||||||
|
describe 'and value given as a hash with keys like :year, :month, etc' do
|
||||||
|
it 'builds a DateTime instance from hash values' do
|
||||||
|
@course.updated_at = {
|
||||||
|
:year => '2006',
|
||||||
|
:month => '11',
|
||||||
|
:day => '23',
|
||||||
|
:hour => '12',
|
||||||
|
:min => '0',
|
||||||
|
:sec => '0'
|
||||||
|
}
|
||||||
|
result = @course['updated_at']
|
||||||
|
|
||||||
|
result.should be_kind_of(DateTime)
|
||||||
|
result.year.should eql(2006)
|
||||||
|
result.month.should eql(11)
|
||||||
|
result.day.should eql(23)
|
||||||
|
result.hour.should eql(12)
|
||||||
|
result.min.should eql(0)
|
||||||
|
result.sec.should eql(0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'and value is a string' do
|
||||||
|
it 'parses the string' do
|
||||||
|
@course.updated_at = 'Dec, 2006'
|
||||||
|
@course['updated_at'].month.should == 12
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not typecast non-datetime values' do
|
||||||
|
@course.updated_at = 'not-datetime'
|
||||||
|
@course['updated_at'].should eql('not-datetime')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'when type primitive is a Date' do
|
||||||
|
describe 'and value given as a hash with keys like :year, :month, etc' do
|
||||||
|
it 'builds a Date instance from hash values' do
|
||||||
|
@course.started_on = {
|
||||||
|
:year => '2007',
|
||||||
|
:month => '3',
|
||||||
|
:day => '25'
|
||||||
|
}
|
||||||
|
result = @course['started_on']
|
||||||
|
|
||||||
|
result.should be_kind_of(Date)
|
||||||
|
result.year.should eql(2007)
|
||||||
|
result.month.should eql(3)
|
||||||
|
result.day.should eql(25)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'and value is a string' do
|
||||||
|
it 'parses the string' do
|
||||||
|
@course.started_on = 'Dec 20th, 2006'
|
||||||
|
@course.started_on.month.should == 12
|
||||||
|
@course.started_on.day.should == 20
|
||||||
|
@course.started_on.year.should == 2006
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not typecast non-date values' do
|
||||||
|
@course.started_on = 'not-date'
|
||||||
|
@course['started_on'].should eql('not-date')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'when type primitive is a Time' do
|
||||||
|
describe 'and value given as a hash with keys like :year, :month, etc' do
|
||||||
|
it 'builds a Time instance from hash values' do
|
||||||
|
@course.ends_at = {
|
||||||
|
:year => '2006',
|
||||||
|
:month => '11',
|
||||||
|
:day => '23',
|
||||||
|
:hour => '12',
|
||||||
|
:min => '0',
|
||||||
|
:sec => '0'
|
||||||
|
}
|
||||||
|
result = @course['ends_at']
|
||||||
|
|
||||||
|
result.should be_kind_of(Time)
|
||||||
|
result.year.should eql(2006)
|
||||||
|
result.month.should eql(11)
|
||||||
|
result.day.should eql(23)
|
||||||
|
result.hour.should eql(12)
|
||||||
|
result.min.should eql(0)
|
||||||
|
result.sec.should eql(0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'and value is a string' do
|
||||||
|
it 'parses the string' do
|
||||||
|
t = Time.now
|
||||||
|
@course.ends_at = t.strftime('%Y/%m/%d %H:%M:%S %z')
|
||||||
|
@course['ends_at'].year.should eql(t.year)
|
||||||
|
@course['ends_at'].month.should eql(t.month)
|
||||||
|
@course['ends_at'].day.should eql(t.day)
|
||||||
|
@course['ends_at'].hour.should eql(t.hour)
|
||||||
|
@course['ends_at'].min.should eql(t.min)
|
||||||
|
@course['ends_at'].sec.should eql(t.sec)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not typecast non-time values' do
|
||||||
|
@course.ends_at = 'not-time'
|
||||||
|
@course['ends_at'].should eql('not-time')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'when type primitive is a Class' do
|
||||||
|
it 'returns same value if a class' do
|
||||||
|
value = Course
|
||||||
|
@course.klass = value
|
||||||
|
@course['klass'].should equal(value)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the class if found' do
|
||||||
|
@course.klass = 'Course'
|
||||||
|
@course['klass'].should eql(Course)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not typecast non-class values' do
|
||||||
|
@course.klass = 'NoClass'
|
||||||
|
@course['klass'].should eql('NoClass')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "casting to a boolean value" do
|
describe 'when type primitive is a Boolean' do
|
||||||
class RootBeerFloat < CouchRest::ExtendedDocument
|
[ true, 'true', 'TRUE', '1', 1, 't', 'T' ].each do |value|
|
||||||
use_database DB
|
it "returns true when value is #{value.inspect}" do
|
||||||
property :tasty, :cast_as => :boolean
|
@course.active = value
|
||||||
|
@course['active'].should be_true
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should add an accessor with a '?' for boolean attributes that returns true or false" do
|
[ false, 'false', 'FALSE', '0', 0, 'f', 'F' ].each do |value|
|
||||||
RootBeerFloat.new(:tasty => true).tasty?.should == true
|
it "returns false when value is #{value.inspect}" do
|
||||||
RootBeerFloat.new(:tasty => 'you bet').tasty?.should == true
|
@course.active = value
|
||||||
RootBeerFloat.new(:tasty => 123).tasty?.should == true
|
@course['active'].should be_false
|
||||||
|
end
|
||||||
RootBeerFloat.new(:tasty => false).tasty?.should == false
|
|
||||||
RootBeerFloat.new(:tasty => 'false').tasty?.should == false
|
|
||||||
RootBeerFloat.new(:tasty => 'FaLsE').tasty?.should == false
|
|
||||||
RootBeerFloat.new(:tasty => nil).tasty?.should == false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should return the real value when the default accessor is used" do
|
[ 'string', 2, 1.0, BigDecimal('1.0'), DateTime.now, Time.now, Date.today, Class, Object.new, ].each do |value|
|
||||||
RootBeerFloat.new(:tasty => true).tasty.should == true
|
it "does not typecast value #{value.inspect}" do
|
||||||
RootBeerFloat.new(:tasty => 'you bet').tasty.should == 'you bet'
|
@course.active = value
|
||||||
RootBeerFloat.new(:tasty => 123).tasty.should == 123
|
@course['active'].should equal(value)
|
||||||
RootBeerFloat.new(:tasty => 'false').tasty.should == 'false'
|
end
|
||||||
RootBeerFloat.new(:tasty => false).tasty.should == false
|
|
||||||
RootBeerFloat.new(:tasty => nil).tasty.should == nil
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "a newly created casted model" do
|
|
||||||
before(:each) do
|
|
||||||
reset_test_db!
|
|
||||||
@cat = Cat.new(:name => 'Toonces')
|
|
||||||
@squeaky_mouse = CatToy.new(:name => 'Squeaky')
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "assigned assigned to a casted property" do
|
|
||||||
it "should have casted_by set to its parent" do
|
|
||||||
@squeaky_mouse.casted_by.should be_nil
|
|
||||||
@cat.favorite_toy = @squeaky_mouse
|
|
||||||
@squeaky_mouse.casted_by.should === @cat
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "appended to a casted collection" do
|
|
||||||
it "should have casted_by set to its parent" do
|
|
||||||
@squeaky_mouse.casted_by.should be_nil
|
|
||||||
@cat.toys << @squeaky_mouse
|
|
||||||
@squeaky_mouse.casted_by.should === @cat
|
|
||||||
@cat.save
|
|
||||||
@cat.toys.first.casted_by.should === @cat
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "list assigned to a casted collection" do
|
|
||||||
it "should have casted_by set on all elements" do
|
|
||||||
toy1 = CatToy.new(:name => 'Feather')
|
|
||||||
toy2 = CatToy.new(:name => 'Mouse')
|
|
||||||
@cat.toys = [toy1, toy2]
|
|
||||||
toy1.casted_by.should === @cat
|
|
||||||
toy2.casted_by.should === @cat
|
|
||||||
@cat.save
|
|
||||||
@cat = Cat.get(@cat.id)
|
|
||||||
@cat.toys[0].casted_by.should === @cat
|
|
||||||
@cat.toys[1].casted_by.should === @cat
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -286,4 +625,4 @@ describe "a casted model retrieved from the database" do
|
||||||
@cat.toys[1].casted_by.should === @cat
|
@cat.toys[1].casted_by.should === @cat
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
4
spec/fixtures/more/article.rb
vendored
4
spec/fixtures/more/article.rb
vendored
|
@ -20,10 +20,10 @@ class Article < CouchRest::ExtendedDocument
|
||||||
return sum(values);
|
return sum(values);
|
||||||
}"
|
}"
|
||||||
|
|
||||||
property :date
|
property :date, :type => 'Date'
|
||||||
property :slug, :read_only => true
|
property :slug, :read_only => true
|
||||||
property :title
|
property :title
|
||||||
property :tags
|
property :tags, :type => ['String']
|
||||||
|
|
||||||
timestamps!
|
timestamps!
|
||||||
|
|
||||||
|
|
18
spec/fixtures/more/course.rb
vendored
18
spec/fixtures/more/course.rb
vendored
|
@ -4,11 +4,19 @@ require File.join(FIXTURE_PATH, 'more', 'person')
|
||||||
class Course < CouchRest::ExtendedDocument
|
class Course < CouchRest::ExtendedDocument
|
||||||
use_database TEST_SERVER.default_database
|
use_database TEST_SERVER.default_database
|
||||||
|
|
||||||
property :title
|
property :title, :cast_as => 'String'
|
||||||
property :questions, :cast_as => ['Question']
|
property :questions, :cast_as => ['Question']
|
||||||
property :professor, :cast_as => 'Person'
|
property :professor, :cast_as => 'Person'
|
||||||
property :final_test_at, :cast_as => 'Time'
|
property :participants, :type => ['Object']
|
||||||
|
property :ends_at, :type => 'Time'
|
||||||
|
property :estimate, :type => 'Float'
|
||||||
|
property :hours, :type => 'Integer'
|
||||||
|
property :profit, :type => 'BigDecimal'
|
||||||
|
property :started_on, :type => 'Date'
|
||||||
|
property :updated_at, :type => 'DateTime'
|
||||||
|
property :active, :type => 'Boolean'
|
||||||
|
property :klass, :type => 'Class'
|
||||||
|
|
||||||
view_by :title
|
view_by :title
|
||||||
view_by :dept, :ducktype => true
|
view_by :dept, :ducktype => true
|
||||||
end
|
end
|
||||||
|
|
3
spec/fixtures/more/event.rb
vendored
3
spec/fixtures/more/event.rb
vendored
|
@ -5,5 +5,4 @@ class Event < CouchRest::ExtendedDocument
|
||||||
property :occurs_at, :cast_as => 'Time', :init_method => 'parse'
|
property :occurs_at, :cast_as => 'Time', :init_method => 'parse'
|
||||||
property :end_date, :cast_as => 'Date', :init_method => 'parse'
|
property :end_date, :cast_as => 'Date', :init_method => 'parse'
|
||||||
|
|
||||||
|
end
|
||||||
end
|
|
||||||
|
|
2
spec/fixtures/more/person.rb
vendored
2
spec/fixtures/more/person.rb
vendored
|
@ -1,7 +1,7 @@
|
||||||
class Person < Hash
|
class Person < Hash
|
||||||
include ::CouchRest::CastedModel
|
include ::CouchRest::CastedModel
|
||||||
property :name
|
|
||||||
property :pet, :cast_as => 'Cat'
|
property :pet, :cast_as => 'Cat'
|
||||||
|
property :name, :type => ['String']
|
||||||
|
|
||||||
def last_name
|
def last_name
|
||||||
name.last
|
name.last
|
||||||
|
|
2
spec/fixtures/more/question.rb
vendored
2
spec/fixtures/more/question.rb
vendored
|
@ -2,5 +2,5 @@ class Question < Hash
|
||||||
include ::CouchRest::CastedModel
|
include ::CouchRest::CastedModel
|
||||||
|
|
||||||
property :q
|
property :q
|
||||||
property :a
|
property :a, :type => 'Object'
|
||||||
end
|
end
|
2
spec/fixtures/more/service.rb
vendored
2
spec/fixtures/more/service.rb
vendored
|
@ -7,6 +7,6 @@ class Service < CouchRest::ExtendedDocument
|
||||||
|
|
||||||
# Official Schema
|
# Official Schema
|
||||||
property :name, :length => 4...20
|
property :name, :length => 4...20
|
||||||
property :price, :type => Integer
|
property :price, :type => 'Integer'
|
||||||
|
|
||||||
end
|
end
|
Loading…
Reference in a new issue