facets 2.7.0 → 2.8.0

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