key_tree 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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/lib/key_tree/forest.rb +89 -0
- data/lib/key_tree/tree.rb +36 -3
- data/lib/key_tree/version.rb +1 -1
- data/lib/key_tree.rb +3 -0
- metadata +3 -3
- data/Gemfile.lock +0 -51
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1785b5cb1b1cd95bcce54e395760a8fba2c3f18a1fe90ec6142a84371dcecebb
|
4
|
+
data.tar.gz: b2fce6710288a95c80db648556abe8e4b6a35289d743afd451e2b266bf2b3b2f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5e6d082279a58dfb080872a193ffa2eaee310b97f64f34120edbe7814935feaebd9fdd73aa4e8071acedaadef4036a66e204b7194c1e1bd9429915b60ef2094e
|
7
|
+
data.tar.gz: bc50e45870118d9ad5c21a0bf2f19dc89f53ec54aba5eb1af7917f87cdf5476eaf6509643c4bb1189d7a8dba02d9ce17a153229cb83ad4bc95065f6cde4d8899
|
data/.gitignore
CHANGED
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'key_tree/tree'
|
2
|
+
|
3
|
+
module KeyTree
|
4
|
+
#
|
5
|
+
# A forest is a (possibly nested) collection of trees
|
6
|
+
#
|
7
|
+
class Forest < Array
|
8
|
+
def self.[](*contents)
|
9
|
+
contents.reduce(Forest.new) do |result, content|
|
10
|
+
result << KeyTree[content]
|
11
|
+
end.sort!
|
12
|
+
end
|
13
|
+
|
14
|
+
# For a numeric key, return the n:th tree in the forest
|
15
|
+
#
|
16
|
+
# For a key path convertable key, return the closest match in the forest
|
17
|
+
#
|
18
|
+
# When a closer tree contains a prefix of the key, this shadows any
|
19
|
+
# key path matches in trees further away, returning nil. This preserves
|
20
|
+
# the constraints that only leaves may contain a value.
|
21
|
+
#
|
22
|
+
def [](key)
|
23
|
+
case key
|
24
|
+
when Numeric
|
25
|
+
super(key)
|
26
|
+
else
|
27
|
+
each do |tree_or_forest|
|
28
|
+
return tree_or_forest[key] if tree_or_forest.key?(key)
|
29
|
+
return nil if tree_or_forest.prefix?(key)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Trees are always smaller than forrests.
|
35
|
+
# Forests are compared by nesting depth, not number of trees.
|
36
|
+
#
|
37
|
+
def <=>(other)
|
38
|
+
return 0 if self == other
|
39
|
+
|
40
|
+
case other
|
41
|
+
when Forest
|
42
|
+
depth <=> other.depth
|
43
|
+
when Tree
|
44
|
+
1
|
45
|
+
else
|
46
|
+
raise ArgumentError, 'only forests and trees are comparable'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# The nesting depth of a forest
|
51
|
+
def depth
|
52
|
+
reduce(1) do |result, tree_or_forest|
|
53
|
+
[result, content_depth(tree_or_forest) + 1].max
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def key?(key)
|
58
|
+
any? { |tree_or_forest| tree_or_forest.key?(key) }
|
59
|
+
end
|
60
|
+
|
61
|
+
def prefix?(key)
|
62
|
+
any? { |tree_or_forest| tree_or_forest.prefix?(key) }
|
63
|
+
end
|
64
|
+
|
65
|
+
# Flattening a forest produces a tree with the equivalent view of key paths
|
66
|
+
#
|
67
|
+
def flatten
|
68
|
+
reduce(Tree[]) do |result, tree_or_forest|
|
69
|
+
case tree_or_forest
|
70
|
+
when Forest
|
71
|
+
tree_or_forest.flatten.merge(result)
|
72
|
+
else
|
73
|
+
tree_or_forest.merge(result)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def content_depth(content)
|
81
|
+
case content
|
82
|
+
when Forest
|
83
|
+
content.depth
|
84
|
+
else
|
85
|
+
0
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/key_tree/tree.rb
CHANGED
@@ -20,8 +20,8 @@ module KeyTree
|
|
20
20
|
super(Path[key_or_path])
|
21
21
|
end
|
22
22
|
|
23
|
-
def fetch(key_or_path, *args,
|
24
|
-
super(Path[key_or_path], *args,
|
23
|
+
def fetch(key_or_path, *args, &proc)
|
24
|
+
super(Path[key_or_path], *args, &proc)
|
25
25
|
end
|
26
26
|
|
27
27
|
def values_at(*keys)
|
@@ -31,7 +31,7 @@ module KeyTree
|
|
31
31
|
def []=(key_or_path, new_value)
|
32
32
|
path = Path[key_or_path]
|
33
33
|
|
34
|
-
|
34
|
+
delete_if { |key, _| path.conflict?(key) }
|
35
35
|
|
36
36
|
case new_value
|
37
37
|
when Hash
|
@@ -40,5 +40,38 @@ module KeyTree
|
|
40
40
|
super(path, new_value)
|
41
41
|
end
|
42
42
|
end
|
43
|
+
|
44
|
+
def key?(key_or_path)
|
45
|
+
super(Path[key_or_path])
|
46
|
+
end
|
47
|
+
|
48
|
+
def prefix?(key_or_path)
|
49
|
+
keys.any? { |key| key.prefix?(Path[key_or_path]) }
|
50
|
+
end
|
51
|
+
|
52
|
+
def conflict?(key_or_path)
|
53
|
+
keys.any? { |key| key.conflict?(Path[key_or_path]) }
|
54
|
+
end
|
55
|
+
|
56
|
+
# All trees are created equal. Forests are always larger than trees.
|
57
|
+
#
|
58
|
+
def <=>(other)
|
59
|
+
case other
|
60
|
+
when Forest
|
61
|
+
-1
|
62
|
+
when Tree
|
63
|
+
0
|
64
|
+
else
|
65
|
+
raise ArgumentError, 'only trees and forests are comparable'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# The merging of trees needs some extra consideration; due to the
|
70
|
+
# nature of key paths, prefix conflicts must be deleted
|
71
|
+
#
|
72
|
+
def merge(other)
|
73
|
+
delete_if { |key, _| other.conflict?(key) }
|
74
|
+
super
|
75
|
+
end
|
43
76
|
end
|
44
77
|
end
|
data/lib/key_tree/version.rb
CHANGED
data/lib/key_tree.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'key_tree/version'
|
2
2
|
require 'key_tree/tree'
|
3
|
+
require 'key_tree/forest'
|
3
4
|
|
4
5
|
# Manage a tree of keys
|
5
6
|
#
|
@@ -15,6 +16,8 @@ module KeyTree
|
|
15
16
|
case contents
|
16
17
|
when Hash
|
17
18
|
KeyTree::Tree[contents]
|
19
|
+
when Array
|
20
|
+
KeyTree::Forest[*contents]
|
18
21
|
else
|
19
22
|
raise ArgumentError, "can't load #{contents.class} into a KeyTree"
|
20
23
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: key_tree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Calle Englund
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-01-
|
11
|
+
date: 2018-01-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -79,7 +79,6 @@ files:
|
|
79
79
|
- ".travis.yml"
|
80
80
|
- CODE_OF_CONDUCT.md
|
81
81
|
- Gemfile
|
82
|
-
- Gemfile.lock
|
83
82
|
- LICENSE.txt
|
84
83
|
- README.md
|
85
84
|
- Rakefile
|
@@ -87,6 +86,7 @@ files:
|
|
87
86
|
- bin/setup
|
88
87
|
- key_tree.gemspec
|
89
88
|
- lib/key_tree.rb
|
89
|
+
- lib/key_tree/forest.rb
|
90
90
|
- lib/key_tree/path.rb
|
91
91
|
- lib/key_tree/tree.rb
|
92
92
|
- lib/key_tree/version.rb
|
data/Gemfile.lock
DELETED
@@ -1,51 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
key_tree (0.1.0)
|
5
|
-
|
6
|
-
GEM
|
7
|
-
remote: https://rubygems.org/
|
8
|
-
specs:
|
9
|
-
ast (2.3.0)
|
10
|
-
diff-lcs (1.3)
|
11
|
-
parallel (1.12.1)
|
12
|
-
parser (2.4.0.2)
|
13
|
-
ast (~> 2.3)
|
14
|
-
powerpack (0.1.1)
|
15
|
-
rainbow (3.0.0)
|
16
|
-
rake (10.5.0)
|
17
|
-
rspec (3.7.0)
|
18
|
-
rspec-core (~> 3.7.0)
|
19
|
-
rspec-expectations (~> 3.7.0)
|
20
|
-
rspec-mocks (~> 3.7.0)
|
21
|
-
rspec-core (3.7.1)
|
22
|
-
rspec-support (~> 3.7.0)
|
23
|
-
rspec-expectations (3.7.0)
|
24
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
25
|
-
rspec-support (~> 3.7.0)
|
26
|
-
rspec-mocks (3.7.0)
|
27
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
28
|
-
rspec-support (~> 3.7.0)
|
29
|
-
rspec-support (3.7.0)
|
30
|
-
rubocop (0.52.1)
|
31
|
-
parallel (~> 1.10)
|
32
|
-
parser (>= 2.4.0.2, < 3.0)
|
33
|
-
powerpack (~> 0.1)
|
34
|
-
rainbow (>= 2.2.2, < 4.0)
|
35
|
-
ruby-progressbar (~> 1.7)
|
36
|
-
unicode-display_width (~> 1.0, >= 1.0.1)
|
37
|
-
ruby-progressbar (1.9.0)
|
38
|
-
unicode-display_width (1.3.0)
|
39
|
-
|
40
|
-
PLATFORMS
|
41
|
-
ruby
|
42
|
-
|
43
|
-
DEPENDENCIES
|
44
|
-
bundler (~> 1.16)
|
45
|
-
key_tree!
|
46
|
-
rake (~> 10.0)
|
47
|
-
rspec (~> 3.0)
|
48
|
-
rubocop (~> 0.52)
|
49
|
-
|
50
|
-
BUNDLED WITH
|
51
|
-
1.16.0
|