key_tree 0.4.3 → 0.5.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/.rubocop.yml +4 -0
- data/RELEASE_NOTES.md +51 -0
- data/lib/key_tree/forest.rb +33 -47
- data/lib/key_tree/loader/nil.rb +10 -0
- data/lib/key_tree/loader.rb +7 -1
- data/lib/key_tree/tree.rb +3 -16
- data/lib/key_tree.rb +22 -16
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 47919143e113a4448b85cf800098227c6e47a5bcb92501e7d612722e7c06898f
|
4
|
+
data.tar.gz: 2b44f183949638a0a379297c0cb8ea6c50d0aa756c7e5a17e52324c9d3d96957
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2122707f4be42067a53db54c4fa98a13ecc55ff64fddc91c8ec3d40dbd5fddef9c1e3f01028ee3e662ce8e47d7f10bb4a3bc6d2ffbd156291e9223dedb4e283b
|
7
|
+
data.tar.gz: a1c2bc1e22162a646d2a88444e70017f093cde440fce3b9eeeefeba65d662c7e2d65ea3bad6f5c7d9f12cb3191a37e7fd4d4a380a1971e1aefa2ff4b2ed73c0b
|
data/.rubocop.yml
CHANGED
data/RELEASE_NOTES.md
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# Release Notes
|
2
|
+
|
3
|
+
## v0.5.0 – 2018-04-17
|
4
|
+
|
5
|
+
### Changed methods
|
6
|
+
|
7
|
+
* `KeyTree.load(type, serialization, prefix: nil)`
|
8
|
+
* `KeyTree::Forest#[key] { |key, original, incoming| }`
|
9
|
+
* `KeyTree::Forest#fetch(key) { |key, original, incoming| }`
|
10
|
+
* `KeyTree::Forest#flatten { |key, original, incoming| }`
|
11
|
+
* `KeyTree::Tree#merge { |key, original, incoming| }`
|
12
|
+
* `KeyTree::Tree#merge! { |key, original, incoming| }`
|
13
|
+
|
14
|
+
### New methods
|
15
|
+
|
16
|
+
* `KeyTree::Loader.fallback(loader)`
|
17
|
+
|
18
|
+
### New features
|
19
|
+
|
20
|
+
#### Merge value selection
|
21
|
+
Improve merge related methods in `KeyTree::Tree`, and `KeyTree::Forest`
|
22
|
+
to take a `Hash#merge` style block argument, to allow control of the result when a key i present on both sides of a merge operation.
|
23
|
+
|
24
|
+
* 083b25c Add merge value selector to Forest#[]
|
25
|
+
* e813e55 Add merge value selection to Forest#fetch
|
26
|
+
* 581bc82 Add method to get list of trees with key
|
27
|
+
* 0f66f03 Pass merge value selector via Forest#flatten
|
28
|
+
* df9b80e Pass any merge selection block to super
|
29
|
+
|
30
|
+
#### Key prefix for file loading
|
31
|
+
When a key file has a name like `prefix@name.ext`, the `prefix` part will be prepended to all keys loaded from the file.
|
32
|
+
|
33
|
+
* fbe333a Changed call syntax for KeyTree.load
|
34
|
+
* 595902c Load keytree with prefix from files with @ in name
|
35
|
+
* d23a7e1 Allow prepending a prefix when loading keys
|
36
|
+
|
37
|
+
#### Fallback for KeyTree loaders
|
38
|
+
Allow a fallback class for handling loading of file types where no loader is specified, e.g. to ignore all files with unrecognized extension for `KeyTree.load_all`.
|
39
|
+
|
40
|
+
* a9d096c Add tree loader fallback
|
41
|
+
|
42
|
+
### Bug fixes
|
43
|
+
|
44
|
+
#### Proper breadth first flattening
|
45
|
+
|
46
|
+
* ff327f2 Use tree enumarator for Forest#key? and #prefix?
|
47
|
+
* 74fa15d Rewrite Forest#[]
|
48
|
+
* 177de08 Use tree enumerator in Forest#[]
|
49
|
+
* d161fe1 Use tree enumerator in Forest#flatten
|
50
|
+
* b0c94df Add breadth-first enumerator for trees
|
51
|
+
* 3468f28 Remove forest vs tree sorting nonsense
|
data/lib/key_tree/forest.rb
CHANGED
@@ -11,7 +11,7 @@ module KeyTree
|
|
11
11
|
def self.[](*contents)
|
12
12
|
contents.reduce(Forest.new) do |result, content|
|
13
13
|
result << KeyTree[content]
|
14
|
-
end
|
14
|
+
end
|
15
15
|
end
|
16
16
|
|
17
17
|
# For a numeric key, return the n:th tree in the forest
|
@@ -22,70 +22,56 @@ module KeyTree
|
|
22
22
|
# key path matches in trees further away, returning nil. This preserves
|
23
23
|
# the constraints that only leaves may contain a value.
|
24
24
|
#
|
25
|
-
def [](key)
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
detect do |tree_or_forest|
|
31
|
-
return tree_or_forest[key] if tree_or_forest.key?(key)
|
32
|
-
return nil if tree_or_forest.prefix?(key)
|
33
|
-
end
|
34
|
-
end
|
25
|
+
def [](key, &merger)
|
26
|
+
return super(key) if key.is_a?(Numeric)
|
27
|
+
fetch(key, &merger)
|
28
|
+
rescue KeyError
|
29
|
+
nil
|
35
30
|
end
|
36
31
|
|
37
|
-
|
38
|
-
|
39
|
-
#
|
40
|
-
def <=>(other)
|
41
|
-
return 0 if self == other
|
32
|
+
def fetch(key)
|
33
|
+
return tree_with_key(key).fetch(key) unless block_given?
|
42
34
|
|
43
|
-
|
44
|
-
|
45
|
-
depth <=> other.depth
|
46
|
-
when Tree
|
47
|
-
1
|
48
|
-
else
|
49
|
-
raise ArgumentError, 'only forests and trees are comparable'
|
50
|
-
end
|
35
|
+
values = trees_with_key(key).map { |tree| tree.fetch(key) }
|
36
|
+
values.reverse.reduce { |left, right| yield(key, left, right) }
|
51
37
|
end
|
52
38
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
39
|
+
def tree_with_key(key)
|
40
|
+
result = trees.detect { |tree| tree.prefix?(key) }
|
41
|
+
result || raise(KeyError, "key not found: #{key}")
|
42
|
+
end
|
43
|
+
|
44
|
+
def trees_with_key(key)
|
45
|
+
result = trees.select { |tree| tree.prefix?(key) }
|
46
|
+
raise(KeyError, "key not found: #{key}") if result.empty?
|
47
|
+
result
|
58
48
|
end
|
59
49
|
|
60
50
|
def key?(key)
|
61
|
-
any? { |
|
51
|
+
trees.any? { |tree| tree.key?(key) }
|
62
52
|
end
|
63
53
|
|
64
54
|
def prefix?(key)
|
65
|
-
any? { |tree_or_forest| tree_or_forest.prefix?(key) }
|
55
|
+
trees.any? { |tree_or_forest| tree_or_forest.prefix?(key) }
|
66
56
|
end
|
67
57
|
|
68
58
|
# Flattening a forest produces a tree with the equivalent view of key paths
|
69
59
|
#
|
70
|
-
def flatten
|
71
|
-
reduce(Tree[]) do |result,
|
72
|
-
|
73
|
-
when Forest
|
74
|
-
tree_or_forest.flatten.merge(result)
|
75
|
-
else
|
76
|
-
tree_or_forest.merge(result)
|
77
|
-
end
|
60
|
+
def flatten(&merger)
|
61
|
+
trees.reverse_each.reduce(Tree[]) do |result, tree|
|
62
|
+
result.merge!(tree, &merger)
|
78
63
|
end
|
79
64
|
end
|
80
65
|
|
81
|
-
|
82
|
-
|
83
|
-
def
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
66
|
+
# Return a breadth-first Enumerator for all the trees in the forest,
|
67
|
+
# and any nested forests
|
68
|
+
def trees
|
69
|
+
Enumerator.new do |yielder|
|
70
|
+
remaining = [self]
|
71
|
+
remaining.each do |woods|
|
72
|
+
next yielder << woods if woods.is_a?(Tree)
|
73
|
+
woods.each { |wood| remaining << wood }
|
74
|
+
end
|
89
75
|
end
|
90
76
|
end
|
91
77
|
end
|
data/lib/key_tree/loader.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'key_tree/loader/nil'
|
2
|
+
|
1
3
|
module KeyTree
|
2
4
|
# Module to manage key tree loaders
|
3
5
|
module Loader
|
@@ -8,7 +10,7 @@ module KeyTree
|
|
8
10
|
|
9
11
|
def self.[](type)
|
10
12
|
type = type.to_sym if type.respond_to?(:to_sym)
|
11
|
-
loaders[type]
|
13
|
+
loaders[type] || @fallback
|
12
14
|
end
|
13
15
|
|
14
16
|
def self.[]=(type, loader_class)
|
@@ -16,6 +18,10 @@ module KeyTree
|
|
16
18
|
loaders[type] = loader_class
|
17
19
|
end
|
18
20
|
|
21
|
+
def self.fallback(loader)
|
22
|
+
@fallback = loader
|
23
|
+
end
|
24
|
+
|
19
25
|
private_class_method
|
20
26
|
|
21
27
|
def self.loaders
|
data/lib/key_tree/tree.rb
CHANGED
@@ -54,31 +54,18 @@ module KeyTree
|
|
54
54
|
keys.any? { |key| key.conflict?(Path[key_or_path]) }
|
55
55
|
end
|
56
56
|
|
57
|
-
# All trees are created equal. Forests are always larger than trees.
|
58
|
-
#
|
59
|
-
def <=>(other)
|
60
|
-
case other
|
61
|
-
when Forest
|
62
|
-
-1
|
63
|
-
when Tree
|
64
|
-
0
|
65
|
-
else
|
66
|
-
raise ArgumentError, 'only trees and forests are comparable'
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
57
|
# The merging of trees needs some extra consideration; due to the
|
71
58
|
# nature of key paths, prefix conflicts must be deleted
|
72
59
|
#
|
73
|
-
def merge!(other)
|
60
|
+
def merge!(other, &merger)
|
74
61
|
other = Tree[other] unless other.is_a?(Tree)
|
75
62
|
delete_if { |key, _| other.conflict?(key) }
|
76
63
|
super
|
77
64
|
end
|
78
65
|
alias << merge!
|
79
66
|
|
80
|
-
def merge(other)
|
81
|
-
dup.merge!(other)
|
67
|
+
def merge(other, &merger)
|
68
|
+
dup.merge!(other, &merger)
|
82
69
|
end
|
83
70
|
alias + merge
|
84
71
|
end
|
data/lib/key_tree.rb
CHANGED
@@ -27,35 +27,41 @@ module KeyTree
|
|
27
27
|
# Load a KeyTree from some external serialization
|
28
28
|
#
|
29
29
|
# load +type+: +serialization+
|
30
|
+
# load +key_prefix+, +type+: +serialization+
|
30
31
|
#
|
31
32
|
# +type+ is upcased to form a class name that should provide a
|
32
33
|
# +.load+ class method (like YAML or JSON does).
|
33
34
|
#
|
34
|
-
#
|
35
|
-
#
|
35
|
+
# If a +key_prefix+ is given, it will be prepended to the loaded data.
|
36
|
+
#
|
37
|
+
# Examples:
|
38
|
+
# load(:yaml, "---\na: 1\n")
|
36
39
|
# => {"a" => 1}
|
37
40
|
#
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
type, serialization = typed_serialization.flatten
|
41
|
+
# load(:yaml, "---\nb: 2\n", prefix: 'a')
|
42
|
+
# => {"a.b" => 2}
|
43
|
+
#
|
44
|
+
def self.load(type, serialization, prefix: nil)
|
45
|
+
type = type.to_sym unless type.nil?
|
44
46
|
loader = Loader[type]
|
45
|
-
|
46
|
-
|
47
|
-
|
47
|
+
contents = loader.load(serialization)
|
48
|
+
contents = { prefix => contents } unless prefix.nil?
|
49
|
+
|
50
|
+
self[contents].with_meta_data do |meta_data|
|
51
|
+
meta_data << { load: { type: type, loader: loader } }
|
52
|
+
meta_data << { load: { prefix: prefix } } unless prefix.nil?
|
48
53
|
end
|
49
54
|
end
|
50
55
|
|
51
56
|
# Open an external file and load contents into a KeyTree
|
52
|
-
#
|
57
|
+
# When the file basename begins with 'prefix@', the prefix
|
58
|
+
# is prepended to all keys in the filee.
|
53
59
|
def self.open(file_name)
|
54
60
|
type = File.extname(file_name)[/[^.]+/]
|
55
|
-
|
61
|
+
prefix = File.basename(file_name)[/(.+)@/, 1]
|
56
62
|
|
57
63
|
keytree = File.open(file_name, mode: 'rb:utf-8') do |file|
|
58
|
-
load_from_file(file, type)
|
64
|
+
load_from_file(file, type, prefix)
|
59
65
|
end
|
60
66
|
|
61
67
|
return keytree unless block_given?
|
@@ -79,8 +85,8 @@ module KeyTree
|
|
79
85
|
|
80
86
|
private_class_method
|
81
87
|
|
82
|
-
def self.load_from_file(file, type)
|
83
|
-
load(type
|
88
|
+
def self.load_from_file(file, type, prefix)
|
89
|
+
load(type, file.read, prefix: prefix).with_meta_data do |meta_data|
|
84
90
|
file_path = file.path
|
85
91
|
meta_data << { file: { path: file_path,
|
86
92
|
name: File.basename(file_path),
|
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.5.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-04-
|
11
|
+
date: 2018-04-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: git-version-bump
|
@@ -95,6 +95,7 @@ files:
|
|
95
95
|
- Gemfile
|
96
96
|
- LICENSE.txt
|
97
97
|
- README.md
|
98
|
+
- RELEASE_NOTES.md
|
98
99
|
- Rakefile
|
99
100
|
- bin/console
|
100
101
|
- bin/setup
|
@@ -102,6 +103,7 @@ files:
|
|
102
103
|
- lib/key_tree.rb
|
103
104
|
- lib/key_tree/forest.rb
|
104
105
|
- lib/key_tree/loader.rb
|
106
|
+
- lib/key_tree/loader/nil.rb
|
105
107
|
- lib/key_tree/meta_data.rb
|
106
108
|
- lib/key_tree/path.rb
|
107
109
|
- lib/key_tree/tree.rb
|