squeeze 0.1.0 → 0.2.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 (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