doodle 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|