angry_hash 0.2.2 → 0.3.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/Changelog.md CHANGED
@@ -1,13 +1,14 @@
1
1
  ## 0.2.2
2
2
 
3
3
  Changes:
4
+
4
5
  - `#to_normal_hash` can now return a hash with symbol keys. The default is still to use string keys as is default within `AngryHash`.
5
6
 
6
7
  ## 0.2.1
7
8
 
8
9
  Changes:
9
10
 
10
- - In `#merge` & friends `other` hash is now converted to an AngryHash before conversion.
11
+ - In `#merge` & friends `other` hash is now converted to an AngryHash before merging.
11
12
  This ensures subhashes in `other` but not `this` end up as AngryHashes in `this` after merging.
12
13
 
13
14
  ## 0.2.0
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.2
1
+ 0.3.0
data/angry_hash.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{angry_hash}
8
- s.version = "0.2.2"
8
+ s.version = "0.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Lachie Cox"]
12
- s.date = %q{2010-08-30}
12
+ s.date = %q{2010-10-20}
13
13
  s.description = %q{A stabler mash with different emphases. Used in plus2 projects AngryMob and Igor.}
14
14
  s.email = %q{lachie@plus2.com.au}
15
15
  s.extra_rdoc_files = [
@@ -25,9 +25,14 @@ Gem::Specification.new do |s|
25
25
  "VERSION",
26
26
  "angry_hash.gemspec",
27
27
  "lib/angry_hash.rb",
28
+ "lib/angry_hash/conversion/by_reference.rb",
29
+ "lib/angry_hash/conversion/duplicating.rb",
28
30
  "lib/angry_hash/dsl.rb",
29
31
  "lib/angry_hash/extension.rb",
30
- "lib/angry_hash/merge_string.rb"
32
+ "lib/angry_hash/extension_aware.rb",
33
+ "lib/angry_hash/initialiser.rb",
34
+ "lib/angry_hash/merge_string.rb",
35
+ "lib/angry_hash/merging.rb"
31
36
  ]
32
37
  s.homepage = %q{http://github.com/plus2/angry_hash}
33
38
  s.rdoc_options = ["--charset=UTF-8"]
@@ -40,7 +45,7 @@ Gem::Specification.new do |s|
40
45
  "examples/dsl.eg.rb",
41
46
  "examples/dup_eg.rb",
42
47
  "examples/eg_helper.rb",
43
- "examples/extension_tracking.rb",
48
+ "examples/extension_tracking.eg.rb",
44
49
  "examples/merge_eg.rb",
45
50
  "examples/normal_hash.eg.rb"
46
51
  ]
@@ -1,5 +1,5 @@
1
1
  require 'eg_helper'
2
- require 'angry_hash/extension'
2
+ require 'angry_hash'
3
3
 
4
4
  module Fixtures
5
5
  module CreditCard
@@ -104,3 +104,59 @@ eg 'extensions persist with dup_with_extension' do
104
104
  Assert( new_user.credit_card.valid? )
105
105
  Assert( defined?( new_user.credit_card.valid? ) == 'method' )
106
106
  end
