2010-03-03 03:18:32 +01:00
require 'time'
2010-05-10 21:19:24 +02:00
require File . join ( File . dirname ( __FILE__ ) , '..' , 'property' )
require File . join ( File . dirname ( __FILE__ ) , '..' , 'casted_array' )
require File . join ( File . dirname ( __FILE__ ) , '..' , 'typecast' )
2009-07-08 08:55:20 +02:00
2009-01-30 03:45:01 +01:00
module CouchRest
module Mixins
2009-02-09 20:20:23 +01:00
module Properties
2009-01-30 03:45:01 +01:00
2009-02-06 03:57:11 +01:00
class IncludeError < StandardError ; end
2010-03-03 03:18:32 +01:00
include :: CouchRest :: More :: Typecast
2009-01-30 03:45:01 +01:00
def self . included ( base )
2009-07-19 10:23:51 +02:00
base . class_eval <<-EOS, __FILE__, __LINE__ + 1
2009-03-27 19:11:49 +01:00
extlib_inheritable_accessor ( :properties ) unless self . respond_to? ( :properties )
2009-03-20 02:53:17 +01:00
self . properties || = [ ]
EOS
2009-01-30 03:45:01 +01:00
base . extend ( ClassMethods )
2009-02-09 20:20:23 +01:00
raise CouchRest :: Mixins :: Properties :: IncludeError , " You can only mixin Properties in a class responding to [] and []=, if you tried to mixin CastedModel, make sure your class inherits from Hash or responds to the proper methods " unless ( base . new . respond_to? ( :[] ) && base . new . respond_to? ( :[]= ) )
2009-02-06 03:57:11 +01:00
end
def apply_defaults
2009-06-05 05:44:44 +02:00
return if self . respond_to? ( :new? ) && ( new ? == false )
2009-02-09 20:20:23 +01:00
return unless self . class . respond_to? ( :properties )
2009-02-06 03:57:11 +01:00
return if self . class . properties . empty?
# TODO: cache the default object
self . class . properties . each do | property |
key = property . name . to_s
2009-05-15 21:44:41 +02:00
# let's make sure we have a default
2009-06-16 00:31:50 +02:00
unless property . default . nil?
2009-02-06 03:57:11 +01:00
if property . default . class == Proc
2009-02-09 21:08:55 +01:00
self [ key ] = property . default . call
2009-02-06 03:57:11 +01:00
else
self [ key ] = Marshal . load ( Marshal . dump ( property . default ) )
end
end
end
2009-01-30 03:45:01 +01:00
end
2009-02-09 20:20:23 +01:00
def cast_keys
return unless self . class . properties
self . class . properties . each do | property |
2010-03-03 03:18:32 +01:00
cast_property ( property )
2009-06-10 06:02:04 +02:00
end
end
2010-03-03 03:18:32 +01:00
def cast_property ( property , assigned = false )
return unless property . casted
key = self . has_key? ( property . name ) ? property . name : property . name . to_sym
# Don't cast the property unless it has a value
return unless self [ key ]
if property . type . is_a? ( Array )
2010-03-30 22:50:47 +02:00
klass = property . type [ 0 ]
2010-03-11 11:49:54 +01:00
self [ key ] = [ self [ key ] ] unless self [ key ] . is_a? ( Array )
arr = self [ key ] . collect do | value |
2010-03-03 03:18:32 +01:00
value = typecast_value ( value , klass , property . init_method )
associate_casted_to_parent ( value , assigned )
value
end
2010-03-30 22:50:47 +02:00
# allow casted_by calls to be passed up chain by wrapping in CastedArray
2010-03-31 10:25:33 +02:00
self [ key ] = klass != String ? :: CouchRest :: CastedArray . new ( arr ) : arr
2010-03-03 03:18:32 +01:00
self [ key ] . casted_by = self if self [ key ] . respond_to? ( :casted_by )
else
2010-03-30 22:50:47 +02:00
self [ key ] = typecast_value ( self [ key ] , property . type , property . init_method )
2010-03-03 03:18:32 +01:00
associate_casted_to_parent ( self [ key ] , assigned )
2009-01-30 03:45:01 +01:00
end
2010-03-03 03:18:32 +01:00
end
2009-02-09 20:20:23 +01:00
2009-06-12 21:22:58 +02:00
def associate_casted_to_parent ( casted , assigned )
2009-06-10 06:02:04 +02:00
casted . casted_by = self if casted . respond_to? ( :casted_by )
2009-06-12 21:22:58 +02:00
casted . document_saved = true if ! assigned && casted . respond_to? ( :document_saved )
2009-06-10 06:02:04 +02:00
end
def cast_property_by_name ( property_name )
return unless self . class . properties
property = self . class . properties . detect { | property | property . name == property_name }
return unless property
2009-06-12 21:22:58 +02:00
cast_property ( property , true )
2009-06-10 06:02:04 +02:00
end
2010-03-03 03:18:32 +01:00
2009-02-09 20:20:23 +01:00
module ClassMethods
2009-02-06 02:06:12 +01:00
2010-05-13 00:17:30 +02:00
def property ( name , * options )
opts = { }
type = options . shift
if type . class != Hash
opts [ :type ] = type
opts . merge! ( options . shift || { } )
else
opts . update ( type )
end
2009-03-20 02:53:17 +01:00
existing_property = self . properties . find { | p | p . name == name . to_s }
2010-05-13 00:17:30 +02:00
if existing_property . nil? || ( existing_property . default != opts [ :default ] )
define_property ( name , opts )
2009-03-20 02:53:17 +01:00
end
2009-02-06 02:06:12 +01:00
end
protected
# This is not a thread safe operation, if you have to set new properties at runtime
# make sure to use a mutex.
def define_property ( name , options = { } )
2009-02-09 20:20:23 +01:00
# check if this property is going to casted
2010-03-03 03:18:32 +01:00
options [ :casted ] = ! ! ( options [ :cast_as ] || options [ :type ] )
2009-02-09 20:20:23 +01:00
property = CouchRest :: Property . new ( name , ( options . delete ( :cast_as ) || options . delete ( :type ) ) , options )
2010-03-03 03:18:32 +01:00
create_property_getter ( property )
2009-01-30 03:45:01 +01:00
create_property_setter ( property ) unless property . read_only == true
2009-02-06 02:06:12 +01:00
properties << property
2009-01-30 03:45:01 +01:00
end
2009-02-06 02:06:12 +01:00
2009-02-09 20:20:23 +01:00
# defines the getter for the property (and optional aliases)
2009-01-30 03:45:01 +01:00
def create_property_getter ( property )
2009-02-09 20:20:23 +01:00
# meth = property.name
2009-07-19 10:23:51 +02:00
class_eval <<-EOS, __FILE__, __LINE__ + 1
2009-02-09 20:20:23 +01:00
def #{property.name}
self [ '#{property.name}' ]
2009-01-30 03:45:01 +01:00
end
EOS
2010-05-21 23:00:19 +02:00
if [ 'boolean' , TrueClass . to_s . downcase ] . include? ( property . type . to_s . downcase )
2009-07-25 05:12:03 +02:00
class_eval <<-EOS, __FILE__, __LINE__
def #{property.name}?
2010-05-22 00:17:33 +02:00
if self [ '#{property.name}' ] . nil? || self [ '#{property.name}' ] == false
2009-07-25 05:12:03 +02:00
false
else
true
end
end
EOS
end
2009-01-30 03:45:01 +01:00
if property . alias
2009-07-19 10:23:51 +02:00
class_eval <<-EOS, __FILE__, __LINE__ + 1
2009-02-09 20:20:23 +01:00
alias #{property.alias.to_sym} #{property.name.to_sym}
2009-01-30 03:45:01 +01:00
EOS
end
end
2009-02-09 20:20:23 +01:00
# defines the setter for the property (and optional aliases)
2009-01-30 03:45:01 +01:00
def create_property_setter ( property )
2009-06-10 06:02:04 +02:00
property_name = property . name
2009-01-30 03:45:01 +01:00
class_eval <<-EOS
2009-06-10 06:02:04 +02:00
def #{property_name}=(value)
2010-03-03 03:18:32 +01:00
self [ '#{property_name}' ] = value
cast_property_by_name ( '#{property_name}' )
2009-01-30 03:45:01 +01:00
end
EOS
if property . alias
class_eval <<-EOS
2009-06-10 06:02:04 +02:00
alias #{property.alias.to_sym}= #{property_name.to_sym}=
2009-01-30 03:45:01 +01:00
EOS
end
end
2010-03-03 03:18:32 +01:00
2009-01-30 03:45:01 +01:00
end # module ClassMethods
2010-03-03 03:18:32 +01:00
2009-01-30 03:45:01 +01:00
end
end
2009-09-22 15:19:08 +02:00
end