squeeze 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/squeeze/hash_tree.rb +162 -118
  2. metadata +3 -3
@@ -1,153 +1,197 @@
1
1
  require "squeeze/traversable"
2
2
 
3
- # A Hash subclass with a default proc that creates another HashTree
4
- # on attempts to access missing keys.
5
- class HashTree < Hash
6
- include Squeeze::Traversable
7
-
8
- # Override the constructor to provide a default_proc
9
- # NOTE: there's a better way to do this in 1.9.2, it seems.
10
- # See Hash#default_proc=
11
- def self.new()
12
- hash = Hash.new { |h,k| h[k] = HashTree.new }
13
- super.replace(hash)
14
- end
3
+ # A Hash specially equipped with a default_proc that wishes into being
4
+ # new HashTrees when you try to get or set a non-existent key.
5
+ #
6
+ # ht = HashTree.new
7
+ # ht[:i][:am][:a][:roving] = :gambler
8
+ #
9
+ class Squeeze
10
+ class HashTree < Hash
11
+ include Squeeze::Traversable
12
+
13
+ # Override the constructor to provide a default_proc
14
+ # NOTE: there's a better way to do this in >=1.9.2, it seems.
15
+ # See Hash#default_proc=
16
+ def self.new()
17
+ hash = Hash.new { |h,k| h[k] = HashTree.new }
18
+ super.replace(hash)
19
+ end
15
20
 
16
- def self.[](hash)
17
- ht = self.new
18
- ht << hash
19
- ht
20
- end
21
+ def self.[](hash)
22
+ ht = self.new
23
+ ht << hash
24
+ ht
25
+ end
21
26
 
22
- def _dump(depth)
23
- h = Hash[self]
24
- h.delete_if {|k,v| v.is_a? Proc }
25
- Marshal.dump(h)
26
- end
27
+ def _dump(depth)
28
+ h = Hash[self]
29
+ h.delete_if {|k,v| v.is_a? Proc }
30
+ Marshal.dump(h)
31
+ end
27
32
 
28
- def self._load(*args)
29
- h = Marshal.load(*args)
30
- ht = self.new
31
- ht.replace(h)
32
- ht
33
- end
33
+ def self._load(*args)
34
+ h = Marshal.load(*args)
35
+ ht = self.new
36
+ ht.replace(h)
37
+ ht
38
+ end
34
39
 
35
- # Follow the path specified, creating new nodes where necessary.
36
- # Returns the value at the end of the path. If a block is supplied,
37
- # it will be called with the last node and the last key as parameters,
38
- # analogous to Hash.new's default proc. This is necessary to allow
39
- # setting a value at the end of the path. See the implementation of #insert.
40
- def create_path(sig)
41
- final_key = sig.pop
42
- hash = self
43
- sig.each do |a|
44
- hash = hash[a]
40
+ # Follow the path specified, creating new nodes where necessary.
41
+ # Returns the value at the end of the path. If a block is supplied,
42
+ # it will be called with the last node and the last key as parameters,
43
+ # analogous to Hash.new's default proc. This is necessary to allow
44
+ # setting a value at the end of the path. See the implementation of #insert.
45
+ def create_path(sig)
46
+ final_key = sig.pop
47
+ hash = self
48
+ sig.each do |a|
49
+ hash = hash[a]
50
+ end
51
+ yield(hash, final_key) if block_given?
52
+ hash[final_key]
45
53
  end
46
- yield(hash, final_key) if block_given?
47
- hash[final_key]
48
- end
49
54
 
50
- # Attempt to retrieve the value at the end of the path specified,
51
- # without creating new nodes. Returns nil on failure.
52
- # TODO: consider whether splatting the signature is wise.
53
- def find(*sig)
54
- stage = self
55
- sig.each do |a|
56
- if stage.has_key?(a)
57
- stage = stage[a]
58
- else
59
- return nil
55
+ # Attempt to retrieve the value at the end of the path specified,
56
+ # without creating new nodes. Returns nil on failure.
57
+ def find(sig)
58
+ stage = self
59
+ sig.each do |a|
60
+ if stage.has_key?(a)
61
+ stage = stage[a]
62
+ else
63
+ return nil
64
+ end
60
65
  end
66
+ stage
61
67
  end
62
- stage
63
- end
64
68
 
65
- def children(matcher=true)
66
- next_keys = self.keys.select do |key|
67
- match?(matcher, key)
69
+ def remove(sig)
70
+ stage = self
71
+ s2 = sig.slice(0..-2)
72
+ s2.each do |a|
73
+ if stage.has_key?(a)
74
+ stage = stage[a]
75
+ else
76
+ return nil
77
+ end
78
+ end
79
+ stage.delete(sig.last)
68
80
  end
69
- self.values_at(*next_keys)
70
- end
71
81
 
72
- def +(other)
73
- out = HashTree.new
74
- _plus(other, out)
75
- out
76
- end
82
+ def children(matcher=true)
83
+ next_keys = self.keys.select do |key|
84
+ match?(matcher, key)
85
+ end
86
+ self.values_at(*next_keys)
87
+ end
77
88
 