107
+
108
+ def same_obj(a,b)
109
+ a.__id__ == b.__id__
110
+ end
111
+
112
+ eg 'extensions persist after deep_merge' do
113
+ user = AngryHash[ :name => 'Bob', :credit_card => {:number => 123}, :phone_numbers => {:home => {:number => '+41 0404555111'}} ]
114
+ user.extend Fixtures::User
115
+
116
+ Assert( user.phone_numbers.home.country_code == 41 )
117
+
118
+ new_user = user.deep_merge(:credit_card => {:number => 456})
119
+
120
+ Assert( new_user.phone_numbers.home.country_code == 41 )
121
+
122
+ Assert( ! same_obj( user.__id__, new_user.__id__ ) )
123
+ Assert( ! same_obj( user.phone_numbers.home.__id__, new_user.phone_numbers.home.__id__ ) )
124
+
125
+ Assert( new_user.credit_card.number == 456 )
126
+ Assert( new_user.phone_numbers.home.country_code == 41 )
127
+ end
128
+
129
+ eg 'extensions persist after reverse_deep_merge' do
130
+ user = AngryHash[ :name => 'Bob', :credit_card => {:number => 123}, :phone_numbers => {:home => {:number => '+41 0404555111'}} ]
131
+ user.extend Fixtures::User
132
+
133
+ Assert( user.phone_numbers.home.country_code == 41 )
134
+
135
+ # Do the reverse_deep_merge
136
+ new_user = user.reverse_deep_merge(:credit_card => {:number => 456})
137
+
138
+ Assert( new_user.phone_numbers.home.country_code == 41 )
139
+
140
+ Assert( ! same_obj( user.__id__, new_user.__id__ ) )
141
+ Assert( ! same_obj( user.phone_numbers.home.__id__, new_user.phone_numbers.home.__id__ ) )
142
+
143
+ Assert( new_user.credit_card.number == 123 )
144
+ Assert( new_user.phone_numbers.home.country_code == 41 )
145
+ end
146
+
147
+ eg 'extensions persist after AngryHash[]' do
148
+ user = AngryHash[ :name => 'Bob', :credit_card => {:number => 123}, :phone_numbers => {:home => {:number => '+41 0404555111'}} ]
149
+ user.extend Fixtures::User
150
+
151
+ user.__angry_hash_extension.tapp(:ahe)
152
+
153
+ num = user.phone_numbers.home
154
+ num.__angry_hash_extension.tapp(:ahe)
155
+
156
+ Assert(num.country_code == 41)
157
+
158
+ args = AngryHash[ :nothing_special => {:phone => user.phone_numbers.home} ]
159
+
160
+ Show(args)
161
+ Assert(args.nothing_special.phone.country_code == 41)
162
+ end
data/lib/angry_hash.rb CHANGED
@@ -1,80 +1,49 @@
1
-
2
1
  class AngryHash < Hash
2
+ require 'angry_hash/conversion/by_reference'
3
+ require 'angry_hash/conversion/duplicating'
4
+ require 'angry_hash/extension'
5
+ require 'angry_hash/extension_aware'
6
+ require 'angry_hash/merging'
7
+ require 'angry_hash/initialiser'
8
+
3
9
  # config
4
10
  require 'angry_hash/dsl'
5
11
  include AngryHash::DSL
6
12
 
7
- def self.[](other=nil)
8
- if other
9
- super(__convert(other))
10
- else
11
- new
12
- end
13
- end
13
+ extend AngryHash::Initialiser
14
+
14
15
 
15
16
  alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
16
17
  alias_method :regular_reader, :[] unless method_defined?(:regular_reader)
17
18
  alias_method :regular_update, :update unless method_defined?(:regular_update)
18
19
 
20
+ # store value under key, without dup-ing.
19
21
  def []=(key, value)
20
- regular_writer(__convert_key(key), self.class.__convert_value_without_dup(value))
22
+ regular_writer(__convert_key(key), __convert_value_without_dup(value))
21
23
  end
22
24
 
25
+ # fetch value stored under key.
23
26
  def [](key)
24
27
  regular_reader(__convert_key(key))
25
28
  end
26
29
 
30
+ # override id to fetch value stored under 'id'.
27
31
  def id
28
32
  regular_reader('id')
29
33
  end
30
34
 
35
+ # store value under key by duping the value.
31
36
  def dup_and_store(key,value)
32
- regular_writer(__convert_key(key), self.class.__convert_value(value))
37
+ regular_writer(__convert_key(key), __convert_value(value))
33
38
  end
34
39
 
35
- alias_method :regular_merge, :merge unless method_defined?(:regular_merge)
36
- def merge(hash)
37
- regular_merge(self.class.__convert_without_dup(hash))
38
- end
39
-
40
- def merge!(other_hash)
41
- other_hash.each_pair { |key, value| dup_and_store(key,value) }
42
- self
43
- end
44
- alias_method :update, :merge!
45
-
40
+ # Duplicate the AngryHash
46
41
  def dup
47
42
  self.class[ self ]
