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.
Files changed (130) hide show
  1. data/HISTORY.rdoc +135 -294
  2. data/MANIFEST +40 -91
  3. data/NOTES +1 -1
  4. data/README.rdoc +10 -8
  5. data/Rakefile +11 -34
  6. data/demo/{hook.rd → hook.rdoc} +2 -0
  7. data/demo/{scenario_require.rd → scenario_require.rdoc} +3 -0
  8. data/lib/core/facets-live.rb +7 -5
  9. data/lib/core/facets.rb +379 -359
  10. data/lib/core/facets/array/conjoin.rb +2 -2
  11. data/lib/core/facets/array/pad.rb +1 -1
  12. data/lib/core/facets/array/recursively.rb +2 -2
  13. data/lib/core/facets/array/splice.rb +1 -1
  14. data/lib/core/facets/binding/caller.rb +2 -4
  15. data/lib/core/facets/comparable/comparable.rb +2 -2
  16. data/lib/core/facets/dir/ascend.rb +3 -0
  17. data/lib/core/facets/dir/recurse.rb +4 -0
  18. data/lib/core/facets/duplicable.rb +6 -8
  19. data/lib/core/facets/enumerable/count.rb +22 -13
  20. data/lib/core/facets/enumerable/map_detect.rb +28 -0
  21. data/lib/core/facets/enumerable/mash.rb +13 -5
  22. data/lib/core/facets/enumerable/per.rb +3 -1
  23. data/lib/core/facets/hash/count.rb +14 -0
  24. data/lib/core/facets/hash/data.rb +14 -0
  25. data/lib/core/facets/kernel/__method__.rb +1 -1
  26. data/lib/core/facets/kernel/d.rb +9 -8
  27. data/lib/core/facets/kernel/eigenclass.rb +20 -0
  28. data/lib/core/facets/kernel/extend.rb +10 -0
  29. data/lib/core/facets/kernel/instance_class.rb +1 -0
  30. data/lib/core/facets/kernel/instance_variables.rb +6 -6
  31. data/lib/core/facets/kernel/meta_alias.rb +18 -0
  32. data/lib/core/facets/kernel/meta_class.rb +17 -0
  33. data/lib/core/facets/kernel/meta_def.rb +18 -0
  34. data/lib/core/facets/kernel/meta_eval.rb +18 -0
  35. data/lib/core/facets/kernel/object_hexid.rb +21 -6
  36. data/lib/core/facets/kernel/object_state.rb +4 -2
  37. data/lib/core/facets/kernel/populate.rb +3 -1
  38. data/lib/core/facets/kernel/with.rb +1 -1
  39. data/lib/core/facets/metaid.rb +6 -93
  40. data/lib/core/facets/module/class_def.rb +2 -0
  41. data/lib/core/facets/module/extend.rb +10 -11
  42. data/lib/core/facets/module/is.rb +5 -5
  43. data/lib/core/facets/module/module_def.rb +31 -0
  44. data/lib/core/facets/string/camelcase.rb +14 -12
  45. data/lib/core/facets/string/cleanlines.rb +35 -0
  46. data/lib/core/facets/string/edit_distance.rb +62 -0
  47. data/lib/core/facets/string/indent.rb +86 -4
  48. data/lib/core/facets/string/index_all.rb +24 -0
  49. data/lib/core/facets/string/lines.rb +3 -6
  50. data/lib/core/facets/string/margin.rb +2 -1
  51. data/lib/core/facets/string/newlines.rb +35 -0
  52. data/lib/core/facets/string/op_div.rb +14 -0
  53. data/lib/core/facets/string/range.rb +2 -22
  54. data/lib/core/facets/string/range_all.rb +1 -0
  55. data/lib/core/facets/string/range_of_line.rb +1 -0
  56. data/lib/core/facets/string/similarity.rb +92 -0
  57. data/lib/core/facets/string/start_with.rb +6 -6
  58. data/lib/core/facets/string/titlecase.rb +1 -1
  59. data/lib/more/facets/basicobject.rb +16 -15
  60. data/lib/more/facets/blankslate.rb +8 -0
  61. data/lib/more/facets/class_extend.rb +126 -1
  62. data/lib/more/facets/continuation.rb +53 -54
  63. data/lib/more/facets/dictionary.rb +9 -63
  64. data/lib/more/facets/erb.rb +63 -0
  65. data/lib/more/facets/filelist.rb +5 -5
  66. data/lib/more/facets/hashbuilder.rb +101 -0
  67. data/lib/more/facets/inheritor.rb +36 -45
  68. data/lib/more/facets/ini.rb +267 -0
  69. data/lib/more/facets/instance_eval.rb +4 -4
  70. data/lib/more/facets/ioredirect.rb +7 -60
  71. data/lib/more/facets/linkedlist.rb +195 -0
  72. data/lib/more/facets/matcher.rb +140 -0
  73. data/lib/more/facets/memoizer.rb +64 -0
  74. data/lib/more/facets/methodspace.rb +9 -4
  75. data/lib/more/facets/module/class_extend.rb +2 -121
  76. data/lib/more/facets/ostruct.rb +9 -9
  77. data/lib/more/facets/pathlist.rb +1 -9
  78. data/lib/more/facets/pathname.rb +11 -4
  79. data/lib/more/facets/plugin_manager.rb +50 -0
  80. data/lib/more/facets/random.rb +25 -3
  81. data/lib/more/facets/roman.rb +174 -0
  82. data/lib/more/facets/semaphore.rb +92 -0
  83. data/lib/more/facets/shellwords.rb +21 -48
  84. data/lib/more/facets/succ.rb +1 -1
  85. data/meta/{modified → released} +0 -0
  86. data/meta/repository +1 -0
  87. data/meta/suite +1 -0
  88. data/meta/version +1 -1
  89. data/script/conflicts +63 -0
  90. data/script/methods +49 -0
  91. data/test/core/binding/test_caller.rb +11 -4
  92. data/test/core/enumerable/test_count.rb +19 -10
  93. data/test/core/enumerable/test_map_detect.rb +75 -0
  94. data/test/core/enumerable/test_take.rb +1 -1
  95. data/test/core/kernel/test_object_hexid.rb +2 -1
  96. data/test/core/proc/test_to_method.rb +1 -1
  97. data/test/core/string/test_cleanlines.rb +11 -0
  98. data/test/core/string/test_indent.rb +66 -4
  99. data/test/core/string/test_lines.rb +2 -1
  100. data/test/core/string/test_newlines.rb +13 -0
  101. data/test/core/time/test_change.rb +1 -1
  102. data/test/core/time/test_stamp.rb +4 -7
  103. data/test/core/unboundmethod/test_name.rb +1 -1
  104. data/test/more/test_basicobject.rb +1 -20
  105. data/test/more/test_class_extend.rb +7 -0
  106. data/test/more/test_continuation.rb +8 -6
  107. data/test/more/test_inheritor.rb +12 -6
  108. data/test/more/test_random.rb +19 -10
  109. data/test/more/test_shellwords.rb +33 -0
  110. metadata +60 -31
  111. data/TODO +0 -5
  112. data/doc/README.core +0 -102
  113. data/doc/README.more +0 -61
  114. data/doc/manual/about.rb +0 -47
  115. data/doc/manual/annotations.rdoc +0 -60
  116. data/doc/manual/associations.rdoc +0 -55
  117. data/doc/manual/blockups.rdoc +0 -101
  118. data/doc/manual/capsule.rdoc +0 -34
  119. data/doc/manual/command.rdoc +0 -177
  120. data/doc/manual/core.rdoc +0 -37
  121. data/doc/manual/faq.rdoc +0 -32
  122. data/doc/manual/typecast.html +0 -112
  123. data/lib/more/facets/capsule.rb +0 -258
  124. data/lib/more/facets/coroutine.rb +0 -159
  125. data/lib/more/facets/enumerablepass.rb +0 -3
  126. data/lib/more/facets/fileable.rb +0 -162
  127. data/lib/more/facets/progressbar.rb +0 -253
  128. data/lib/more/facets/recorder.rb +0 -108
  129. data/meta/releases +0 -14
  130. data/test/more/test_coroutine.rb +0 -46
