key_tree 0.5.2 → 0.8.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.
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KeyTree
4
+ # KeyTree refinements to core classes
5
+ module Refinements
6
+ refine Array do
7
+ def to_key_forest
8
+ Forest[*map(&:to_key_wood)]
9
+ end
10
+ alias_method :to_key_wood, :to_key_forest
11
+
12
+ def to_key_path
13
+ Path.new(self)
14
+ end
15
+ end
16
+
17
+ refine Hash do
18
+ def to_key_tree
19
+ Tree[self]
20
+ end
21
+ alias_method :to_key_wood, :to_key_tree
22
+ end
23
+
24
+ refine String do
25
+ def to_key_path
26
+ split('.').to_key_path
27
+ end
28
+ end
29
+
30
+ refine Symbol do
31
+ def to_key_path
32
+ to_s.to_key_path
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ require_relative 'forest'
39
+ require_relative 'path'
40
+ require_relative 'tree'
data/lib/key_tree/tree.rb CHANGED
@@ -1,118 +1,145 @@
1
- require 'key_tree/path'
2
- require 'key_tree/meta_data'
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require_relative 'meta_data'
5
+ require_relative 'path'
6
+ require_relative 'refinements'
7
+ require_relative 'refine/deep_hash'
8
+
9
+ module KeyTree # rubocop:disable Style/Documentation
10
+ using Refinements
11
+ using Refine::DeepHash
3
12
 
4
- module KeyTree
5
13
  # A tree of key-value lookup tables (hashes)
6
- class Tree < Hash
14
+ class Tree
7
15
  include MetaData
16
+ extend Forwardable
8
17
  #
9
18
  # KeyTree::Tree.new(+hash+)
10
19
  #
11
20
  # Initialize a new KeyTree from nested Hash:es
12
21
  #
13
22
  def self.[](hash = {})
14
- keytree = Tree.new
15
- hash.each do |key, value|
16
- keytree[key] = value
17
- end
18
- keytree
23
+ new(hash)
19
24
  end
20
25
 
21
- def [](key_or_path)
22
- super(Path[key_or_path])
26
+ def initialize(hash = {}, default = nil, &default_proc)
27
+ @hash = hash.to_h.deep_key_pathify
28
+ @default = default
29
+ @default_proc = default_proc
23
30
  end
24
31
 
25
- def fetch(key_or_path, *args, &missing_key)
26
- super(Path[key_or_path], *args, &missing_key)
27
- end
32
+ attr_reader :default, :default_proc
28
33
 
29
- def values_at(*keys)
30
- super(keys.map { |key_or_path| Path[key_or_path] })
31
- end
34
+ alias to_key_tree itself
35
+ alias to_key_wood itself
32
36
 
33
- def []=(key_or_path, new_value)
34
- path = Path[key_or_path]
37
+ delegate %i[empty? to_h to_json] => :@hash
38
+
39
+ # Convert a Tree to YAML, with string keys
40
+ #
41
+ # :call-seq:
42
+ # to_yaml => String
43
+ def to_yaml
44
+ to_h.deep_transform_keys(&:to_s).to_yaml
45
+ end
35
46
 
36
- delete_if { |key, _| path.conflict?(key) }
47
+ def [](key_path)
48
+ fetch(key_path) do
49
+ next default if default_proc.nil?
37
50
 
38
- case new_value
39
- when Hash
40
- new_value.each { |suffix, value| self[path + suffix] = value }
41
- else
42
- super(path, new_value)
51
+ default_proc.call(self, key_path)
43
52
  end
44
53
  end
45
54
 
46
- def key?(key_or_path)
47
- super(Path[key_or_path])
55
+ def fetch(key_path, *args, &key_missing)
56
+ @hash.deep_fetch(key_path.to_key_path, *args, &key_missing)
48
57
  end
49
58
 
50
- def default_key?(key_or_path)
51
- return unless default_proc
52
- default_proc.yield(self, Path[key_or_path])
53
- true
54
- rescue KeyError
55
- false
59
+ def store(key_path, new_value)
60
+ @hash.deep_store(key_path.to_key_path, new_value)
56
61
  end
57
62
 
58
- def prefix?(key_or_path)
59
- keys.any? { |key| key.prefix?(Path[key_or_path]) }
63
+ def store!(key_path, new_value)
64
+ store(key_path, new_value)
65
+ rescue KeyError
66
+ delete!(key_path)
67
+ retry
60
68
  end
69
+ alias []= store!
61
70
 
62
- def conflict?(key_or_path)
63
- keys.any? { |key| key.conflict?(Path[key_or_path]) }
71
+ def delete(key_path)
72
+ @hash.deep_delete(key_path.to_key_path)
64
73
  end