48
43
  end
49
44
 
50
- def deep_merge(other_hash)
51
- other_hash = AngryHash[other_hash]
52
-
53
- self.regular_merge( other_hash ) do |key, oldval, newval|
54
- oldval = AngryHash.__convert_value(oldval)
55
- newval = AngryHash.__convert_value(newval)
56
-
57
- AngryHash === oldval && AngryHash === newval ? oldval.deep_merge(newval) : newval
58
- end
59
- end
60
-
61
- def deep_merge!(other_hash)
62
- replace(deep_merge(other_hash))
63
- self
64
- end
65
- alias_method :deep_update, :deep_merge!
66
-
67
- def reverse_deep_merge(other_hash)
68
- self.class.__convert_value(other_hash).deep_merge(self)
69
- end
70
-
71
- def reverse_deep_merge!(other_hash)
72
- replace(reverse_deep_merge(other_hash))
73
- self
74
- end
75
- alias_method :reverse_deep_update, :reverse_deep_merge!
76
-
77
45
 
46
+ # override normal Hash methods
78
47
 
79
48
  def key?(key)
80
49
  super(__convert_key(key))
@@ -99,6 +68,8 @@ class AngryHash < Hash
99
68
  self
100
69
  end
101
70
 
71
+ ## Convert back to a plain hash
72
+
102
73
  def to_normal_hash(keys=nil)
103
74
  __to_hash(self,keys)
104
75
  end
@@ -132,6 +103,7 @@ class AngryHash < Hash
132
103
  end
133
104
 
134
105
 
106
+ # Support dot notation access
135
107
  def method_missing(method,*args,&blk)
136
108
  method_s = method.to_s
137
109
 
@@ -157,6 +129,7 @@ class AngryHash < Hash
157
129
  end
158
130
  end
159
131
 
132
+ # please be Strings
160
133
  def __convert_key(key)
161
134
  Symbol === key ? key.to_s : key
162
135
  end
@@ -164,67 +137,8 @@ class AngryHash < Hash
164
137
  Symbol === key ? key.to_s : key
165
138
  end
166
139
 
167
-
168
- # non-duplicating convert
169
- def self.__convert_without_dup(hash)
170
- hash.inject(AngryHash.new) do |newhash,(k,v)|
171
- newhash[__convert_key(k)] = __convert_value_without_dup(v)
172
- newhash
173
- end
174
- end
175
-
176
- def self.__convert_value_without_dup(v)
177
- v = v.to_hash if v.respond_to?(:to_hash)
178
-
179
- case v
180
- when AngryHash
181
- v
182
- when Hash
183
- __convert_without_dup(v)
184
- when Array
185
- v.map {|vv| __convert_value_without_dup(vv)}
186
- else
187
- v
188
- end
189
- end
190
-
191
-
192
- # duplicating convert
193
- def self.__convert(hash,cycle_watch=[])
194
- new_hash = hash.inject(AngryHash.new) do |hash,(k,v)|
195
- hash.regular_writer( __convert_key(k), __convert_value(v,cycle_watch) )
196
- hash
197
- end
198
-
199
- new_hash
200
- end
201
-
202
- def self.__convert_value(v,cycle_watch=[])
203
- id = v.__id__
204
-
205
- return if cycle_watch.include? id
206
-
207
- begin
208
- cycle_watch << id
209
-
210
- original_v = v
211
- v = v.to_hash if v.respond_to?(:to_hash)
212
-
213
- case v
214
- when Hash
215
- __convert(v,cycle_watch)
216
- when Array
217
- v.map {|vv| __convert_value(vv,cycle_watch)}
218
- when Fixnum,Symbol,NilClass,TrueClass,FalseClass,Float,Bignum
219
- v
220
- else
221
- v.dup
222
- end
223
- ensure
224
- cycle_watch.pop
225
- end
226
- end
227
-
140
+ include Merging
141
+ include Conversion::Duplicating
142
+ include Conversion::ByReference
143
+ include ExtensionAware
228
144
  end