@@ -1,57 +1,56 @@
1
- # = Continuation Extension
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
- # == Usage Notes
3
+ # Copyright (c) 2005, 2009 Jan Molic, Thomas Sawyer
38
4
  #
39
- # * You can use #order_by to set internal sort order.
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
- # == Authors
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
- # * Jan Molic
47
- # * Thomas Sawyer
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
- # Originally ported from OrderHash 2.0, Copyright (c) 2005 jan molic
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
+
@@ -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( ['testfile.txt', 'testfile2.txt'], fl.to_a )
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
- # the class heirarchy. An inheritor creates two methods
33
- # one named after the key that provides a reader. And one
34
- # named after key! which provides the writer. (Because of
35
- # the unique nature of inheritor the reader and writer
36
- # can't be the same method.)
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 at all
54
- # is becuase Ruby does not allow modules to be "inherited" at the class-level,
55
- # or conversely that the class-level is not a module instead.
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
- def inheritor(key, obj, op=nil)
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
- # inhertiance operator
61
- op = op ? op.to_sym : :add #NOTE: why #add ?
67
+ def inheritor(key, obj, op=nil, &fop)
68
+ raise ArgumentError if op && fop
62
69
 
63
- # inheritor store a this level
64
- instance_variable_set("@#{key}", obj)
70
+ if !fop
71
+ op = op ? op.to_sym : :+
72
+ fop = lambda{ |o, x| o.__send__(op, x) }
73
+ end
65
74
 
66
- #base = self
67
- deflambda = lambda do
75
+ #(class << self; self; end).module_eval do
76
+ class_extend do
68
77
 
69
- define_method( key ) do
70
- defined?(super) ? super.__send__(op,obj) : obj.dup
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( "#{key}!" ) do
88
+ define_method("#{key}!") do
74
89
  if instance_variable_defined?("@#{key}")
75
90
  instance_variable_get("@#{key}")
76
91
  else
77
- inheritor(key, obj.class.new, op)
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