key_tree 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|