229
-
230
-
@@ -0,0 +1,30 @@
1
+ class AngryHash
2
+ module Conversion
3
+ module ByReference
4
+
5
+ # non-duplicating convert
6
+ def __convert_without_dup(hash)
7
+ hash.inject(AngryHash.new) do |newhash,(k,v)|
8
+ newhash[__convert_key(k)] = __convert_value_without_dup(v)
9
+ newhash
10
+ end
11
+ end
12
+
13
+ def __convert_value_without_dup(v)
14
+ v = v.to_hash if v.respond_to?(:to_hash)
15
+
16
+ case v
17
+ when AngryHash
18
+ v
19
+ when Hash
20
+ __convert_without_dup(v)
21
+ when Array
22
+ v.map {|vv| __convert_value_without_dup(vv)}
23
+ else
24
+ v
25
+ end
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,56 @@
1
+ class AngryHash
2
+ module Conversion
3
+ module Duplicating
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ # duplicating convert
10
+ def __convert(hash,cycle_watch=[])
11
+ new_hash = hash.inject(AngryHash.new) do |hash,(k,v)|
12
+ hash.regular_writer( __convert_key(k), __convert_value(v,cycle_watch) )
13
+ hash
14
+ end
15
+
16
+ new_hash
17
+ end
18
+
19
+ def __convert_value(v,cycle_watch=[])
20
+ id = v.__id__
21
+
22
+ return if cycle_watch.include? id
23
+
24
+ begin
25
+ cycle_watch << id
26
+
27
+ original_v = v
28
+ v = v.to_hash if v.respond_to?(:to_hash)
29
+
30
+ case v
31
+ when Hash
32
+ __convert(v,cycle_watch)
33
+ when Array
34
+ v.map {|vv| __convert_value(vv,cycle_watch)}
35
+ when Fixnum,Symbol,NilClass,TrueClass,FalseClass,Float,Bignum,BigDecimal
36
+ v
37
+ else
38
+ v.dup
39
+ end
40
+ ensure
41
+ cycle_watch.pop
42
+ end
43
+ end
44
+ end
45
+
46
+ def __convert(hash,cycle_watch=[])
47
+ self.class.__convert(hash,cycle_watch)
48
+ end
49
+
50
+ def __convert_value(v,cycle_watch=[])
51
+ self.class.__convert_value(v,cycle_watch)
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -1,10 +1,4 @@
1
- require 'angry_hash'
2
-
3
1
  class AngryHash
4
- def self.new_extended(mod,seed={})
5
- self[ seed ].tap {|hash| hash.extend(mod) }
6
- end
7
-
8
2
  module Extension
9
3
  class << self
10
4
  def included(base)
@@ -49,7 +43,13 @@ class AngryHash
49
43
  mixin_registry[target_class][field.to_s] = [:hash, mod, options]
50
44
  end
51
45
 
52
- def extend_hash(hash, mod, parent_hash)
46
+ # Register a block extension - applied to objects of the defining module.
47
+ # This is in contrast to the other mixin types which are applied to subordinate objects.
48
+ def register_mixin_block(target_class, options)
49
+ mixin_registry[target_class]['*'] = [:block, options]
50
+ end
51
+
52
+ def extend_hash(hash, mod, parent_hash, block)
53
53
  if !parent_hash.nil? && hash.nil?
54
54
  hash = AngryHash.new
55
55
  end
@@ -57,6 +57,11 @@ class AngryHash
57
57
  hash.extend mod
58
58
 
59
59
  hash.__parent_hash = parent_hash if hash.respond_to?(:__parent_hash=)
60
+
61
+ if block
62
+ hash.instance_eval(&block)
63
+ end
64
+
60
65
  hash
61
66
  end
62
67
 
@@ -71,29 +76,44 @@ class AngryHash
71
76
  if mixin = mixin_registry[extension][field.to_s]
72
77
  kind,mod,options = *mixin
73
78
 
79
+ if options[:allow_nil] && obj.nil?
80
+ return nil
81
+ end
82
+
74
83
  if options.key?(:default) && obj.nil?
75
84
  obj = options[:default]
76
85
  end