65
74
 
66
- # The merging of trees needs some extra consideration; due to the
67
- # nature of key paths, prefix conflicts must be deleted
68
- #
69
- def merge!(other, &merger)
70
- other = Tree[other] unless other.is_a?(Tree)
71
- delete_if { |key, _| other.conflict?(key) }
72
- super
75
+ def delete!(key_path)
76
+ delete(key_path)
77
+ rescue KeyError
78
+ key_path = key_path[0..-2]
79
+ retry
73
80
  end
74
- alias << merge!
75
81
 
76
- def merge(other, &merger)
77
- dup.merge!(other, &merger)
82
+ def values_at(*key_paths)
83
+ key_paths.map { |key_path| self[key_path] }
78
84
  end
79
- alias + merge
80
85
 
81
- # Format +fmtstr+ with values from the Tree
82
- def format(fmtstr)
83
- Kernel.format(fmtstr, Hash.new { |_, key| fetch(key) })
86
+ # Return all maximal key paths in a tree
87
+ #
88
+ # :call-seq:
89
+ # keys => Array of KeyTree::Path
90
+ def keys
91
+ @hash.deep.each_with_object([]) do |(key_path, value), result|
92
+ result << key_path.to_key_path unless value.is_a?(Hash)
93
+ end
84
94
  end
95
+ alias key_paths keys
85
96
 
86
- # Convert a Tree back to nested hashes.
87
- #
88
- # to_h => Hash, with symbol keys
89
- # to_h(string_keys: true) => Hash, with string keys
90
- def to_h(**kwargs)
91
- to_hash_tree(**kwargs)
97
+ def include?(key_path)
98
+ fetch(key_path)
99
+ true
100
+ rescue KeyError
101
+ false
92
102
  end
103
+ alias key? include?
104
+ alias has_key? include?
105
+ alias key_path? include?
106
+ alias has_key_path? include?
107
+
108
+ def prefix?(key_path)
109
+ key_path.to_key_path.reduce(@hash) do |subtree, key|
110
+ return false unless subtree.is_a?(Hash)
111
+ return false unless subtree.key?(key)
93
112
 
94
- # Convert a Tree to JSON, with string keys
95
- def to_json
96
- to_hash_tree(string_keys: true).to_json
113
+ subtree[key]
114
+ end
115
+ true
97
116
  end
117
+ alias has_prefix? prefix?
98
118
 
99
- # Convert a Tree to YAML, with string keys
100
- def to_yaml
101
- to_hash_tree(string_keys: true).to_yaml
119
+ def value?(needle)
120
+ @hash.deep.lazy.any? { |(_, straw)| straw == needle }
102
121
  end
122
+ alias has_value? value?
103
123
 
104
- private
124
+ # Merge values from +other+ tree into self
125
+ #
126
+ # :call-seq:
127
+ # merge!(other) => self
128
+ # merge!(other) { |key, lhs, rhs| } => self
129
+ def merge!(other, &block)
130
+ @hash.deep_merge!(other.to_h, &block)
131
+ self
132
+ end
133
+ alias << merge!
105
134
 
106
- def to_hash_tree(key_pairs = self, string_keys: false)
107
- hash = key_pairs.group_by do |path, _|
108
- string_keys ? path.first.to_s : path.first
109
- end
110
- hash.transform_values do |next_level|
111
- next_level.map! { |path, value| [path[1..-1], value] }
112
- first_key, first_value = next_level.first
113
- next first_value if first_key.nil? || first_key.empty?
114
- to_hash_tree(next_level)
115
- end
135
+ # Return a new tree by merging values from +other+ tree
136
+ #
137
+ # :call-seq:
138
+ # merge(other) => Tree
139
+ # merge(other) { |key, lhs, rhs| } => Tree
140
+ def merge(other, &block)
141
+ @hash.deep_merge(other.to_h, &block).to_key_tree
116
142
  end
143
+ alias + merge
117
144
  end
118
145
  end
@@ -1,6 +1,10 @@
1
- require 'git-version-bump'
1
+ # frozen_string_literal: true
2
2
 
3
- module KeyTree
4
- VERSION = GVB.version.freeze
5
- DATE = GVB.date.freeze
3
+ module KeyTree # rubocop:disable Style/Documentation
4
+ begin
5
+ require 'git-version-bump'
6
+ VERSION = GVB.version.freeze
7
+ rescue LoadError
8
+ VERSION = '0.0.0.UNDEF'
9
+ end
6
10
  end
