facets 2.7.0 → 2.8.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/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
|