77
86
 
87
+
88
+
89
+ # the result of `extend_self` block
90
+ extend_self = if (sub_ext = mixin_registry[mod]['*']) && sub_ext[0] == :block
91
+ sub_ext[1][:block]
92
+ end
93
+
78
94
  case kind
79
95
  when :single
80
- obj = extend_hash(obj,mod,parent_obj)
96
+ obj = extend_hash(obj,mod,parent_obj,extend_self)
81
97
  when :array
82
98
  # XXX - this is ok for now... we really need to typecheck, perhaps wrap in a smart-array
83
99
  obj ||= []
84
- obj = obj.map {|elt| extend_hash(elt, mod, parent_obj)}
100
+ obj = obj.map! {|elt| extend_hash(elt, mod, parent_obj, extend_self) }
85
101
  when :hash
86
102
  obj ||= {}
87
- obj = obj.inject(AngryHash.new) do |h,(k,elt)|
88
- h[k] = extend_hash(elt,mod,parent_obj)
103
+ obj.replace( obj.inject(AngryHash.new) {|h,(k,elt)|
104
+ h[k] = extend_hash(elt,mod,parent_obj, extend_self)
89
105
  h
90
- end
106
+ })
91
107
  end
108
+
109
+
92
110
  end
93
111
 
94
112
  obj
95
113
  end
96
- end
114
+ end # Extension module methods
115
+
116
+ ## Instance methods
97
117
 
98
118
  def [](key)
99
119
  Extension.mixin_to(self,key,super)
@@ -105,10 +125,12 @@ class AngryHash
105
125
 
106
126
  def dup_with_extension
107
127
  dup.tap {|new_hash|
108
- new_hash.extend(__angry_hash_extension) if __angry_hash_extension
128
+ AngryHash.copy_extension(self,new_hash)
109
129
  }
110
130
  end
111
131
 
132
+ ## AngryHash extension attributes
133
+ # These should be copied using `AngryHash.copy_extension` when duping
112
134
  def __parent_hash=(hash)
113
135
  @__parent_hash = hash
114
136
  end
@@ -125,6 +147,7 @@ class AngryHash
125
147
  @__angry_hash_extension
126
148
  end
127
149
 
150
+
128
151
  module ClassMethods
129
152
  def defaults(default_form=nil)
130
153
  if default_form
@@ -151,6 +174,11 @@ class AngryHash
151
174
  Extension.register_mixin_hash(self,field,mod,options)
152
175
  end
153
176
 
177
+ def extend_self(options={}, &block)
178
+ options[:block] = block
179
+ Extension.register_mixin_block(self,options)
180
+ end
181
+
154
182
  def build(seed={})