@@ -0,0 +1,8 @@
1
+ {
2
+ "folders":
3
+ [
4
+ {
5
+ "path": "."
6
+ }
7
+ ]
8
+ }
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.5.2
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Calle Englund
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-05-19 00:00:00.000000000 Z
11
+ date: 2021-05-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: git-version-bump
@@ -16,86 +16,170 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.15'
19
+ version: 0.17.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0.15'
26
+ version: 0.17.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.16'
33
+ version: '2.2'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.16'
40
+ version: '2.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: codecov
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.5.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.5.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.14.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.14.0
41
69
  - !ruby/object:Gem::Dependency
42
70
  name: rake
43
71
  requirement: !ruby/object:Gem::Requirement
44
72
  requirements:
45
73
  - - "~>"
46
74
  - !ruby/object:Gem::Version
47
- version: '10.0'
75
+ version: '13.0'
48
76
  type: :development
49
77
  prerelease: false
50
78
  version_requirements: !ruby/object:Gem::Requirement
51
79
  requirements:
52
80
  - - "~>"
53
81
  - !ruby/object:Gem::Version
54
- version: '10.0'
82
+ version: '13.0'
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: rspec
57
85
  requirement: !ruby/object:Gem::Requirement
58
86
  requirements:
59
87
  - - "~>"
60
88
  - !ruby/object:Gem::Version
61
- version: '3.0'
89
+ version: '3.10'
62
90
  type: :development
63
91
  prerelease: false
64
92
  version_requirements: !ruby/object:Gem::Requirement
65
93
  requirements:
66
94
  - - "~>"
67
95
  - !ruby/object:Gem::Version
68
- version: '3.0'
96
+ version: '3.10'
69
97
  - !ruby/object:Gem::Dependency
70
98
  name: rubocop
71
99
  requirement: !ruby/object:Gem::Requirement
72
100
  requirements:
73
101
  - - "~>"
74
102
  - !ruby/object:Gem::Version
75
- version: '0.52'
103
+ version: '1.15'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.15'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop-rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.5.1
76
118
  type: :development
77
119
  prerelease: false
78
120
  version_requirements: !ruby/object:Gem::Requirement
79
121
  requirements:
80
122
  - - "~>"
81
123
  - !ruby/object:Gem::Version
82
- version: '0.52'
83
- description: Manage trees of keys
124
+ version: 0.5.1
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop-rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '2.3'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '2.3'
139
+ - !ruby/object:Gem::Dependency
140
+ name: ruby-prof
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '1.4'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '1.4'
153
+ - !ruby/object:Gem::Dependency
154
+ name: simplecov
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: 0.21.0
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: 0.21.0
167
+ description:
84
168
  email:
85
169
  - calle@discord.bofh.se
86
170
  executables: []
87
171
  extensions: []
88
172
  extra_rdoc_files: []
89
173
  files:
174
+ - ".github/workflows/ruby.yml"
90
175
  - ".gitignore"
91
176
  - ".rspec"
92
177
  - ".rubocop.yml"
93
- - ".travis.yml"
178
+ - CHANGELOG.md
94
179
  - CODE_OF_CONDUCT.md
95
180
  - Gemfile
96
181
  - LICENSE.txt
97
182
  - README.md
98
- - RELEASE_NOTES.md
99
183
  - Rakefile
100
184
  - bin/console
101
185
  - bin/setup
@@ -106,13 +190,16 @@ files:
106
190
  - lib/key_tree/loader/nil.rb
107
191
  - lib/key_tree/meta_data.rb
108
192
  - lib/key_tree/path.rb
193
+ - lib/key_tree/refine/deep_hash.rb
194
+ - lib/key_tree/refinements.rb
109
195
  - lib/key_tree/tree.rb
110
196
  - lib/key_tree/version.rb
197
+ - ruby-keytree.sublime-project
111
198
  homepage: https://github.com/notcalle/ruby-keytree
112
199
  licenses:
113
200
  - MIT
114
201
  metadata: {}
115
- post_install_message:
202
+ post_install_message:
116
203
  rdoc_options: []
117
204
  require_paths:
118
205
  - lib
@@ -120,16 +207,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
120
207
  requirements:
121
208
  - - ">="
122
209
  - !ruby/object:Gem::Version
123
- version: '0'
210
+ version: '2.6'
211
+ - - "<"
212
+ - !ruby/object:Gem::Version
213
+ version: '4.0'
124
214
  required_rubygems_version: !ruby/object:Gem::Requirement
125
215
  requirements:
126
216
  - - ">="
127
217
  - !ruby/object:Gem::Version
128
218
  version: '0'
129
219
  requirements: []
130
- rubyforge_project:
131
- rubygems_version: 2.7.6
132
- signing_key:
220
+ rubygems_version: 3.2.15
221
+ signing_key:
133
222
  specification_version: 4
134
223
  summary: Manage trees of keys
135
224
  test_files: []