doodle 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +24 -0
- data/Manifest.txt +26 -1
- data/README.txt +9 -8
- data/lib/doodle.rb +43 -1496
- data/lib/doodle/app.rb +6 -0
- data/lib/doodle/attribute.rb +165 -0
- data/lib/doodle/base.rb +180 -0
- data/lib/doodle/collector-1.9.rb +72 -0
- data/lib/doodle/collector.rb +191 -0
- data/lib/doodle/comparable.rb +8 -0
- data/lib/doodle/conversion.rb +80 -0
- data/lib/doodle/core.rb +42 -0
- data/lib/doodle/datatype-holder.rb +39 -0
- data/lib/doodle/debug.rb +20 -0
- data/lib/doodle/deferred.rb +13 -0
- data/lib/doodle/equality.rb +21 -0
- data/lib/doodle/exceptions.rb +29 -0
- data/lib/doodle/factory.rb +91 -0
- data/lib/doodle/getter-setter.rb +154 -0
- data/lib/doodle/info.rb +298 -0
- data/lib/doodle/inherit.rb +40 -0
- data/lib/doodle/json.rb +38 -0
- data/lib/doodle/marshal.rb +16 -0
- data/lib/doodle/normalized_array.rb +512 -0
- data/lib/doodle/normalized_hash.rb +356 -0
- data/lib/doodle/ordered-hash.rb +8 -0
- data/lib/doodle/singleton.rb +23 -0
- data/lib/doodle/smoke-and-mirrors.rb +23 -0
- data/lib/doodle/to_hash.rb +17 -0
- data/lib/doodle/utils.rb +173 -11
- data/lib/doodle/validation.rb +122 -0
- data/lib/doodle/version.rb +1 -1
- data/lib/molic_orderedhash.rb +24 -10
- data/spec/assigned_spec.rb +45 -0
- data/spec/attributes_spec.rb +7 -7
- data/spec/collector_spec.rb +100 -13
- data/spec/doodle_context_spec.rb +5 -5
- data/spec/from_spec.rb +43 -3
- data/spec/json_spec.rb +232 -0
- data/spec/member_init_spec.rb +11 -11
- data/spec/modules_spec.rb +4 -4
- data/spec/multi_collector_spec.rb +91 -0
- data/spec/must_spec.rb +32 -0
- data/spec/spec_helper.rb +14 -4
- data/spec/specialized_attribute_class_spec.rb +2 -2
- data/spec/typed_collector_spec.rb +57 -0
- data/spec/xml_spec.rb +8 -8
- metadata +33 -3
@@ -0,0 +1,80 @@
|
|
1
|
+
class Doodle
|
2
|
+
module ConversionHelper
|
3
|
+
# if block passed, define a conversion from class
|
4
|
+
# if no args, apply conversion to arguments
|
5
|
+
def from(*args, &block)
|
6
|
+
Doodle::Debug.d { [self, args, block]}
|
7
|
+
#p [:from, self, args]
|
8
|
+
if block_given?
|
9
|
+
# set the rule for each arg given
|
10
|
+
args.each do |arg|
|
11
|
+
__doodle__.local_conversions[arg] = block
|
12
|
+
end
|
13
|
+
else
|
14
|
+
convert(self, *args)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# convert a value according to conversion rules
|
19
|
+
# FIXME: move
|
20
|
+
def convert(owner, *args)
|
21
|
+
#pp( { :convert => 1, :owner => owner, :args => args, :conversions => __doodle__.conversions } )
|
22
|
+
begin
|
23
|
+
args = args.map do |value|
|
24
|
+
#!p [:convert, 2, value]
|
25
|
+
if (converter = __doodle__.conversions[value.class])
|
26
|
+
#p [:convert, 3, value, self, caller]
|
27
|
+
value = converter[value]
|
28
|
+
#!p [:convert, 4, value]
|
29
|
+
else
|
30
|
+
#!p [:convert, 5, value]
|
31
|
+
# try to find nearest ancestor
|
32
|
+
this_ancestors = value.class.ancestors
|
33
|
+
#!p [:convert, 6, this_ancestors]
|
34
|
+
matches = this_ancestors & __doodle__.conversions.keys
|
35
|
+
#!p [:convert, 7, matches]
|
36
|
+
indexed_matches = matches.map{ |x| this_ancestors.index(x)}
|
37
|
+
#!p [:convert, 8, indexed_matches]
|
38
|
+
if indexed_matches.size > 0
|
39
|
+
#!p [:convert, 9]
|
40
|
+
converter_class = this_ancestors[indexed_matches.min]
|
41
|
+
#!p [:convert, 10, converter_class]
|
42
|
+
if converter = __doodle__.conversions[converter_class]
|
43
|
+
#!p [:convert, 11, converter]
|
44
|
+
value = converter[value]
|
45
|
+
#!p [:convert, 12, value]
|
46
|
+
end
|
47
|
+
else
|
48
|
+
#!p [:convert, 13, :kind, kind, name, value]
|
49
|
+
mappable_kinds = kind.select{ |x| x <= Doodle::Core }
|
50
|
+
#!p [:convert, 13.1, :kind, kind, mappable_kinds]
|
51
|
+
if mappable_kinds.size > 0
|
52
|
+
mappable_kinds.each do |mappable_kind|
|
53
|
+
#!p [:convert, 14, :kind_is_a_doodle, value.class, mappable_kind, mappable_kind.doodle.conversions, args]
|
54
|
+
if converter = mappable_kind.doodle.conversions[value.class]
|
55
|
+
#!p [:convert, 15, value, mappable_kind, args]
|
56
|
+
value = converter[value]
|
57
|
+
break
|
58
|
+
else
|
59
|
+
#!p [:convert, 16, :no_conversion_for, value.class]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
else
|
63
|
+
#!p [:convert, 17, :kind_has_no_conversions]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
#!p [:convert, 18, value]
|
68
|
+
value
|
69
|
+
end
|
70
|
+
rescue Exception => e
|
71
|
+
owner.__doodle__.handle_error name, ConversionError, "#{e.message}", Doodle::Utils.doodle_caller
|
72
|
+
end
|
73
|
+
if args.size > 1
|
74
|
+
args
|
75
|
+
else
|
76
|
+
args.first
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/doodle/core.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
class Doodle
|
2
|
+
module ClassMethods
|
3
|
+
# provide somewhere to hold thread-specific context information
|
4
|
+
# (I'm claiming the :doodle_xxx namespace)
|
5
|
+
def context
|
6
|
+
Thread.current[:doodle_context] ||= []
|
7
|
+
end
|
8
|
+
def parent
|
9
|
+
context[-1]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
extend ClassMethods
|
14
|
+
|
15
|
+
# Place to hold refs to built-in classes that need special handling
|
16
|
+
module BuiltIns
|
17
|
+
BUILTINS = [String, Hash, Array]
|
18
|
+
end
|
19
|
+
|
20
|
+
# Include Doodle::Core if you want to derive from another class
|
21
|
+
# but still get Doodle goodness in your class (including Factory
|
22
|
+
# methods).
|
23
|
+
module Core
|
24
|
+
module ModuleMethods
|
25
|
+
def included(other)
|
26
|
+
super
|
27
|
+
other.module_eval {
|
28
|
+
# FIXME: this is getting a bit arbitrary
|
29
|
+
include Equality
|
30
|
+
include Comparable
|
31
|
+
include Inherited
|
32
|
+
inherit BaseMethods
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
extend ModuleMethods
|
37
|
+
end
|
38
|
+
|
39
|
+
include Core
|
40
|
+
|
41
|
+
end
|
42
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class Doodle
|
2
|
+
# implements the #doodle directive
|
3
|
+
class DataTypeHolder
|
4
|
+
attr_accessor :klass
|
5
|
+
|
6
|
+
def initialize(klass, &block)
|
7
|
+
@klass = klass
|
8
|
+
instance_eval(&block) if block_given?
|
9
|
+
end
|
10
|
+
|
11
|
+
def define(name, params, block, type_params, &type_block)
|
12
|
+
@klass.class_eval {
|
13
|
+
td = has(name, type_params.merge(params), &type_block)
|
14
|
+
td.instance_eval(&block) if block
|
15
|
+
td
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def has(*args, &block)
|
20
|
+
@klass.class_eval { has(*args, &block) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def must(*args, &block)
|
24
|
+
@klass.class_eval { must(*args, &block) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def from(*args, &block)
|
28
|
+
@klass.class_eval { from(*args, &block) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def arg_order(*args, &block)
|
32
|
+
@klass.class_eval { arg_order(*args, &block) }
|
33
|
+
end
|
34
|
+
|
35
|
+
def doc(*args, &block)
|
36
|
+
@klass.class_eval { doc(*args, &block) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/doodle/debug.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
class Doodle
|
2
|
+
# debugging utilities
|
3
|
+
module Debug
|
4
|
+
class << self
|
5
|
+
# Robert Klemme, (ruby-talk 205150), (ruby-talk 205950)
|
6
|
+
def calling_method(level = 1)
|
7
|
+
caller[level] =~ /`([^']*)'/ and $1
|
8
|
+
end
|
9
|
+
|
10
|
+
def this_method
|
11
|
+
calling_method
|
12
|
+
end
|
13
|
+
|
14
|
+
# output result of block if ENV['DEBUG_DOODLE'] set
|
15
|
+
def d(&block)
|
16
|
+
puts(calling_method + ": " + block.call.inspect) if ENV['DEBUG_DOODLE']
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Doodle
|
2
|
+
# save a block for later execution
|
3
|
+
class DeferredBlock
|
4
|
+
attr_accessor :block
|
5
|
+
def initialize(arg_block = nil, &block)
|
6
|
+
arg_block = block if block_given?
|
7
|
+
@block = arg_block
|
8
|
+
end
|
9
|
+
def call(*a, &b)
|
10
|
+
block.call(*a, &b)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Doodle
|
2
|
+
# two doodles of the same class with the same attribute values are
|
3
|
+
# considered equal
|
4
|
+
module Equality
|
5
|
+
def eql?(o)
|
6
|
+
# p [:comparing, self.class, o.class, self.class == o.class]
|
7
|
+
# p [:values, self.doodle.values, o.doodle.values, self.doodle.values == o.doodle.values]
|
8
|
+
# p [:attributes, doodle.attributes.map { |k, a| [k, send(k).==(o.send(k))] }]
|
9
|
+
res = self.class == o.class &&
|
10
|
+
#self.doodle.values == o.doodle.values
|
11
|
+
# short circuit comparison
|
12
|
+
doodle.attributes.all? { |k, a| send(k).==(o.send(k)) }
|
13
|
+
# p [:res, res]
|
14
|
+
res
|
15
|
+
end
|
16
|
+
def ==(o)
|
17
|
+
eql?(o)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class Doodle
|
2
|
+
# error handling
|
3
|
+
@@raise_exception_on_error = true
|
4
|
+
def self.raise_exception_on_error
|
5
|
+
@@raise_exception_on_error
|
6
|
+
end
|
7
|
+
def self.raise_exception_on_error=(tf)
|
8
|
+
@@raise_exception_on_error = tf
|
9
|
+
end
|
10
|
+
|
11
|
+
# internal error raised when a default was expected but not found
|
12
|
+
class NoDefaultError < Exception
|
13
|
+
end
|
14
|
+
# raised when a validation rule returns false
|
15
|
+
class ValidationError < Exception
|
16
|
+
end
|
17
|
+
# raised when an unknown parameter is passed to initialize
|
18
|
+
class UnknownAttributeError < Exception
|
19
|
+
end
|
20
|
+
# raised when a conversion fails
|
21
|
+
class ConversionError < Exception
|
22
|
+
end
|
23
|
+
# raised when arg_order called with incorrect arguments
|
24
|
+
class InvalidOrderError < Exception
|
25
|
+
end
|
26
|
+
# raised when try to set a readonly attribute after initialization
|
27
|
+
class ReadOnlyError < Exception
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
class Doodle
|
2
|
+
# A factory function is a function that has the same name as
|
3
|
+
# a class which acts just like class.new. For example:
|
4
|
+
# Cat(:name => 'Ren')
|
5
|
+
# is the same as:
|
6
|
+
# Cat.new(:name => 'Ren')
|
7
|
+
# As the notion of a factory function is somewhat contentious [xref
|
8
|
+
# ruby-talk], you need to explicitly ask for them by including Factory
|
9
|
+
# in your base class:
|
10
|
+
# class Animal < Doodle
|
11
|
+
# include Factory
|
12
|
+
# end
|
13
|
+
# class Dog < Animal
|
14
|
+
# end
|
15
|
+
# stimpy = Dog(:name => 'Stimpy')
|
16
|
+
# etc.
|
17
|
+
module Factory
|
18
|
+
RX_IDENTIFIER = /^[A-Za-z_][A-Za-z_0-9]+\??$/
|
19
|
+
module ClassMethods
|
20
|
+
# create a factory function in appropriate module for the specified class
|
21
|
+
def factory(konst)
|
22
|
+
#p [:factory, :ancestors, konst, konst.ancestors]
|
23
|
+
#p [:factory, :lookup, Module.nesting]
|
24
|
+
name = konst.to_s
|
25
|
+
#p [:factory, :name, name]
|
26
|
+
anon_class = false
|
27
|
+
if name =~ /#<Class:0x[a-fA-F0-9]+>::/
|
28
|
+
#p [:factory_anon_class, name]
|
29
|
+
anon_class = true
|
30
|
+
end
|
31
|
+
names = name.split(/::/)
|
32
|
+
name = names.pop
|
33
|
+
# TODO: the code below is almost the same - refactor
|
34
|
+
#p [:factory, :names, names, name]
|
35
|
+
if names.empty? && !anon_class
|
36
|
+
#p [:factory, :top_level_class]
|
37
|
+
# top level class - should be available to all
|
38
|
+
parent_class = Object
|
39
|
+
method_defined = begin
|
40
|
+
method(name)
|
41
|
+
true
|
42
|
+
rescue Object
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
46
|
+
if name =~ Factory::RX_IDENTIFIER && !method_defined && !parent_class.respond_to?(name) && !eval("respond_to?(:#{name})", TOPLEVEL_BINDING)
|
47
|
+
eval("def #{ name }(*args, &block); ::#{name}.new(*args, &block); end", ::TOPLEVEL_BINDING, __FILE__, __LINE__)
|
48
|
+
end
|
49
|
+
else
|
50
|
+
#p [:factory, :other_level_class]
|
51
|
+
parent_class = Object
|
52
|
+
if !anon_class
|
53
|
+
parent_class = names.inject(parent_class) {|c, n| c.const_get(n)}
|
54
|
+
#p [:factory, :parent_class, parent_class]
|
55
|
+
if name =~ Factory::RX_IDENTIFIER && !parent_class.respond_to?(name)
|
56
|
+
# FIXME: find out why define_method version not working
|
57
|
+
parent_class.module_eval("def self.#{name}(*args, &block); #{name}.new(*args, &block); end", __FILE__, __LINE__)
|
58
|
+
end
|
59
|
+
else
|
60
|
+
# NOTE: ruby 1.9.1 specific
|
61
|
+
parent_class_name = names.join('::')
|
62
|
+
#p [:factory, :parent_class_name, parent_class_name]
|
63
|
+
#p [:parent_class_name, parent_class_name]
|
64
|
+
# FIXME: this is truly horrible...
|
65
|
+
hex_object_id = parent_class_name.match(/:(0x[a-zA-Z0-9]+)/)[1]
|
66
|
+
oid = hex_object_id.to_i(16) >> 1
|
67
|
+
# p [:object_id, oid, hex_object_id, hex_object_id.to_i(16) >> 1]
|
68
|
+
parent_class = ObjectSpace._id2ref(oid)
|
69
|
+
|
70
|
+
#p [:parent_object_id, parent_class.object_id, names, parent_class, parent_class_name, parent_class.name]
|
71
|
+
# p [:names, :oid, "%x" % (oid << 1), :konst, konst, :pc, parent_class, :names, names, :self, self]
|
72
|
+
if name =~ Factory::RX_IDENTIFIER && !parent_class.respond_to?(name) && parent_class.const_defined?(name)
|
73
|
+
#p [:context, context]
|
74
|
+
parent_class.module_eval("def #{name}(*args, &block); #{name}.new(*args, &block); end", __FILE__, __LINE__)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
# TODO: check how many times this is being called
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# inherit the factory function capability
|
82
|
+
def included(other)
|
83
|
+
#p [:included, other]
|
84
|
+
super
|
85
|
+
# make +factory+ method available
|
86
|
+
factory other
|
87
|
+
end
|
88
|
+
end
|
89
|
+
extend ClassMethods
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
class Doodle
|
2
|
+
module GetterSetter
|
3
|
+
# either get an attribute value (if no args given) or set it
|
4
|
+
# (using args and/or block)
|
5
|
+
# FIXME: move
|
6
|
+
def getter_setter(name, *args, &block)
|
7
|
+
#p [:getter_setter, name]
|
8
|
+
name = name.to_sym
|
9
|
+
if block_given? || args.size > 0
|
10
|
+
#!p [:getter_setter, :setter, name, *args]
|
11
|
+
_setter(name, *args, &block)
|
12
|
+
else
|
13
|
+
#!p [:getter_setter, :getter, name]
|
14
|
+
_getter(name)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
private :getter_setter
|
18
|
+
|
19
|
+
# get an attribute by name - return default if not otherwise defined
|
20
|
+
# FIXME: init deferred blocks are not getting resolved in all cases
|
21
|
+
def _getter(name, &block)
|
22
|
+
begin
|
23
|
+
#p [:_getter, name]
|
24
|
+
ivar = "@#{name}"
|
25
|
+
if instance_variable_defined?(ivar)
|
26
|
+
#p [:_getter, :instance_variable_defined, name, ivar, instance_variable_get(ivar)]
|
27
|
+
instance_variable_get(ivar)
|
28
|
+
else
|
29
|
+
# handle default
|
30
|
+
# Note: use :init => value to cover cases where defaults don't work
|
31
|
+
# (e.g. arrays that disappear when you go out of scope)
|
32
|
+
att = __doodle__.lookup_attribute(name)
|
33
|
+
# special case for class/singleton :init
|
34
|
+
if att && att.optional?
|
35
|
+
optional_value = att.init_defined? ? att.init : att.default
|
36
|
+
#p [:optional_value, optional_value]
|
37
|
+
case optional_value
|
38
|
+
when DeferredBlock
|
39
|
+
#p [:deferred_block]
|
40
|
+
v = instance_eval(&optional_value.block)
|
41
|
+
when Proc
|
42
|
+
v = instance_eval(&optional_value)
|
43
|
+
else
|
44
|
+
v = optional_value
|
45
|
+
end
|
46
|
+
if att.init_defined?
|
47
|
+
_setter(name, v)
|
48
|
+
end
|
49
|
+
v
|
50
|
+
else
|
51
|
+
# This is an internal error (i.e. shouldn't happen)
|
52
|
+
__doodle__.handle_error name, NoDefaultError, "'#{name}' has no default defined", Doodle::Utils.doodle_caller
|
53
|
+
end
|
54
|
+
end
|
55
|
+
rescue Object => e
|
56
|
+
__doodle__.handle_error name, e, e.to_s, Doodle::Utils.doodle_caller
|
57
|
+
end
|
58
|
+
end
|
59
|
+
private :_getter
|
60
|
+
|
61
|
+
def after_update(params)
|
62
|
+
end
|
63
|
+
|
64
|
+
# set an instance variable by symbolic name and call after_update if changed
|
65
|
+
def ivar_set(name, *args)
|
66
|
+
ivar = "@#{name}"
|
67
|
+
if instance_variable_defined?(ivar)
|
68
|
+
old_value = instance_variable_get(ivar)
|
69
|
+
else
|
70
|
+
old_value = nil
|
71
|
+
end
|
72
|
+
instance_variable_set(ivar, *args)
|
73
|
+
new_value = instance_variable_get(ivar)
|
74
|
+
if new_value != old_value
|
75
|
+
#pp [Doodle, :after_update, { :instance => self, :name => name, :old_value => old_value, :new_value => new_value }]
|
76
|
+
after_update :instance => self, :name => name, :old_value => old_value, :new_value => new_value
|
77
|
+
end
|
78
|
+
end
|
79
|
+
private :ivar_set
|
80
|
+
|
81
|
+
# set an attribute by name - apply validation if defined
|
82
|
+
# FIXME: move
|
83
|
+
def _setter(name, *args, &block)
|
84
|
+
##DBG: Doodle::Debug.d { [:_setter, name, args] }
|
85
|
+
#p [:_setter, name, *args]
|
86
|
+
att = __doodle__.lookup_attribute(name)
|
87
|
+
if att && __doodle__.validation_on && att.readonly
|
88
|
+
raise Doodle::ReadOnlyError, "Trying to set a readonly attribute: #{att.name}", Doodle::Utils.doodle_caller
|
89
|
+
end
|
90
|
+
if block_given?
|
91
|
+
# if a class has been defined, let's assume it can take a
|
92
|
+
# block initializer (test that it's a Doodle or Proc)
|
93
|
+
if att.kind && !att.abstract && klass = att.kind.first
|
94
|
+
if [Doodle, Proc].any?{ |c| klass <= c }
|
95
|
+
# p [:_setter, '# 1 converting arg to value with kind ' + klass.to_s]
|
96
|
+
args = [klass.new(*args, &block)]
|
97
|
+
else
|
98
|
+
__doodle__.handle_error att.name, ArgumentError, "#{klass} #{att.name} does not take a block initializer", Doodle::Utils.doodle_caller
|
99
|
+
end
|
100
|
+
else
|
101
|
+
# this is used by init do ... block
|
102
|
+
args.unshift(DeferredBlock.new(block))
|
103
|
+
end
|
104
|
+
end
|
105
|
+
if att # = __doodle__.lookup_attribute(name)
|
106
|
+
if att.kind && !att.abstract && klass = att.kind.first
|
107
|
+
if !args.first.kind_of?(klass) && [Doodle].any?{ |c| klass <= c }
|
108
|
+
#p [:_setter, "#2 converting arg #{att.name} to value with kind #{klass.to_s}"]
|
109
|
+
#p [:_setter, args]
|
110
|
+
begin
|
111
|
+
args = [klass.new(*args, &block)]
|
112
|
+
rescue Object => e
|
113
|
+
__doodle__.handle_error att.name, e.class, e.to_s, Doodle::Utils.doodle_caller
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
#p [:_setter, :got_att1, name, ivar, *args]
|
118
|
+
v = ivar_set(name, att.validate(self, *args))
|
119
|
+
|
120
|
+
#p [:_setter, :got_att2, name, ivar, :value, v]
|
121
|
+
#v = instance_variable_set(ivar, *args)
|
122
|
+
else
|
123
|
+
#p [:_setter, :no_att, name, *args]
|
124
|
+
##DBG: Doodle::Debug.d { [:_setter, "no attribute"] }
|
125
|
+
v = ivar_set(name, *args)
|
126
|
+
end
|
127
|
+
validate!(false)
|
128
|
+
v
|
129
|
+
end
|
130
|
+
private :_setter
|
131
|
+
|
132
|
+
# define a getter_setter
|
133
|
+
# fixme: move
|
134
|
+
def define_getter_setter(name, params = { }, &block)
|
135
|
+
# need to use string eval because passing block
|
136
|
+
sc_eval "def #{name}(*args, &block); getter_setter(:#{name}, *args, &block); end", __FILE__, __LINE__
|
137
|
+
sc_eval "def #{name}=(*args, &block); _setter(:#{name}, *args); end", __FILE__, __LINE__
|
138
|
+
|
139
|
+
# this is how it should be done (in 1.9)
|
140
|
+
# module_eval {
|
141
|
+
# define_method name do |*args, &block|
|
142
|
+
# getter_setter(name.to_sym, *args, &block)
|
143
|
+
# end
|
144
|
+
# define_method "#{name}=" do |*args, &block|
|
145
|
+
# _setter(name.to_sym, *args, &block)
|
146
|
+
# end
|
147
|
+
# }
|
148
|
+
end
|
149
|
+
private :define_getter_setter
|
150
|
+
|
151
|
+
|
152
|
+
|
153
|
+
end
|
154
|
+
end
|