155
183
  AngryHash[ seed ].tap {|hash|
156
184
  self.fill_in_defaults(hash)
@@ -0,0 +1,54 @@
1
+ class AngryHash
2
+ ## Adds extension awareness to core AngryHash copying methods
3
+ module ExtensionAware
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ def [](seed)
10
+ super.tap {|hash|
11
+ copy_extension(seed,hash)
12
+ }
13
+ end
14
+
15
+ def __convert(hash,cycle_watch=[])
16
+ super.tap {|new_hash|
17
+ copy_extension(hash,new_hash)
18
+ }
19
+ end
20
+
21
+ def new_extended(mod,seed={})
22
+ self[ seed ].tap {|hash| hash.extend(mod) }
23
+ end
24
+
25
+ def dup_with_extension(other)
26
+ if AngryHash === other
27
+ other.respond_to?(:dup_with_extension) ? other.dup_with_extension : other.dup
28
+ elsif Hash === other
29
+ dup_with_extension(AngryHash[other])
30
+ elsif
31
+ other.dup
32
+ end
33
+ end
34
+
35
+ def copy_extension(from,to_hash)
36
+ to_hash.tap {|to|
37
+ to.extend(from.__angry_hash_extension) if from.respond_to?(:__angry_hash_extension)
38
+ to.__parent_hash = from.__parent_hash if to.respond_to?(:__parent_hash=) && from.respond_to?(:__parent_hash)
39
+ }
40
+ end
41
+
42
+ # Given a hash, detect whether it's extended & by which module.
43
+ def extended_with(hash)
44
+ hash.__angry_hash_extension if hash.respond_to?(:__angry_hash_extension)
45
+ end
46
+ end
47
+
48
+ def reverse_deep_merge(other_hash)
49
+ super.tap {|merged|
50
+ self.class.copy_extension(self,merged)
51
+ }
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,11 @@
1
+ class AngryHash
2
+ module Initialiser
3
+ def [](other=nil)
4
+ if other
5
+ super(__convert(other))
6
+ else
7
+ new
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,56 @@
1
+ class AngryHash
2
+ module Merging
3
+ def self.included(base)
4
+ base.class_eval do
5
+ alias_method :regular_merge, :merge unless method_defined?(:regular_merge)
6
+ end
7
+ end
8
+
9
+
10
+ ## Merging
11
+
12
+ # Shallow merge hash.
13
+ def merge(hash)
14
+ super(__convert_without_dup(hash))
15
+ end
16
+
17
+ # Shallow merge! hash.
18
+ def merge!(other_hash)
19
+ other_hash.each_pair { |key, value| dup_and_store(key,value) }
20
+ self
21
+ end
22
+ alias_method :update, :merge!
23
+
24
+
25
+ # Merge deeply. The other hash's contents are favoured.
26
+ def deep_merge(other_hash)
27
+ other_hash = AngryHash[other_hash]
28
+
29
+ self.regular_merge( other_hash ) do |key, oldval, newval|
30
+ oldval = __convert_value(oldval)
31
+ newval = __convert_value(newval)
32
+
33
+ AngryHash === oldval && AngryHash === newval ? oldval.deep_merge(newval) : newval
34
+ end
35
+ end
36
+
37
+ # Merge deeply, replacing the AngryHash with the result.
38
+ def deep_merge!(other_hash)
39
+ replace(deep_merge(other_hash))
40
+ self
41
+ end
42
+ alias_method :deep_update, :deep_merge!
43
+
44
+ # Merge deeply in reverse. This hash's contents are favoured.
45
+ def reverse_deep_merge(other_hash)
46
+ __convert(other_hash).deep_merge(self)
47
+ end
48
+
49
+ # Merge deeply in reverse, replacing this AngryHash with the result. This hash's contents are favoured.
50
+ def reverse_deep_merge!(other_hash)
51
+ replace(reverse_deep_merge(other_hash))
52
+ self
53
+ end
54
+ alias_method :reverse_deep_update, :reverse_deep_merge!
55
+ end
56
+ end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 2
8
- - 2
9
- version: 0.2.2
7
+ - 3
8
+ - 0
9
+ version: 0.3.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Lachie Cox
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-08-30 00:00:00 +10:00
17
+ date: 2010-10-20 00:00:00 +11:00
18
18
  default_executable:
19
19
  dependencies: []
20
20
 
@@ -36,9 +36,14 @@ files:
36
36
  - VERSION
37
37
  - angry_hash.gemspec
38
38
  - lib/angry_hash.rb
39
+ - lib/angry_hash/conversion/by_reference.rb
40
+ - lib/angry_hash/conversion/duplicating.rb
39
41
  - lib/angry_hash/dsl.rb
40
42
  - lib/angry_hash/extension.rb
43
+ - lib/angry_hash/extension_aware.rb
44
+ - lib/angry_hash/initialiser.rb
41
45
  - lib/angry_hash/merge_string.rb
46
+ - lib/angry_hash/merging.rb
42
47
  - LICENSE
43
48
  - README.md
44
49
  has_rdoc: true
@@ -77,6 +82,6 @@ test_files:
77
82
  - examples/dsl.eg.rb
78
83
  - examples/dup_eg.rb
79
84
  - examples/eg_helper.rb
80
- - examples/extension_tracking.rb
85
+ - examples/extension_tracking.eg.rb
81
86
  - examples/merge_eg.rb
82
87
  - examples/normal_hash.eg.rb