persistent_tree 0.0.1
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.
- checksums.yaml +7 -0
- data/lib/persistent_tree.rb +4 -0
- data/lib/persistent_tree/map.rb +258 -0
- metadata +101 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 0f943246939cd4aac7047ee312fa7160b76295ae4577c2d3ad5e28a3684e2c03
|
4
|
+
data.tar.gz: dc98aca9c41d1c150489985b68fdc7faee841cbe6fb2742e00046a0f05567d03
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1d119db599794dd0499bf19421327c312a4db271bf1dd71da24fb0e87fdd0528efaa7165e36317855323fae1ec37ad4b7d8dd3fe8e7838e7414145d3c07fd91b
|
7
|
+
data.tar.gz: c81de3f1c7ccedac2e25c69aea715710f35cafd03252bdad863c6e4e0ad4dde81fa69a618e0729733bbb5666a66c0aeb799f0b60ed89533a9e50a46bb4145241
|
@@ -0,0 +1,258 @@
|
|
1
|
+
require_relative 'tree'
|
2
|
+
|
3
|
+
module PersistentTree
|
4
|
+
class MapEnumeratorDecorator
|
5
|
+
def enumerator(subject)
|
6
|
+
@subject = subject
|
7
|
+
end
|
8
|
+
|
9
|
+
def respond_to_missing?(method_name, *args)
|
10
|
+
@subject.respond_to?(method_name) || super
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing(method_name, *args, &block)
|
14
|
+
if @subject.respond_to? method_name
|
15
|
+
@subject.send method_name, *args, &block
|
16
|
+
else
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Read only view of a Map at a given version
|
23
|
+
class MapView
|
24
|
+
include Enumerable
|
25
|
+
|
26
|
+
# Internal data structure used to store key-value pairs
|
27
|
+
Entry = Struct.new(:key, :value)
|
28
|
+
private_constant :Entry
|
29
|
+
|
30
|
+
# Allow Entry objects to be ordered by their key or by comparing their key to other values
|
31
|
+
class Entry
|
32
|
+
include Comparable
|
33
|
+
|
34
|
+
def <=>(other)
|
35
|
+
if other.is_a? Entry
|
36
|
+
key <=> other.key
|
37
|
+
else
|
38
|
+
key <=> other
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def initialize(tree, version, default_value, default_proc)
|
44
|
+
@tree = tree
|
45
|
+
@version = version
|
46
|
+
@default_value = default_value
|
47
|
+
@default_proc = default_proc
|
48
|
+
end
|
49
|
+
|
50
|
+
def [](key)
|
51
|
+
found, value = fetch_without_default(key)
|
52
|
+
if found
|
53
|
+
value
|
54
|
+
elsif @default_proc
|
55
|
+
@default_proc.call(self, key)
|
56
|
+
else
|
57
|
+
@default_value
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def each
|
62
|
+
if block_given?
|
63
|
+
@tree.each(@version) do |item|
|
64
|
+
yield item.key, item.value
|
65
|
+
end
|
66
|
+
self
|
67
|
+
else
|
68
|
+
to_enum
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def each_key(&block)
|
73
|
+
if block_given?
|
74
|
+
keys.each(&block)
|
75
|
+
self
|
76
|
+
else
|
77
|
+
keys.each
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def fetch(*args)
|
82
|
+
raise ArgumentError, "wrong number of arguments (#{args.length} for 1..2)" \
|
83
|
+
unless args.length.between?(1, 2)
|
84
|
+
|
85
|
+
warn('block supersedes default value argument') if args.length == 2 && block_given?
|
86
|
+
|
87
|
+
key = args[0]
|
88
|
+
found, value = fetch_without_default(key)
|
89
|
+
if found
|
90
|
+
value
|
91
|
+
elsif block_given?
|
92
|
+
yield key
|
93
|
+
elsif args.length == 2
|
94
|
+
args[1]
|
95
|
+
else
|
96
|
+
raise KeyError, "key not found: #{key}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def flatten(*args)
|
101
|
+
# avoid unnecessary work if possible
|
102
|
+
return flatten_key_value_pairs if args.empty?
|
103
|
+
|
104
|
+
raise ArgumentError, "wrong number of arguments (#{args.length} for 0..1)" if args.length > 1
|
105
|
+
|
106
|
+
raise TypeError, "no implicit conversion of #{args[0].class.name} into Integer" \
|
107
|
+
unless args[0].respond_to?(:to_int)
|
108
|
+
|
109
|
+
level = args[0].to_int
|
110
|
+
return to_a if level.zero? # Rely on implicit array conversion behaviour
|
111
|
+
|
112
|
+
result = flatten_key_value_pairs # Otherwise, flatten recursively
|
113
|
+
if (level - 1).positive?
|
114
|
+
result.flatten!(level - 1)
|
115
|
+
elsif level.negative?
|
116
|
+
result.flatten!
|
117
|
+
end
|
118
|
+
result
|
119
|
+
end
|
120
|
+
|
121
|
+
def keys
|
122
|
+
@tree.map(@version, &:key)
|
123
|
+
end
|
124
|
+
|
125
|
+
def include?(key)
|
126
|
+
@tree.include?(@version, key)
|
127
|
+
end
|
128
|
+
|
129
|
+
def select
|
130
|
+
if block_given?
|
131
|
+
new_map = Map.new
|
132
|
+
@tree.each(@version) do |item|
|
133
|
+
new_map.store item.key, item.value if yield item.key, item.value
|
134
|
+
end
|
135
|
+
new_map
|
136
|
+
else
|
137
|
+
to_enum(:select)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def size
|
142
|
+
@tree.size(@version)
|
143
|
+
end
|
144
|
+
|
145
|
+
alias each_pair each
|
146
|
+
alias has_key? include?
|
147
|
+
alias key? include?
|
148
|
+
alias member? include?
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
def fetch_without_default(key)
|
153
|
+
[true, @tree.find(@version, key).value]
|
154
|
+
rescue KeyError
|
155
|
+
[false, nil]
|
156
|
+
end
|
157
|
+
|
158
|
+
def flatten_key_value_pairs
|
159
|
+
result = Array.new(size * 2)
|
160
|
+
index = 0
|
161
|
+
each do |key, value|
|
162
|
+
result[index] = key
|
163
|
+
result[index + 1] = value
|
164
|
+
index += 2
|
165
|
+
end
|
166
|
+
result
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
class Map < MapView
|
171
|
+
attr_reader :default_proc
|
172
|
+
|
173
|
+
def initialize(*args, &block)
|
174
|
+
default_proc = nil
|
175
|
+
default_value = nil
|
176
|
+
if block_given?
|
177
|
+
raise ArgumentError, "wrong number of arguments (#{args.length} for 0)" unless args.empty?
|
178
|
+
|
179
|
+
if block.is_a? Proc
|
180
|
+
default_proc = block
|
181
|
+
else
|
182
|
+
default_proc = Proc.new
|
183
|
+
end
|
184
|
+
|
185
|
+
raise TypeError, "default_proc takes two arguments (2 for #{default_proc.arity})" \
|
186
|
+
if default_proc.lambda? && default_proc.arity != 2
|
187
|
+
|
188
|
+
elsif args.length == 1
|
189
|
+
default_value = args[0]
|
190
|
+
elsif !args.empty?
|
191
|
+
raise ArgumentError, "wrong number of arguments (#{args.length} for 0..1)"
|
192
|
+
end
|
193
|
+
super(PersistentTree::Tree.new, 0, default_value, default_proc)
|
194
|
+
end
|
195
|
+
|
196
|
+
def []=(key, value)
|
197
|
+
store(key, value)
|
198
|
+
end
|
199
|
+
|
200
|
+
def default(key = nil)
|
201
|
+
if @default_value
|
202
|
+
@default_value
|
203
|
+
elsif @default_proc && key
|
204
|
+
@default_proc.call(self, key)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def default=(value)
|
209
|
+
@default_proc = nil
|
210
|
+
@default_value = value
|
211
|
+
end
|
212
|
+
|
213
|
+
def default_proc=(proc)
|
214
|
+
raise TypeError, "wrong default_proc type #{proc.class.name} (expected Proc)" \
|
215
|
+
if proc.nil? || !proc.respond_to?(:to_proc)
|
216
|
+
|
217
|
+
default_proc = proc.to_proc
|
218
|
+
unless default_proc.is_a?(Proc)
|
219
|
+
raise TypeError,
|
220
|
+
"can't convert #{proc.class.name} to Proc (#{proc.class.name}#to_proc gives #{default_proc.class.name})"
|
221
|
+
end
|
222
|
+
|
223
|
+
raise TypeError, "default_proc takes two arguments (2 for #{default_proc.arity})" \
|
224
|
+
if default_proc.lambda? && default_proc.arity != 2
|
225
|
+
|
226
|
+
@default_value = nil
|
227
|
+
@default_proc = default_proc
|
228
|
+
end
|
229
|
+
|
230
|
+
def store(key, value)
|
231
|
+
if key.is_a?(String) && !key.frozen?
|
232
|
+
key_to_store = key.freeze
|
233
|
+
else
|
234
|
+
key_to_store = key
|
235
|
+
end
|
236
|
+
@tree.insert_or_replace(Entry.new(key_to_store, value)).value
|
237
|
+
@version = @tree.version
|
238
|
+
value
|
239
|
+
end
|
240
|
+
|
241
|
+
def version(*args)
|
242
|
+
if args.length == 1
|
243
|
+
requested_version = args[0]
|
244
|
+
raise TypeError, 'requested version should be an integer' \
|
245
|
+
unless requested_version.is_a? Integer
|
246
|
+
|
247
|
+
raise RangeError, "requested version (#{requested_version}) exceeds current version (#{@tree.version})" \
|
248
|
+
if requested_version > @tree.version
|
249
|
+
|
250
|
+
MapView.new(@tree, requested_version, @default_value, @default_proc)
|
251
|
+
elsif args.empty?
|
252
|
+
@tree.version
|
253
|
+
else
|
254
|
+
raise ArgumentError, "wrong number of arguments(#{args.length} for 0..1)"
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: persistent_tree
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tristan Penman
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-06-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rubocop
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Partially Persistent Tree, based on Okasaki's 'Purely Functional Data
|
70
|
+
Structures'
|
71
|
+
email: tristan@tristanpenman.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- lib/persistent_tree.rb
|
77
|
+
- lib/persistent_tree/map.rb
|
78
|
+
homepage: http://rubygems.org/gems/persistent_tree
|
79
|
+
licenses:
|
80
|
+
- MIT
|
81
|
+
metadata: {}
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options: []
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
requirements: []
|
97
|
+
rubygems_version: 3.0.9
|
98
|
+
signing_key:
|
99
|
+
specification_version: 4
|
100
|
+
summary: Partially Persistent Tree for Ruby
|
101
|
+
test_files: []
|