persistent_tree 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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,4 @@
1
+ require_relative 'persistent_tree/algorithms'
2
+ require_relative 'persistent_tree/map'
3
+ require_relative 'persistent_tree/node'
4
+ require_relative 'persistent_tree/tree'
@@ -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: []