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