78
- def _plus(ht2, out)
79
- self.each do |k1,v1|
80
- v1 = v1.respond_to?(:dup) ? v1 : v1.dup
81
- if ht2.has_key?(k1)
82
- v2 = ht2[k1]
83
- if v1.respond_to?(:_plus)
84
- out[k1] = v1
85
- v1._plus(v2, out[k1])
86
- elsif v2.respond_to?(:_plus)
87
- raise ArgumentError,
88
- "Can't merge leaf with non-leaf:\n#{v1.inspect}\n#{v2.inspect}"
89
+ def +(other)
90
+ out = HashTree.new
91
+ _plus(other, out)
92
+ out
93
+ end
94
+
95
+ def _plus(ht2, out)
96
+ self.each do |k1,v1|
97
+ v1 = v1.respond_to?(:dup) ? v1 : v1.dup
98
+ if ht2.has_key?(k1)
99
+ v2 = ht2[k1]
100
+ if v1.respond_to?(:_plus)
101
+ out[k1] = v1
102
+ v1._plus(v2, out[k1])
103
+ elsif v2.respond_to?(:_plus)
104
+ raise ArgumentError,
105
+ "Can't merge leaf with non-leaf:\n#{v1.inspect}\n#{v2.inspect}"
106
+ else
107
+ if v2.is_a?(Numeric) && v1.is_a?(Numeric)
108
+ out[k1] = v1 + v2
109
+ else
110
+ out[k1] = [v1, ht2[k1]]
111
+ end
112
+ end
113
+ else
114
+ # should anything happen here?
115
+ end
116
+ end
117
+ ht2.each do |k,v|
118
+ if self.has_key?(k)
119
+ # should anything happen here?
120
+ else
121
+ v = v.respond_to?(:dup) ? v : v.dup
122
+ out[k] = v
123
+ end
124
+ end
125
+ end
126
+
127
+ # Merge the argument into self, overwriting where necessary.
128
+ def <<(other)
129
+ other.each do |k,v1|
130
+ if self.has_key?(k)
131
+ v2 = self[k]
132
+ if v1.respond_to?(:has_key?) && v2.respond_to?(:has_key?)
133
+ v2 << v1
134
+ elsif v1.is_a?(Numeric) && v2.is_a?(Numeric)
135
+ self[k] = v1 + v2
136
+ else
137
+ raise ArgumentError,
138
+ "Can't merge leaf with non-leaf:\n#{v1.inspect}\n#{v2.inspect}"
139
+ end
89
140
  else
90
- if v2.is_a?(Numeric) && v1.is_a?(Numeric)
91
- out[k1] = v1 + v2
141
+ if v1.respond_to?(:has_key?)
142
+ self[k] << v1
92
143
  else
93
- out[k1] = [v1, ht2[k1]]
144
+ self[k] = v1
94
145
  end
95
146
  end
96
- else
97
- # should anything happen here?
98
147
  end
99
148
  end
100
- ht2.each do |k,v|
101
- if self.has_key?(k)
102
- # should anything happen here?
149
+
150
+ def match?(val, key)
151
+ case val
152
+ when true
153
+ true
154
+ when String, Symbol
155
+ key == val
156
+ when Regexp
157
+ key =~ val
158
+ when Proc
159
+ val.call(key)
160
+ when nil
161
+ false
103
162
  else
104
- v = v.respond_to?(:dup) ? v : v.dup
105
- out[k] = v
163
+ raise ArgumentError, "Unexpected matcher type: #{val.inspect}"
106
164
  end
107
165
  end
108
- end
109
166
 
110
- def <<(other)
111
- other.each do |k,v1|
112
- if self.has_key?(k)
113
- v2 = self[k]
114
- if v1.respond_to?(:has_key?) && v2.respond_to?(:has_key?)
115
- v2 << v1
116
- elsif v1.is_a?(Numeric) && v2.is_a?(Numeric)
117
- self[k] = v1 + v2
167
+ # Depth-first traversal, yielding the path to each leaf.
168
+ def each_path(stack=[], &block)
169
+ self.each do |k, v|
170
+ stack.push(k)
171
+ if v.respond_to?(:each_path)
172
+ v.each_path(stack, &block)
118
173
  else
119
- raise ArgumentError,
120
- "Can't merge leaf with non-leaf:\n#{v1.inspect}\n#{v2.inspect}"
174
+ yield(stack, v)
175
+ #block.call(stack, v)
121
176
  end
122
- else
123
- if v1.respond_to?(:has_key?)
124
- self[k] << v1
177
+ stack.pop
178
+ end
179
+ end
180
+
181
+ def each_leaf(stack=[], &block)
182
+ self.each do |k,v|
183
+ stack.push(k)
184
+ if v.respond_to?(:each_leaf)
185
+ v.each_leaf(stack, &block)
125
186
  else
126
- self[k] = v1
187
+ yield(v)
188
+ #block.call(v)
127
189
  end
190
+ stack.pop
128
191
  end
129
192
  end
130
- end
131
193
 
132
- def match?(val, key)
133
- case val
134
- when true
135
- true
136
- when String, Symbol
137
- key == val
138
- when Regexp
139
- key =~ val
140
- when Proc
141
- val.call(key)
142
- when nil
143
- false
144
- else
145
- raise ArgumentError, "Unexpected matcher type: #{val.inspect}"
146
- end
147
194
  end
148
195
 
149
196
  end
150
197
 
151
-
152
-
153
-
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: squeeze
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 1
8
+ - 2
9
9
  - 0
10
- version: 0.1.0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Matthew King