facets 2.7.0 → 2.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY.rdoc +135 -294
- data/MANIFEST +40 -91
- data/NOTES +1 -1
- data/README.rdoc +10 -8
- data/Rakefile +11 -34
- data/demo/{hook.rd → hook.rdoc} +2 -0
- data/demo/{scenario_require.rd → scenario_require.rdoc} +3 -0
- data/lib/core/facets-live.rb +7 -5
- data/lib/core/facets.rb +379 -359
- data/lib/core/facets/array/conjoin.rb +2 -2
- data/lib/core/facets/array/pad.rb +1 -1
- data/lib/core/facets/array/recursively.rb +2 -2
- data/lib/core/facets/array/splice.rb +1 -1
- data/lib/core/facets/binding/caller.rb +2 -4
- data/lib/core/facets/comparable/comparable.rb +2 -2
- data/lib/core/facets/dir/ascend.rb +3 -0
- data/lib/core/facets/dir/recurse.rb +4 -0
- data/lib/core/facets/duplicable.rb +6 -8
- data/lib/core/facets/enumerable/count.rb +22 -13
- data/lib/core/facets/enumerable/map_detect.rb +28 -0
- data/lib/core/facets/enumerable/mash.rb +13 -5
- data/lib/core/facets/enumerable/per.rb +3 -1
- data/lib/core/facets/hash/count.rb +14 -0
- data/lib/core/facets/hash/data.rb +14 -0
- data/lib/core/facets/kernel/__method__.rb +1 -1
- data/lib/core/facets/kernel/d.rb +9 -8
- data/lib/core/facets/kernel/eigenclass.rb +20 -0
- data/lib/core/facets/kernel/extend.rb +10 -0
- data/lib/core/facets/kernel/instance_class.rb +1 -0
- data/lib/core/facets/kernel/instance_variables.rb +6 -6
- data/lib/core/facets/kernel/meta_alias.rb +18 -0
- data/lib/core/facets/kernel/meta_class.rb +17 -0
- data/lib/core/facets/kernel/meta_def.rb +18 -0
- data/lib/core/facets/kernel/meta_eval.rb +18 -0
- data/lib/core/facets/kernel/object_hexid.rb +21 -6
- data/lib/core/facets/kernel/object_state.rb +4 -2
- data/lib/core/facets/kernel/populate.rb +3 -1
- data/lib/core/facets/kernel/with.rb +1 -1
- data/lib/core/facets/metaid.rb +6 -93
- data/lib/core/facets/module/class_def.rb +2 -0
- data/lib/core/facets/module/extend.rb +10 -11
- data/lib/core/facets/module/is.rb +5 -5
- data/lib/core/facets/module/module_def.rb +31 -0
- data/lib/core/facets/string/camelcase.rb +14 -12
- data/lib/core/facets/string/cleanlines.rb +35 -0
- data/lib/core/facets/string/edit_distance.rb +62 -0
- data/lib/core/facets/string/indent.rb +86 -4
- data/lib/core/facets/string/index_all.rb +24 -0
- data/lib/core/facets/string/lines.rb +3 -6
- data/lib/core/facets/string/margin.rb +2 -1
- data/lib/core/facets/string/newlines.rb +35 -0
- data/lib/core/facets/string/op_div.rb +14 -0
- data/lib/core/facets/string/range.rb +2 -22
- data/lib/core/facets/string/range_all.rb +1 -0
- data/lib/core/facets/string/range_of_line.rb +1 -0
- data/lib/core/facets/string/similarity.rb +92 -0
- data/lib/core/facets/string/start_with.rb +6 -6
- data/lib/core/facets/string/titlecase.rb +1 -1
- data/lib/more/facets/basicobject.rb +16 -15
- data/lib/more/facets/blankslate.rb +8 -0
- data/lib/more/facets/class_extend.rb +126 -1
- data/lib/more/facets/continuation.rb +53 -54
- data/lib/more/facets/dictionary.rb +9 -63
- data/lib/more/facets/erb.rb +63 -0
- data/lib/more/facets/filelist.rb +5 -5
- data/lib/more/facets/hashbuilder.rb +101 -0
- data/lib/more/facets/inheritor.rb +36 -45
- data/lib/more/facets/ini.rb +267 -0
- data/lib/more/facets/instance_eval.rb +4 -4
- data/lib/more/facets/ioredirect.rb +7 -60
- data/lib/more/facets/linkedlist.rb +195 -0
- data/lib/more/facets/matcher.rb +140 -0
- data/lib/more/facets/memoizer.rb +64 -0
- data/lib/more/facets/methodspace.rb +9 -4
- data/lib/more/facets/module/class_extend.rb +2 -121
- data/lib/more/facets/ostruct.rb +9 -9
- data/lib/more/facets/pathlist.rb +1 -9
- data/lib/more/facets/pathname.rb +11 -4
- data/lib/more/facets/plugin_manager.rb +50 -0
- data/lib/more/facets/random.rb +25 -3
- data/lib/more/facets/roman.rb +174 -0
- data/lib/more/facets/semaphore.rb +92 -0
- data/lib/more/facets/shellwords.rb +21 -48
- data/lib/more/facets/succ.rb +1 -1
- data/meta/{modified → released} +0 -0
- data/meta/repository +1 -0
- data/meta/suite +1 -0
- data/meta/version +1 -1
- data/script/conflicts +63 -0
- data/script/methods +49 -0
- data/test/core/binding/test_caller.rb +11 -4
- data/test/core/enumerable/test_count.rb +19 -10
- data/test/core/enumerable/test_map_detect.rb +75 -0
- data/test/core/enumerable/test_take.rb +1 -1
- data/test/core/kernel/test_object_hexid.rb +2 -1
- data/test/core/proc/test_to_method.rb +1 -1
- data/test/core/string/test_cleanlines.rb +11 -0
- data/test/core/string/test_indent.rb +66 -4
- data/test/core/string/test_lines.rb +2 -1
- data/test/core/string/test_newlines.rb +13 -0
- data/test/core/time/test_change.rb +1 -1
- data/test/core/time/test_stamp.rb +4 -7
- data/test/core/unboundmethod/test_name.rb +1 -1
- data/test/more/test_basicobject.rb +1 -20
- data/test/more/test_class_extend.rb +7 -0
- data/test/more/test_continuation.rb +8 -6
- data/test/more/test_inheritor.rb +12 -6
- data/test/more/test_random.rb +19 -10
- data/test/more/test_shellwords.rb +33 -0
- metadata +60 -31
- data/TODO +0 -5
- data/doc/README.core +0 -102
- data/doc/README.more +0 -61
- data/doc/manual/about.rb +0 -47
- data/doc/manual/annotations.rdoc +0 -60
- data/doc/manual/associations.rdoc +0 -55
- data/doc/manual/blockups.rdoc +0 -101
- data/doc/manual/capsule.rdoc +0 -34
- data/doc/manual/command.rdoc +0 -177
- data/doc/manual/core.rdoc +0 -37
- data/doc/manual/faq.rdoc +0 -32
- data/doc/manual/typecast.html +0 -112
- data/lib/more/facets/capsule.rb +0 -258
- data/lib/more/facets/coroutine.rb +0 -159
- data/lib/more/facets/enumerablepass.rb +0 -3
- data/lib/more/facets/fileable.rb +0 -162
- data/lib/more/facets/progressbar.rb +0 -253
- data/lib/more/facets/recorder.rb +0 -108
- data/meta/releases +0 -14
- data/test/more/test_coroutine.rb +0 -46
@@ -1,57 +1,56 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
# Creates a continuation in a way that is easier to use than callcc.
|
4
|
-
# On the initial call this will return the created Continuation and
|
5
|
-
# the arguments you gave to Continuation.create in an Array. If you
|
6
|
-
# then issue .call() on the Continuation execution will jump back to
|
7
|
-
# the point of time where you initially invoked Continuation.create,
|
8
|
-
# but this time it will return the Continuation and the arguments
|
9
|
-
# you supplied in an Array.
|
10
|
-
#
|
11
|
-
# You can supply a block instead of default arguments which will
|
12
|
-
# cause that block to be executed once and its result to be returned
|
13
|
-
# along side the created Continuation, but this form is confusing
|
14
|
-
# and does only rarely make sense.
|
15
|
-
#
|
16
|
-
# # Count from 0 to 10
|
17
|
-
# cc, counter = Continuation.create(0)
|
18
|
-
# puts counter
|
19
|
-
# cc.call(counter + 1) if counter < 10
|
20
|
-
#
|
21
|
-
# # Implement something similar to Array#inject using Continuations.
|
22
|
-
# # For simplicity's sake, this is not fully compatible with the real
|
23
|
-
# # inject. Make sure that you understand Array#inject before you try
|
24
|
-
# # to understand this.
|
25
|
-
# class Array
|
26
|
-
# def cc_inject(value = nil)
|
27
|
-
# copy = self.clone
|
28
|
-
# cc, result, item = Continuation.create(value, nil)
|
29
|
-
# next_item = copy.shift
|
30
|
-
# if result and item
|
31
|
-
# # Aggregate the result using the block.
|
32
|
-
# cc.call(yield(result, item), next_item)
|
33
|
-
# elsif next_item
|
34
|
-
# # item not yet set and Array is not empty:
|
35
|
-
# # This means we did not get a value and thus need to use the
|
36
|
-
# # first item from the Array before we can start using the
|
37
|
-
# # block to aggregate the result.
|
38
|
-
# cc.call(next_item, result)
|
39
|
-
# end
|
40
|
-
#
|
41
|
-
# return result
|
42
|
-
# end
|
43
|
-
# end
|
44
|
-
# [1,2,3,4,5].cc_inject { |acc, n| acc + n } # => 15
|
45
|
-
#
|
1
|
+
if defined?(Continuation)
|
46
2
|
|
3
|
+
# = Continuation Extension
|
4
|
+
#
|
5
|
+
# Creates a continuation in a way that is easier to use than callcc.
|
6
|
+
# On the initial call this will return the created Continuation and
|
7
|
+
# the arguments you gave to Continuation.create in an Array. If you
|
8
|
+
# then issue .call() on the Continuation execution will jump back to
|
9
|
+
# the point of time where you initially invoked Continuation.create,
|
10
|
+
# but this time it will return the Continuation and the arguments
|
11
|
+
# you supplied in an Array.
|
12
|
+
#
|
13
|
+
# You can supply a block instead of default arguments which will
|
14
|
+
# cause that block to be executed once and its result to be returned
|
15
|
+
# along side the created Continuation, but this form is confusing
|
16
|
+
# and does only rarely make sense.
|
17
|
+
#
|
18
|
+
# # Count from 0 to 10
|
19
|
+
# cc, counter = Continuation.create(0)
|
20
|
+
# puts counter
|
21
|
+
# cc.call(counter + 1) if counter < 10
|
22
|
+
#
|
23
|
+
# # Implement something similar to Array#inject using Continuations.
|
24
|
+
# # For simplicity's sake, this is not fully compatible with the real
|
25
|
+
# # inject. Make sure that you understand Array#inject before you try
|
26
|
+
# # to understand this.
|
27
|
+
# class Array
|
28
|
+
# def cc_inject(value = nil)
|
29
|
+
# copy = self.clone
|
30
|
+
# cc, result, item = Continuation.create(value, nil)
|
31
|
+
# next_item = copy.shift
|
32
|
+
# if result and item
|
33
|
+
# # Aggregate the result using the block.
|
34
|
+
# cc.call(yield(result, item), next_item)
|
35
|
+
# elsif next_item
|
36
|
+
# # item not yet set and Array is not empty:
|
37
|
+
# # This means we did not get a value and thus need to use the
|
38
|
+
# # first item from the Array before we can start using the
|
39
|
+
# # block to aggregate the result.
|
40
|
+
# cc.call(next_item, result)
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# return result
|
44
|
+
# end
|
45
|
+
# end
|
46
|
+
# [1,2,3,4,5].cc_inject { |acc, n| acc + n } # => 15
|
47
|
+
#
|
48
|
+
def Continuation.create(*args, &block)
|
49
|
+
args = [args] if not args.nil? and not args.is_a? Array # 1.6.8 compatibility
|
50
|
+
cc = nil; result = callcc {|c| cc = c; block.call(cc) if block and args.empty?}
|
51
|
+
result ||= args
|
52
|
+
return *[cc, *result]
|
53
|
+
end
|
47
54
|
|
48
|
-
# There is no continuation in Ruby 1.9.
|
49
|
-
|
50
|
-
raise "Ruby 1.9+ does not support continuations." unless Continuation
|
51
|
-
|
52
|
-
def Continuation.create(*args, &block)
|
53
|
-
args = [args] if not args.nil? and not args.is_a? Array # 1.6.8 compatibility
|
54
|
-
cc = nil; result = callcc {|c| cc = c; block.call(cc) if block and args.empty?}
|
55
|
-
result ||= args
|
56
|
-
return *[cc, *result]
|
57
55
|
end
|
56
|
+
|
@@ -1,50 +1,15 @@
|
|
1
|
-
# = Dictionary
|
2
|
-
#
|
3
|
-
# The Dictionary class is a Hash that preserves order.
|
4
|
-
# So it has some array-like extensions also. By defualt
|
5
|
-
# a Dictionary object preserves insertion order, but any
|
6
|
-
# order can be specified including alphabetical key order.
|
7
|
-
#
|
8
|
-
# == Usage
|
9
|
-
#
|
10
|
-
# Just require this file and use Dictionary instead of Hash.
|
11
|
-
#
|
12
|
-
# # You can do simply
|
13
|
-
# hsh = Dictionary.new
|
14
|
-
# hsh['z'] = 1
|
15
|
-
# hsh['a'] = 2
|
16
|
-
# hsh['c'] = 3
|
17
|
-
# p hsh.keys #=> ['z','a','c']
|
18
|
-
#
|
19
|
-
# # or using Dictionary[] method
|
20
|
-
# hsh = Dictionary['z', 1, 'a', 2, 'c', 3]
|
21
|
-
# p hsh.keys #=> ['z','a','c']
|
22
|
-
#
|
23
|
-
# # but this don't preserve order
|
24
|
-
# hsh = Dictionary['z'=>1, 'a'=>2, 'c'=>3]
|
25
|
-
# p hsh.keys #=> ['a','c','z']
|
26
|
-
#
|
27
|
-
# # Dictionary has useful extensions: push, pop and unshift
|
28
|
-
# p hsh.push('to_end', 15) #=> true, key added
|
29
|
-
# p hsh.push('to_end', 30) #=> false, already - nothing happen
|
30
|
-
# p hsh.unshift('to_begin', 50) #=> true, key added
|
31
|
-
# p hsh.unshift('to_begin', 60) #=> false, already - nothing happen
|
32
|
-
# p hsh.keys #=> ["to_begin", "a", "c", "z", "to_end"]
|
33
|
-
# p hsh.pop #=> ["to_end", 15], if nothing remains, return nil
|
34
|
-
# p hsh.keys #=> ["to_begin", "a", "c", "z"]
|
35
|
-
# p hsh.shift #=> ["to_begin", 30], if nothing remains, return nil
|
1
|
+
# = Dictionary.rb
|
36
2
|
#
|
37
|
-
#
|
3
|
+
# Copyright (c) 2005, 2009 Jan Molic, Thomas Sawyer
|
38
4
|
#
|
39
|
-
#
|
40
|
-
# * #<< takes a two element [k,v] array and inserts.
|
41
|
-
# * Use ::auto which creates Dictionay sub-entries as needed.
|
42
|
-
# * And ::alpha which creates a new Dictionary sorted by key.
|
5
|
+
# Ruby License
|
43
6
|
#
|
44
|
-
#
|
7
|
+
# This module is free software. You may use, modify, and/or redistribute this
|
8
|
+
# software under the same terms as Ruby.
|
45
9
|
#
|
46
|
-
#
|
47
|
-
#
|
10
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
11
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
12
|
+
# FOR A PARTICULAR PURPOSE.
|
48
13
|
#
|
49
14
|
# == Acknowledgments
|
50
15
|
#
|
@@ -52,26 +17,7 @@
|
|
52
17
|
# * Jeff Sharpe (reverse and reverse!)
|
53
18
|
# * Thomas Leitner (has_key? and key?)
|
54
19
|
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
# == History
|
58
|
-
#
|
59
|
-
# * 2007.10.31 trans
|
60
|
-
# ** Fixed initialize so the constructor blocks correctly effected dictionary rather then just the internal hash.
|
61
|
-
#
|
62
|
-
# == Copying
|
63
|
-
#
|
64
|
-
# Copyright (c) 2005 Jan Molic, Thomas Sawyer
|
65
|
-
#
|
66
|
-
# Ruby License
|
67
|
-
#
|
68
|
-
# This module is free software. You may use, modify, and/or redistribute this
|
69
|
-
# software under the same terms as Ruby.
|
70
|
-
#
|
71
|
-
# This program is distributed in the hope that it will be useful, but WITHOUT
|
72
|
-
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
73
|
-
# FOR A PARTICULAR PURPOSE.
|
74
|
-
|
20
|
+
# Ported from OrderHash 2.0, Copyright (c) 2005 Jan Molic
|
75
21
|
|
76
22
|
# = Dictionary
|
77
23
|
#
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
# = OpenTemplate
|
4
|
+
#
|
5
|
+
# The Erb OpenTemplate provides a quick and convenient way to
|
6
|
+
# create a clean rendering space with the desired responses.
|
7
|
+
#
|
8
|
+
# TODO: This might make a good addon library. Just add
|
9
|
+
# require 'erb' to the erb_result method? Call it OpenResponse?
|
10
|
+
#
|
11
|
+
class ERB::OpenTemplate
|
12
|
+
|
13
|
+
# TODO: Should we do this? Perhaps offer it as an option?
|
14
|
+
instance_methods.each do |m|
|
15
|
+
undef_method(m) unless /^(__|instance_|inspect$|extend$)/ =~ m.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
#
|
19
|
+
def initialize(*objs_ioc)
|
20
|
+
ioc = Hash===objs_ioc.last ? objs_ioc.pop : {}
|
21
|
+
objs = objs_ioc
|
22
|
+
|
23
|
+
mods = []
|
24
|
+
|
25
|
+
objs.each do |obj|
|
26
|
+
mod = Module.new
|
27
|
+
obj.public_methods.each do |m|
|
28
|
+
mod.module_eval do
|
29
|
+
define_method(m){ |*a,&b| obj.__send__(m,*a,&b) }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
mods << mod
|
33
|
+
end
|
34
|
+
|
35
|
+
mod = Module.new
|
36
|
+
ioc.each do |k,v|
|
37
|
+
mod.module_eval do
|
38
|
+
define_method(k){ v }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
mods << mod
|
42
|
+
|
43
|
+
extend *mods.reverse
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
def erb_result(str)
|
48
|
+
ERB.new(str).result(binding)
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
#def method_missing(sym, *args, &block)
|
53
|
+
# #if @ioc.key?(sym)
|
54
|
+
# # @ioc[sym]
|
55
|
+
# if obj = @objs.find{ |o| o.respond_to?(sym) }
|
56
|
+
# obj.__send__(sym, *args, &block)
|
57
|
+
# else
|
58
|
+
# super
|
59
|
+
# end
|
60
|
+
#end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
data/lib/more/facets/filelist.rb
CHANGED
@@ -113,14 +113,14 @@ class FileList
|
|
113
113
|
|
114
114
|
# List of array methods (that are not in +Object+) that need to be
|
115
115
|
# delegated.
|
116
|
-
ARRAY_METHODS = Array.instance_methods - Object.instance_methods
|
116
|
+
ARRAY_METHODS = (Array.instance_methods - Object.instance_methods).map(&:to_sym)
|
117
117
|
|
118
118
|
# List of additional methods that must be delegated.
|
119
|
-
MUST_DEFINE = %w[to_a inspect]
|
119
|
+
MUST_DEFINE = %w[to_a inspect].map(&:to_sym)
|
120
120
|
|
121
121
|
# List of methods that should not be delegated here (we define
|
122
122
|
# special versions of them explicitly below).
|
123
|
-
MUST_NOT_DEFINE = %w[to_a to_ary partition *]
|
123
|
+
MUST_NOT_DEFINE = %w[to_a to_ary partition *].map(&:to_sym)
|
124
124
|
|
125
125
|
# List of delegated methods that return new array values which
|
126
126
|
# need wrapping.
|
@@ -128,7 +128,7 @@ class FileList
|
|
128
128
|
map collect sort sort_by select find_all reject grep
|
129
129
|
compact flatten uniq values_at
|
130
130
|
+ - & |
|
131
|
-
]
|
131
|
+
].map(&:to_sym)
|
132
132
|
|
133
133
|
DELEGATING_METHODS = (ARRAY_METHODS + MUST_DEFINE - MUST_NOT_DEFINE).sort.uniq
|
134
134
|
|
@@ -486,7 +486,7 @@ end # FileList
|
|
486
486
|
Dir.chdir File.join( $TESTDIR, 'filelist' ) do
|
487
487
|
fl = FileList.new
|
488
488
|
fl.include('*')
|
489
|
-
assert_equal( ['
|
489
|
+
assert_equal( ['testfile2.txt', 'testfile.txt'], fl.to_a )
|
490
490
|
fl.exclude('*2.txt')
|
491
491
|
assert_equal( ['testfile.txt'], fl.to_a )
|
492
492
|
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# TITLE:
|
2
|
+
#
|
3
|
+
# HashBuilder
|
4
|
+
#
|
5
|
+
# DESCRIPTION:
|
6
|
+
#
|
7
|
+
# Build a hash programmatically via missing method calls.
|
8
|
+
#
|
9
|
+
# COPYRIGHT:
|
10
|
+
#
|
11
|
+
# Copyright (c) 2006 Thomas Sawyer
|
12
|
+
#
|
13
|
+
# LICENSE:
|
14
|
+
#
|
15
|
+
# Ruby License
|
16
|
+
#
|
17
|
+
# This module is free software. You may use, modify, and/or redistribute this
|
18
|
+
# software under the same terms as Ruby.
|
19
|
+
#
|
20
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
21
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
22
|
+
# FOR A PARTICULAR PURPOSE.
|
23
|
+
#
|
24
|
+
# AUTHORS:
|
25
|
+
#
|
26
|
+
# - Thomas Sawyer
|
27
|
+
#
|
28
|
+
# TODO:
|
29
|
+
#
|
30
|
+
# - Use BuildingBlock ?
|
31
|
+
|
32
|
+
|
33
|
+
# = HashBuilder
|
34
|
+
#
|
35
|
+
# Build a hash programmatically via a fluent DSL-like syntax.
|
36
|
+
# In other words, this uses #method_missing to buld the hash.
|
37
|
+
|
38
|
+
class HashBuilder
|
39
|
+
|
40
|
+
# Privatize a few Kernel methods that are most likely to clash,
|
41
|
+
# but arn't essential here. TODO Maybe more in this context?
|
42
|
+
|
43
|
+
alias :__class__ :class
|
44
|
+
|
45
|
+
private *instance_methods.select{ |m| /^__/ !~ m.to_s }
|
46
|
+
|
47
|
+
def self.load( blockstr, &block )
|
48
|
+
new( blockstr, &block ).to_h
|
49
|
+
end
|
50
|
+
|
51
|
+
#def self.build( blockstr=nil, &block )
|
52
|
+
# self.new.build( blockstr, &block ).to_h
|
53
|
+
#end
|
54
|
+
|
55
|
+
def initialize( blockstr=nil, &block )
|
56
|
+
@hash = {}
|
57
|
+
@flag = {}
|
58
|
+
build!( blockstr, &block )
|
59
|
+
end
|
60
|
+
|
61
|
+
def build!( blockstr=nil, &block )
|
62
|
+
raise "both string and block given" if blockstr and block_given?
|
63
|
+
if blockstr
|
64
|
+
instance_eval blockstr
|
65
|
+
else
|
66
|
+
instance_eval &block
|
67
|
+
end
|
68
|
+
self # or to_h ?
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_h ; @hash ; end
|
72
|
+
|
73
|
+
def method_missing( sym, *args, &block )
|
74
|
+
sym = sym.to_s.downcase.chomp('=')
|
75
|
+
|
76
|
+
if @hash.key?(sym)
|
77
|
+
unless @flag[sym]
|
78
|
+
@hash[sym] = [ @hash[sym] ]
|
79
|
+
@flag[sym] = true
|
80
|
+
end
|
81
|
+
if block_given?
|
82
|
+
@hash[sym] << self.__class__.new( &block ).to_h
|
83
|
+
else
|
84
|
+
@hash[sym] << args[0]
|
85
|
+
end
|
86
|
+
else
|
87
|
+
if block_given?
|
88
|
+
@hash[sym] = self.__class__.new( &block ).to_h
|
89
|
+
else
|
90
|
+
@hash[sym] = args[0]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
# Author:: Thomas Sawyer
|
100
|
+
# Copyright:: Copyright (c) 2006 Thomas Sawyer
|
101
|
+
# License:: Ruby License
|
@@ -28,12 +28,16 @@ class Object
|
|
28
28
|
#
|
29
29
|
# Create an inheritor "class attribute".
|
30
30
|
#
|
31
|
-
# Inheritor providse a means to store and inherit data via
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
31
|
+
# Inheritor providse a means to store and inherit data via the class
|
32
|
+
# heirarchy. An inheritor creates two methods one named after the key
|
33
|
+
# that provides a reader. And one named after key! which provides the
|
34
|
+
# writer. (Because of the unique nature of inheritor the reader and
|
35
|
+
# writer can't be the same method.)
|
36
|
+
#
|
37
|
+
# The first argument is the inheritor's name. The second argument
|
38
|
+
# is the archtype object. This object must be duplicable (via #dup).
|
39
|
+
# The last argument is either the symbolic operator/method or a block
|
40
|
+
# that specifies how one hierarchical level "integrates" with the next.
|
37
41
|
#
|
38
42
|
# class X
|
39
43
|
# inheritor :foo, [], :+
|
@@ -50,59 +54,46 @@ class Object
|
|
50
54
|
# X.x => [:a]
|
51
55
|
# Y.x => [:a, :b]
|
52
56
|
#
|
53
|
-
# It is interesting to note that the only reason inheritor is needed
|
54
|
-
# is becuase Ruby does not allow modules to be "inherited" at
|
55
|
-
# or conversely that the class-level is not a module
|
56
|
-
# Otherwise using #super at the class-level would suffice.
|
57
|
+
# It is interesting to note that the only reason inheritor is needed
|
58
|
+
# at all is becuase Ruby does not allow modules to be "inherited" at
|
59
|
+
# the class-level, or conversely that the class-level is not a module
|
60
|
+
# instead. Otherwise using #super at the class-level would suffice.
|
57
61
|
#
|
58
|
-
|
62
|
+
# NOTE: Adding an inheritor directly to Module or Class will probably
|
63
|
+
# not do what is expected. Thankfully that usecase is likely a YAGNI,
|
64
|
+
# but in anycase it is even more likely that it is not possible with
|
65
|
+
# this code.
|
59
66
|
|
60
|
-
|
61
|
-
|
67
|
+
def inheritor(key, obj, op=nil, &fop)
|
68
|
+
raise ArgumentError if op && fop
|
62
69
|
|
63
|
-
|
64
|
-
|
70
|
+
if !fop
|
71
|
+
op = op ? op.to_sym : :+
|
72
|
+
fop = lambda{ |o, x| o.__send__(op, x) }
|
73
|
+
end
|
65
74
|
|
66
|
-
#
|
67
|
-
|
75
|
+
#(class << self; self; end).module_eval do
|
76
|
+
class_extend do
|
68
77
|
|
69
|
-
define_method(
|
70
|
-
|
78
|
+
define_method(key) do
|
79
|
+
ancestors.reverse.inject(obj.dup) do |o, a|
|
80
|
+
if a.respond_to?("#{key}!")
|
81
|
+
fop.call(o, a.__send__("#{key}!"))
|
82
|
+
else
|
83
|
+
o
|
84
|
+
end
|
85
|
+
end
|
71
86
|
end
|
72
87
|
|
73
|
-
define_method(
|
88
|
+
define_method("#{key}!") do
|
74
89
|
if instance_variable_defined?("@#{key}")
|
75
90
|
instance_variable_get("@#{key}")
|
76
91
|
else
|
77
|
-
|
92
|
+
instance_variable_set("@#{key}", obj.dup)
|
78
93
|
end
|
79
|
-
# -- old version --
|
80
|
-
#if instance_variables.include?("@#{key}")
|
81
|
-
# instance_variable_get("@#{key}")
|
82
|
-
#else
|
83
|
-
# if self != base
|
84
|
-
# inheritor( key, obj.class.new, op )
|
85
|
-
# end
|
86
|
-
#end
|
87
94
|
end
|
88
|
-
end
|
89
|
-
|
90
|
-
# TODO: This is an issue if you try to include a module
|
91
|
-
# into Module or Class itself. How to fix?
|
92
95
|
|
93
|
-
# if the object is a module (not a class or other object)
|
94
|
-
if self == Class or self == Module
|
95
|
-
class_eval(&deflambda)
|
96
|
-
elsif is_a?(Class)
|
97
|
-
(class << self; self; end).class_eval(&deflambda)
|
98
|
-
elsif is_a?(Module)
|
99
|
-
#class_inherit &deflambda
|
100
|
-
extend class_extend(&deflambda)
|
101
|
-
else # other Object
|
102
|
-
(class << self; self; end).class_eval(&deflambda)
|
103
96
|
end
|
104
|
-
|
105
|
-
obj
|
106
97
|
end
|
107
98
|
|
108
99
|
end
|