cascading_classes 0.3.0 → 0.6.0
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/README.md +959 -140
- data/Rakefile +9 -2
- data/lib/cascading_classes.rb +254 -46
- data/lib/cascading_classes/cascading_classes.rb +194 -40
- data/lib/cascading_classes/dsl.rb +137 -59
- data/lib/cascading_classes/parse_options.rb +71 -0
- data/lib/cascading_classes/types.rb +229 -0
- data/spec/basics/basic_spec.rb +230 -0
- data/spec/basics/block_spec.rb +52 -0
- data/spec/basics/container_spec.rb +201 -0
- data/spec/basics/inherit_spec.rb +339 -0
- data/spec/basics/proc_spec.rb +343 -0
- data/spec/class_helper_methods/parents_for_spec.rb +36 -0
- data/spec/custom_classes/hash_like_spec.rb +144 -0
- data/spec/helper_spec.rb +34 -2
- data/spec/instances/basics.rb +239 -0
- data/spec/preset_classes/array_spec.rb +214 -0
- data/spec/preset_classes/hash_spec.rb +210 -0
- data/spec/preset_classes/strings.rb +190 -0
- data/spec/preset_classes/undefined.rb +56 -0
- data/spec/usage/block_spec.rb +59 -0
- data/spec/usage/proc_spec.rb +60 -0
- data/todo +6 -0
- metadata +35 -14
- data/spec/alternative_syntax_spec.rb +0 -173
- data/spec/extended_spec.rb +0 -315
- data/spec/included_spec.rb +0 -103
@@ -1,94 +1,147 @@
|
|
1
|
+
|
2
|
+
# todo: blocks in each property definition??
|
3
|
+
|
1
4
|
module CascadingClasses
|
2
5
|
|
3
|
-
# based off
|
4
|
-
# http://blog.oleganza.com/post/115377756/correct-blankslate-in-ruby
|
5
6
|
class BlankSlate
|
6
7
|
class << self; alias __undef_method undef_method; end
|
7
8
|
keep = [:instance_eval, :object_id]
|
8
|
-
ancestors.inject([]){|res, a|
|
9
|
-
|
9
|
+
ancestors.inject([]){|res, a|
|
10
|
+
res + (a.instance_methods - keep)
|
11
|
+
}.uniq.each{|m|
|
12
|
+
(__undef_method(m) rescue nil) unless m =~ /^__/
|
13
|
+
}
|
10
14
|
end
|
11
|
-
|
15
|
+
|
12
16
|
class DSL < BlankSlate
|
13
17
|
|
18
|
+
## def method_missing(meth, *args, &block)
|
14
19
|
def method_missing(meth, *args, &block)
|
20
|
+
## args.unshift(Hash.new) if block_given? # hackish: turns empty block into a hash
|
15
21
|
case meth
|
16
22
|
when /inspect/
|
17
23
|
super
|
18
|
-
when /set_property/
|
19
|
-
__property__(args[0], *args[1..-1], &block)
|
20
24
|
else
|
21
25
|
__property__(meth, *args, &block)
|
22
26
|
end
|
23
27
|
end
|
24
28
|
|
25
29
|
def __property__(name, args={}, &block)
|
30
|
+
name = name.to_sym
|
31
|
+
|
26
32
|
@names << name unless @names.include? name
|
27
33
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
args[
|
34
|
-
|
35
|
-
args[:begin]
|
36
|
-
else
|
37
|
-
nil
|
34
|
+
args.keys.each{|k| args[k.to_sym] = args.delete(k) unless Symbol === k}
|
35
|
+
|
36
|
+
t = [:default, :start, :begin, :data, :template].detect{|v| args.include?(v)}
|
37
|
+
## t = [:default].detect{|v| args.include?(v)}
|
38
|
+
if t
|
39
|
+
@default[name] = args[t]
|
40
|
+
## (@options_given[name] ||= []) << :default
|
38
41
|
end
|
39
42
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
43
|
+
t = [:classes_only, :only_classes, :exclude_instances, :no_instances,
|
44
|
+
:not_instances, :skip_instances].detect{|v| args.include?(v)}
|
45
|
+
s = [:instances, :apply_to_instances, :both, :classes_and_instances,
|
46
|
+
:instances_and_classes, :instances_too, :instance].detect{|v| args.include?(v)}
|
47
|
+
@instances_too[name] = nil
|
48
|
+
@instances_too[name] = !args[t] if t
|
49
|
+
@instances_too[name] = !!args[s] if s
|
50
|
+
@instances_too[name] = @apply_to_instances unless t or s
|
51
|
+
|
52
|
+
t = [:getter, :getters, :reader, :readers].detect{|v| args.include?(v)}
|
53
|
+
@getters[name] = t ? [*args[t]] : [name]
|
54
|
+
|
55
|
+
t = [:setter, :setters, :writer, :writers].detect{|v| args.include?(v)}
|
56
|
+
@setters[name] = t ? [*args[t]] : ["#{name}=".to_sym]
|
57
|
+
|
58
|
+
# nil or a after callback
|
59
|
+
t = [:after_get, :after_getter, :after_read, :after_reader,
|
60
|
+
:after, :after_callback, :callback].detect{|v| args[v]}
|
61
|
+
@after_get[name] = t ? args[t] : nil
|
62
|
+
## @after_get[name] = block if block # and t.nil?
|
63
|
+
# NOTE: the line above doesn't work when default == {}
|
64
|
+
# since it assumes there is a block
|
65
|
+
|
66
|
+
@type_raw[name] =
|
67
|
+
if t = [:bool, :boolean, :binary, :FalseClass,
|
68
|
+
:TrueClass].detect{|v| args.include?(v)}
|
69
|
+
[:TrueClass, args[t]]
|
70
|
+
elsif t = [:int, :integer, :fixnum, :Fixnum,
|
71
|
+
Fixnum].detect{|v| args.include?(v)}
|
72
|
+
[:Fixnum, args[t]]
|
73
|
+
elsif t = [:float, :Float, Float].detect{|v| args.include?(v)}
|
74
|
+
[:Float, args[t]]
|
75
|
+
elsif t = [:string, :str, :String, String].detect{|v| args.include?(v)}
|
76
|
+
[:String, args[t]]
|
77
|
+
elsif t = [:dict, :hash, :Hash, Hash].detect{|v| args.include?(v)}
|
78
|
+
[:Hash, args[t]]
|
79
|
+
elsif t = [:array, :list, :Array, Array].detect{|v| args.include?(v)}
|
80
|
+
[:Array, args[t]]
|
81
|
+
elsif t = [:set, :Set, Set].detect{|v| args.include?(v)}
|
82
|
+
[:Set, args[t]]
|
83
|
+
elsif t = [:proc, :Proc, Proc].detect{|v| args.include?(v)}
|
84
|
+
[:Proc, args[t]]
|
85
|
+
## elsif t = [:nil, :NilClass, NilClass].detect{|v| args.include?(v)}
|
86
|
+
## [:NilClass, args[t]]
|
87
|
+
elsif t = [:sym, :symbol, :Symbol, Symbol].detect{|v| args.include?(v)}
|
88
|
+
[:Symbol, args[t]]
|
89
|
+
else
|
90
|
+
args.include?(:type) ? [args[:type].to_s.to_sym, true] : [nil, nil]
|
91
|
+
end
|
92
|
+
|
93
|
+
if t = [:blank, :null].detect{|v| args.include?(v)}
|
94
|
+
@blank[name] = if Proc === args[t]
|
95
|
+
args[t]
|
96
|
+
else
|
97
|
+
blank_val = args[t]
|
98
|
+
Proc.new{|v| v == blank_val}
|
99
|
+
end
|
100
|
+
(@options_given[name] ||= []) << :blank
|
64
101
|
end
|
65
102
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
103
|
+
t = [:proc_inst_exec, :proc_instance_exec].detect{|v| args.include?(v)}
|
104
|
+
@proc_inst_exec[name] = t ? !!args[t] : CascadingClasses.proc_inst_exec?
|
105
|
+
|
106
|
+
t = [:block_inst_exec, :block_instance_exec].detect{|v| args.include?(v)}
|
107
|
+
@block_inst_exec[name] = t ? !!args[t] : CascadingClasses.block_inst_exec?
|
108
|
+
|
109
|
+
t = [:new, :new_instance, :init, :initialize].detect{|v| args.include?(v)}
|
110
|
+
if t
|
111
|
+
raise "#{args[t]} is not of type Proc (for #{t})" unless Proc === args[t]
|
112
|
+
@new[name] = args[t]
|
113
|
+
(@options_given[name] ||= []) << :new
|
114
|
+
end
|
115
|
+
|
116
|
+
t = [:ensure_type].detect{|v| args.include?(v)}
|
117
|
+
if t
|
118
|
+
raise "#{args[t]} is not of type Proc (for #{t})" unless Proc === args[t]
|
119
|
+
@ensure_type[name] = args[t]
|
120
|
+
(@options_given[name] ||= []) << :ensure_type
|
74
121
|
end
|
75
122
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
[
|
123
|
+
t = [:inherit, :inherit_mode].detect{|v| args.include?(v)}
|
124
|
+
if t
|
125
|
+
unless s = [nil, false, true].any?{|v| args[t] == v}
|
126
|
+
raise "':inherit' value: #{args[t]} must be one of: nil, false, true, <fixnum>" unless
|
127
|
+
Fixnum === args[t]
|
128
|
+
end
|
129
|
+
@inherit[name] = args[t]
|
130
|
+
(@options_given[name] ||= []) << :inherit
|
84
131
|
end
|
85
132
|
end
|
86
133
|
|
87
134
|
def initialize(apply_to_instances, &block)
|
88
|
-
@
|
135
|
+
@type_raw = {} # temp var
|
136
|
+
@apply_to_instances = !!apply_to_instances
|
89
137
|
@names = []
|
90
|
-
@
|
138
|
+
@default, @instances_too = {}, {}
|
91
139
|
@getters, @setters = {}, {}
|
140
|
+
@type, @after_get = {}, {}
|
141
|
+
@blank, @proc_inst_exec, @block_inst_exec = {}, {}, {}
|
142
|
+
@new, @inherit = {}, {}
|
143
|
+
@ensure_type = {}
|
144
|
+
@options_given = {} # need to know which options were set manually
|
92
145
|
|
93
146
|
return unless block_given?
|
94
147
|
if block.arity == 0
|
@@ -96,6 +149,31 @@ module CascadingClasses
|
|
96
149
|
else
|
97
150
|
raise "ArgumentError: wrong number of args for block: #{block.arity} for 1"
|
98
151
|
end
|
152
|
+
|
153
|
+
@names.each do |name|
|
154
|
+
type = @type[name] = CascadingClasses.get_type(@default[name], *@type_raw[name])
|
155
|
+
|
156
|
+
@blank[name] = CascadingClasses.default_blank(type) unless @blank[name]
|
157
|
+
@new[name] = CascadingClasses.default_new(type) unless @new[name]
|
158
|
+
|
159
|
+
unless CascadingClasses.undefined_type?(type)
|
160
|
+
## unless @default.include?(name) and not @default[name].nil?
|
161
|
+
unless @default.include?(name)
|
162
|
+
@default[name] = @new[name].call
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
unless @inherit.include?(name)
|
167
|
+
@inherit[name] = CascadingClasses.undefined_type?(type) ? :undef :
|
168
|
+
(CascadingClasses.is_container?(type) ? false : true)
|
169
|
+
end
|
170
|
+
|
171
|
+
@ensure_type[name] =
|
172
|
+
CascadingClasses.default_ensure_type(type, @blank[name], @new[name]) unless
|
173
|
+
@ensure_type.include?(name)
|
174
|
+
|
175
|
+
@options_given[name] ||= []
|
176
|
+
end
|
99
177
|
end
|
100
178
|
|
101
179
|
end # class Helper
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'cascading_classes/dsl'
|
2
|
+
|
3
|
+
module CascadingClasses
|
4
|
+
|
5
|
+
# returns hash: {..., attr => [default, inst_too], .. }
|
6
|
+
# todo: consider removing support for custom getters/setters in array syntax
|
7
|
+
def self.parse_options(apply_to_instances, *properties, &block)
|
8
|
+
err = "syntax error: list properties or supply block, but not both"
|
9
|
+
raise err if properties.size > 0 and block_given?
|
10
|
+
|
11
|
+
res = {}
|
12
|
+
properties.each do |prop|
|
13
|
+
name, default, inst_too = prop, nil, apply_to_instances
|
14
|
+
if Array === prop
|
15
|
+
case prop.size
|
16
|
+
when 0 then next
|
17
|
+
when 1 then name = prop[0]
|
18
|
+
when 2 then name, default = prop[0..1]
|
19
|
+
else
|
20
|
+
name, default, inst_too = prop[0..2]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
name = name.to_sym
|
24
|
+
## type = default.nil? ? :Object : default.class.name.to_sym
|
25
|
+
type = default.nil? ? CascadingClasses.undefined_type() : default.class.name.to_sym
|
26
|
+
|
27
|
+
new_proc = CascadingClasses.default_new(type) # proc creates new obj
|
28
|
+
is_blank = CascadingClasses.default_blank(type) # proc verifies blank values
|
29
|
+
|
30
|
+
res[name] = {
|
31
|
+
:default => default, :inst_too => !!inst_too,
|
32
|
+
:getters => [name], :setters => ["#{name}=".to_sym],
|
33
|
+
:type => type, :after_get => nil,
|
34
|
+
:blank => is_blank,
|
35
|
+
:proc_inst_exec => CascadingClasses.proc_inst_exec?,
|
36
|
+
:block_inst_exec => CascadingClasses.block_inst_exec?,
|
37
|
+
:new => new_proc,
|
38
|
+
:inherit => CascadingClasses.undefined_type?(type) ?
|
39
|
+
:undef : (CascadingClasses.is_container?(type) ? false : true),
|
40
|
+
:ensure_type => CascadingClasses.default_ensure_type(type, is_blank, new_proc),
|
41
|
+
:options_given => []
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
if block_given?
|
46
|
+
a = CascadingClasses::DSL.new(apply_to_instances, &block)
|
47
|
+
|
48
|
+
names = a.instance_eval{ @names }
|
49
|
+
|
50
|
+
pluck = proc{|v| a.instance_eval("@#{v}") }
|
51
|
+
|
52
|
+
vars = [:default, :instances_too, :getters, :setters, :type, :after_get, :blank,
|
53
|
+
:proc_inst_exec, :block_inst_exec, :new, :inherit, :ensure_type,
|
54
|
+
:options_given]
|
55
|
+
|
56
|
+
names.each{|name| res[name] = Hash[ vars.map{|v| [v, pluck.call(v)[name]] } ] }
|
57
|
+
end
|
58
|
+
|
59
|
+
# validation
|
60
|
+
res.each do |name, settings|
|
61
|
+
|
62
|
+
=begin -> why must new values be blank? ans: b/c if they don't
|
63
|
+
new_var = settings[:new].call
|
64
|
+
raise "proc to create new #{settings[:type]} is #{new_var.inspect}, not blank" unless
|
65
|
+
settings[:blank].call(new_var)
|
66
|
+
=end
|
67
|
+
end
|
68
|
+
|
69
|
+
res
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,229 @@
|
|
1
|
+
module CascadingClasses
|
2
|
+
def self.undefined_type
|
3
|
+
:undefined_type
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.undefined_type?(type)
|
7
|
+
type == self.undefined_type
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.basic_types
|
11
|
+
[:String, :Fixnum, :Float, :bool, :boolean, :Symbol, :Hash, :Array,
|
12
|
+
:Set, :Proc, :FalseClass, :TrueClass]
|
13
|
+
end
|
14
|
+
|
15
|
+
# turns :Hash into Hash, :Array into Array, ...
|
16
|
+
def self.unsymbolize(obj)
|
17
|
+
res = eval(obj.to_s) rescue nil
|
18
|
+
(res.class == Class) ? res : obj
|
19
|
+
end
|
20
|
+
|
21
|
+
# typically you will be given just a default value, from that
|
22
|
+
# we need to determine the type: default.class.to_s.to_sym
|
23
|
+
# in this case ensure_type will be nil and ensure_val will be ignored
|
24
|
+
#
|
25
|
+
# but a type can also be specified explicitly:
|
26
|
+
#
|
27
|
+
# A.cascade do
|
28
|
+
# name :hash => true
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# in this case ensure_type will be :Hash and ensure_val will be true
|
32
|
+
def self.get_type(default=nil, ensure_type=nil, ensure_val=nil)
|
33
|
+
raise "type: #{ensure_type} must be set to true or false, not #{ensure_val}" unless
|
34
|
+
ensure_val == true or ensure_val == false if ensure_type
|
35
|
+
|
36
|
+
type = default.class.to_s.to_sym
|
37
|
+
type = self.undefined_type if type == :NilClass
|
38
|
+
|
39
|
+
if ensure_type
|
40
|
+
if ensure_val then ensure_type
|
41
|
+
## elsif ensure_type == type then :Object
|
42
|
+
elsif ensure_type == type then self.undefined_type
|
43
|
+
else type
|
44
|
+
end
|
45
|
+
else type
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.is_of_type?(obj, klass)
|
50
|
+
obj.class.to_s.to_sym == klass
|
51
|
+
end
|
52
|
+
|
53
|
+
# returns a proc that takes an argument, evaluating
|
54
|
+
# whether it is blank
|
55
|
+
def self.default_blank(klass)
|
56
|
+
## raise "Hell" unless basic_type.include klass # sanity
|
57
|
+
|
58
|
+
=begin
|
59
|
+
if is_container?(klass)
|
60
|
+
return Proc.new{|v| v.respond_to?(:size) ? v.size.zero? : true} # *eval*
|
61
|
+
end
|
62
|
+
=end
|
63
|
+
return Proc.new{|v| v.size.zero?} if is_container?(klass) # *eval*
|
64
|
+
|
65
|
+
klass = klass.to_s.to_sym
|
66
|
+
|
67
|
+
case klass
|
68
|
+
## when :Proc, :Object then Proc.new{|v| v.nil? }
|
69
|
+
when :Proc then Proc.new{|v| v.nil? }
|
70
|
+
when :String then Proc.new{|v| v == ''}
|
71
|
+
when :Fixnum, :Float then Proc.new{|v| v.nil?}
|
72
|
+
## Proc.new{|v| v.zero? }
|
73
|
+
when :Symbol then Proc.new{|v| v.nil? or v.size.zero? }
|
74
|
+
when :FalseClass, :TrueClass then Proc.new{|v| v.nil? }
|
75
|
+
else
|
76
|
+
self.undefined_type?(klass) ? Proc.new{|v| v.nil? } :
|
77
|
+
Proc.new{|v| !!v}
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# subclass' initialition of own version of property
|
82
|
+
def self.default_new(klass)
|
83
|
+
## raise "Hell" unless basic_type.include klass # sanity
|
84
|
+
case klass
|
85
|
+
when :String then Proc.new{ String.new }
|
86
|
+
when :Fixnum then Proc.new{ nil }
|
87
|
+
## Proc.new{ 0 }
|
88
|
+
when :Float then Proc.new{ nil }
|
89
|
+
## Proc.new{ 0.0 }
|
90
|
+
when :Hash then Proc.new{ Hash.new }
|
91
|
+
when :Array then Proc.new{ Array.new }
|
92
|
+
when :Proc, :Symbol then Proc.new{ nil }
|
93
|
+
when :FalseClass, :TrueClass, :NilClass then Proc.new{ nil }
|
94
|
+
else
|
95
|
+
if self.undefined_type?(klass) then Proc.new{ nil }
|
96
|
+
else
|
97
|
+
s = (klass.class == Class) ? klass : unsymbolize(klass) # *eval*
|
98
|
+
if s.class == Class and s.respond_to?(:new)
|
99
|
+
Proc.new{ s.new }
|
100
|
+
else
|
101
|
+
raise "don't know how to create new instance for #{klass}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# naive transformation to be applied for each type to ensure
|
108
|
+
# that it stays that type
|
109
|
+
# of course it would be better (wouldn't it??) to react
|
110
|
+
# to an object rather than a 'type'
|
111
|
+
# but one of the features we support is to specify the
|
112
|
+
# type before any default data has been given
|
113
|
+
# procs take any value and should attempt to convert to specified type
|
114
|
+
# is_blank: proc that determines whether a value of that type is blank
|
115
|
+
# new_proc: proc that creates new objects of that type
|
116
|
+
def self.default_ensure_type(klass, is_blank, new_proc)
|
117
|
+
## raise "Hell" unless basic_type.include klass # sanity
|
118
|
+
klass = klass.to_s.to_sym
|
119
|
+
pre = lambda{|v|
|
120
|
+
is_blank.call(v) ? v : (v || new_proc.call) }
|
121
|
+
|
122
|
+
case klass
|
123
|
+
when :Hash
|
124
|
+
lambda do |v|
|
125
|
+
return v if is_blank.call(v)
|
126
|
+
v = new_proc.call if v.nil?
|
127
|
+
return v if CascadingClasses.is_of_type?(v, klass)
|
128
|
+
v = v.to_hash if v.respond_to?(:to_hash)
|
129
|
+
Hash === v ? v : {v => nil}
|
130
|
+
end
|
131
|
+
when :Array
|
132
|
+
lambda do |v|
|
133
|
+
return v if is_blank.call(v)
|
134
|
+
v = new_proc.call if v.nil?
|
135
|
+
return v if CascadingClasses.is_of_type?(v, klass)
|
136
|
+
v = v.to_a if v.respond_to?(:to_a)
|
137
|
+
Array === v ? v : [*v]
|
138
|
+
end
|
139
|
+
when :Set
|
140
|
+
lambda do |v|
|
141
|
+
return v if is_blank.call(v)
|
142
|
+
v = new_proc.call if v.nil?
|
143
|
+
return v if CascadingClasses.is_of_type?(v, klass)
|
144
|
+
if v.respond_to? :to_set
|
145
|
+
v.to_set
|
146
|
+
elsif v.respond_to? :to_a
|
147
|
+
Set.new(v.to_a)
|
148
|
+
else
|
149
|
+
Set.new([*v])
|
150
|
+
end
|
151
|
+
end
|
152
|
+
## when :Proc then Proc.new{|v| v}
|
153
|
+
when :Proc
|
154
|
+
Proc.new do |v|
|
155
|
+
if v.nil? then nil
|
156
|
+
elsif Proc === v then v
|
157
|
+
else Proc.new{ v }
|
158
|
+
end
|
159
|
+
end
|
160
|
+
when :Fixnum
|
161
|
+
lambda{|v|
|
162
|
+
return v if is_blank.call(v)
|
163
|
+
v.to_i
|
164
|
+
}
|
165
|
+
when :Float
|
166
|
+
lambda{|v|
|
167
|
+
return v if is_blank.call(v)
|
168
|
+
v.to_f
|
169
|
+
}
|
170
|
+
when :String
|
171
|
+
lambda{|v|
|
172
|
+
return v if is_blank.call(v)
|
173
|
+
v.to_s
|
174
|
+
}
|
175
|
+
when :Symbol
|
176
|
+
lambda{|v|
|
177
|
+
return v if is_blank.call(v)
|
178
|
+
v.respond_to?(:to_sym) ? v.to_sym : "#{v}".to_sym
|
179
|
+
}
|
180
|
+
when :FalseClass, :TrueClass
|
181
|
+
lambda{|v|
|
182
|
+
return v if is_blank.call(v)
|
183
|
+
v.nil? ? nil : !!(v)
|
184
|
+
}
|
185
|
+
when :NilClass then Proc.new{|v| nil}
|
186
|
+
## when :Object then Proc.new{|v| v} # unknown type
|
187
|
+
## Proc.new{|v| raise "hell"} # sanity
|
188
|
+
else
|
189
|
+
if self.undefined_type?(klass) then Proc.new{|v| v}
|
190
|
+
else
|
191
|
+
lambda do |v|
|
192
|
+
return v if is_blank.call(v)
|
193
|
+
v = new_proc.call if v.nil?
|
194
|
+
return v if CascadingClasses.is_of_type?(v, klass)
|
195
|
+
raise "error: don't know how to convert #{v} to object of type #{klass}"
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def self.is_container?(obj, blk=nil)
|
202
|
+
obj = unsymbolize(obj) if Symbol === obj
|
203
|
+
return blk.call(obj) if blk
|
204
|
+
s = (obj.class == Class) ? obj : obj.class
|
205
|
+
s.ancestors.include?(Enumerable) or
|
206
|
+
s.instance_methods.include?(:each)
|
207
|
+
end
|
208
|
+
|
209
|
+
def self.is_hash_like?(obj, blk=nil)
|
210
|
+
obj = unsymbolize(obj) if Symbol === obj
|
211
|
+
return blk.call(obj) if blk
|
212
|
+
s = (obj.class == Class) ? obj : obj.class
|
213
|
+
return true if s.ancestors.include?(Hash)
|
214
|
+
meths = s.instance_methods
|
215
|
+
meths.include?(:keys) and meths.include?(:include?) and
|
216
|
+
(meths.include?(:[]) or meths.include?(:fetch))
|
217
|
+
end
|
218
|
+
|
219
|
+
def self.is_array_like?(obj, blk=nil)
|
220
|
+
obj = unsymbolize(obj) if Symbol === obj
|
221
|
+
return blk.call(obj) if blk
|
222
|
+
s = (obj.class == Class) ? obj : obj.class
|
223
|
+
return true if s.ancestors.include?(Array)
|
224
|
+
meths = s.instance_methods
|
225
|
+
meths.include?(:index) and meths.include?(:at) and
|
226
|
+
meths.include?(:include?)
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|