unobtainium 0.5.1 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +5 -1
- data/README.md +3 -2
- data/lib/unobtainium.rb +0 -1
- data/lib/unobtainium/driver.rb +7 -1
- data/lib/unobtainium/runtime.rb +2 -0
- data/lib/unobtainium/support/runner.rb +0 -4
- data/lib/unobtainium/version.rb +1 -1
- data/lib/unobtainium/world.rb +37 -16
- data/media/screenshot.jpg +0 -0
- data/media/video.ogv +0 -0
- data/spec/driver_spec.rb +40 -28
- data/spec/runner_spec.rb +19 -0
- data/spec/spec_helper.rb +1 -0
- data/unobtainium.gemspec +1 -0
- metadata +18 -31
- data/lib/unobtainium/config.rb +0 -304
- data/lib/unobtainium/pathed_hash.rb +0 -199
- data/lib/unobtainium/recursive_merge.rb +0 -55
- data/spec/config_spec.rb +0 -119
- data/spec/data/array.yaml +0 -3
- data/spec/data/arraymerge-local.yaml +0 -2
- data/spec/data/arraymerge.yaml +0 -3
- data/spec/data/empty.yml +0 -1
- data/spec/data/hash.yml +0 -3
- data/spec/data/hashmerge-local.yml +0 -4
- data/spec/data/hashmerge.yml +0 -7
- data/spec/data/mergefail-local.yaml +0 -2
- data/spec/data/mergefail.yaml +0 -5
- data/spec/data/test.json +0 -4
- data/spec/data/world.yml +0 -5
- data/spec/pathed_hash_spec.rb +0 -192
@@ -1,199 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
#
|
3
|
-
# unobtainium
|
4
|
-
# https://github.com/jfinkhaeuser/unobtainium
|
5
|
-
#
|
6
|
-
# Copyright (c) 2016 Jens Finkhaeuser and other unobtainium contributors.
|
7
|
-
# All rights reserved.
|
8
|
-
#
|
9
|
-
|
10
|
-
require 'unobtainium/recursive_merge'
|
11
|
-
|
12
|
-
module Unobtainium
|
13
|
-
|
14
|
-
##
|
15
|
-
# The PathedHash class wraps Hash by offering pathed access on top of
|
16
|
-
# regular access, i.e. instead of `h["first"]["second"]` you can write
|
17
|
-
# `h["first.second"]`.
|
18
|
-
#
|
19
|
-
# The main benefit is much simpler code for accessing nested structured.
|
20
|
-
# For any given path, PathedHash will return nil from `[]` if *any* of
|
21
|
-
# the path components do not exist.
|
22
|
-
#
|
23
|
-
# Similarly, intermediate nodes will be created when you write a value
|
24
|
-
# for a path.
|
25
|
-
#
|
26
|
-
# PathedHash also includes RecursiveMerge.
|
27
|
-
class PathedHash
|
28
|
-
include RecursiveMerge
|
29
|
-
|
30
|
-
DEFAULT_PROC = proc do |hash, key|
|
31
|
-
case key
|
32
|
-
when String
|
33
|
-
sym = key.to_sym
|
34
|
-
hash[sym] if hash.key?(sym)
|
35
|
-
when Symbol
|
36
|
-
str = key.to_s
|
37
|
-
hash[str] if hash.key?(str)
|
38
|
-
end
|
39
|
-
end.freeze
|
40
|
-
|
41
|
-
##
|
42
|
-
# Initializer. Accepts `nil`, hashes or pathed hashes.
|
43
|
-
#
|
44
|
-
# @param init [NilClass, Hash] initial values.
|
45
|
-
def initialize(init = nil)
|
46
|
-
if init.nil?
|
47
|
-
@data = {}
|
48
|
-
else
|
49
|
-
@data = init.dup
|
50
|
-
end
|
51
|
-
@separator = '.'
|
52
|
-
|
53
|
-
@data.default_proc = DEFAULT_PROC
|
54
|
-
end
|
55
|
-
|
56
|
-
# @return [String] the separator is the character or pattern splitting paths.
|
57
|
-
attr_accessor :separator
|
58
|
-
|
59
|
-
# @api private
|
60
|
-
# Methods redefined to support pathed read access.
|
61
|
-
READ_METHODS = [
|
62
|
-
:[], :default, :delete, :fetch, :has_key?, :include?, :key?, :member?,
|
63
|
-
].freeze
|
64
|
-
|
65
|
-
# @api private
|
66
|
-
# Methods redefined to support pathed write access.
|
67
|
-
WRITE_METHODS = [
|
68
|
-
:[]=, :store,
|
69
|
-
].freeze
|
70
|
-
|
71
|
-
##
|
72
|
-
# @return [RegExp] the pattern to split paths at; based on `separator`
|
73
|
-
def split_pattern
|
74
|
-
/(?<!\\)#{Regexp.escape(@separator)}/
|
75
|
-
end
|
76
|
-
|
77
|
-
(READ_METHODS + WRITE_METHODS).each do |method|
|
78
|
-
# Wrap all accessor functions to deal with paths
|
79
|
-
define_method(method) do |*args, &block|
|
80
|
-
# If there are no arguments, there's nothing to do with paths. Just
|
81
|
-
# delegate to the hash.
|
82
|
-
if args.empty?
|
83
|
-
return @data.send(method, *args, &block)
|
84
|
-
end
|
85
|
-
|
86
|
-
# With any of the dispatch methods, we know that the first argument has
|
87
|
-
# to be a key. We'll try to split it by the path separator.
|
88
|
-
components = args[0].to_s.split(split_pattern)
|
89
|
-
loop do
|
90
|
-
if components.empty? or not components[0].empty?
|
91
|
-
break
|
92
|
-
end
|
93
|
-
components.shift
|
94
|
-
end
|
95
|
-
|
96
|
-
# If there are no components, return self/the root
|
97
|
-
if components.empty?
|
98
|
-
return self
|
99
|
-
end
|
100
|
-
|
101
|
-
# This PathedHash is already the leaf-most Hash
|
102
|
-
if components.length == 1
|
103
|
-
# Weird edge case: if we didn't have to shift anything, then it's
|
104
|
-
# possible we inadvertently changed a symbol key into a string key,
|
105
|
-
# which could mean looking fails.
|
106
|
-
# We can detect that by comparing copy[0] to a symbolized version of
|
107
|
-
# components[0].
|
108
|
-
copy = args.dup
|
109
|
-
if copy[0] != components[0].to_sym
|
110
|
-
copy[0] = components[0]
|
111
|
-
end
|
112
|
-
return @data.send(method, *copy, &block)
|
113
|
-
end
|
114
|
-
|
115
|
-
# Deal with other paths. The frustrating part here is that for nested
|
116
|
-
# hashes, only this outermost one is guaranteed to know anything about
|
117
|
-
# path splitting, so we'll have to recurse down to the leaf here.
|
118
|
-
#
|
119
|
-
# For write methods, we need to create intermediary hashes.
|
120
|
-
leaf = recursive_fetch(components, @data,
|
121
|
-
create: WRITE_METHODS.include?(method))
|
122
|
-
if leaf.is_a? Hash
|
123
|
-
leaf.default_proc = DEFAULT_PROC
|
124
|
-
end
|
125
|
-
if leaf.nil?
|
126
|
-
leaf = @data
|
127
|
-
end
|
128
|
-
|
129
|
-
# If we have a leaf, we want to send the requested method to that
|
130
|
-
# leaf.
|
131
|
-
copy = args.dup
|
132
|
-
copy[0] = components.last
|
133
|
-
return leaf.send(method, *copy, &block)
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
# @return [String] string representation
|
138
|
-
def to_s
|
139
|
-
@data.to_s
|
140
|
-
end
|
141
|
-
|
142
|
-
# @return [PathedHash] duplicate, as `.dup` usually works
|
143
|
-
def dup
|
144
|
-
PathedHash.new(@data.dup)
|
145
|
-
end
|
146
|
-
|
147
|
-
# In place merge, as it usually works for hashes.
|
148
|
-
# @return [PathedHash] self
|
149
|
-
def merge!(*args, &block)
|
150
|
-
# FIXME: we may need other methods like this. This is used by
|
151
|
-
# RecursiveMerge, so we know it's required.
|
152
|
-
PathedHash.new(super)
|
153
|
-
end
|
154
|
-
|
155
|
-
##
|
156
|
-
# Map any missing method to the Hash implementation
|
157
|
-
def respond_to_missing?(meth, include_private = false)
|
158
|
-
if not @data.nil? and @data.respond_to?(meth, include_private)
|
159
|
-
return true
|
160
|
-
end
|
161
|
-
return super
|
162
|
-
end
|
163
|
-
|
164
|
-
##
|
165
|
-
# Map any missing method to the Hash implementation
|
166
|
-
def method_missing(meth, *args, &block)
|
167
|
-
if not @data.nil? and @data.respond_to?(meth)
|
168
|
-
return @data.send(meth.to_s, *args, &block)
|
169
|
-
end
|
170
|
-
return super
|
171
|
-
end
|
172
|
-
|
173
|
-
private
|
174
|
-
|
175
|
-
##
|
176
|
-
# Given the path components, recursively fetch any but the last key.
|
177
|
-
def recursive_fetch(path, data, options = {})
|
178
|
-
# For the leaf element, we do nothing because that's where we want to
|
179
|
-
# dispatch to.
|
180
|
-
if path.length == 1
|
181
|
-
return data
|
182
|
-
end
|
183
|
-
|
184
|
-
# Split path into head and tail; for the next iteration, we'll look use only
|
185
|
-
# head, and pass tail on recursively.
|
186
|
-
head = path[0]
|
187
|
-
tail = path.slice(1, path.length)
|
188
|
-
|
189
|
-
# If we're a write function, then we need to create intermediary objects,
|
190
|
-
# i.e. what's at head if nothing is there.
|
191
|
-
if options[:create] and data[head].nil?
|
192
|
-
data[head] = {}
|
193
|
-
end
|
194
|
-
|
195
|
-
# Ok, recurse.
|
196
|
-
return recursive_fetch(tail, data[head], options)
|
197
|
-
end
|
198
|
-
end # class PathedHash
|
199
|
-
end # module Unobtainium
|
@@ -1,55 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
#
|
3
|
-
# unobtainium
|
4
|
-
# https://github.com/jfinkhaeuser/unobtainium
|
5
|
-
#
|
6
|
-
# Copyright (c) 2016 Jens Finkhaeuser and other unobtainium contributors.
|
7
|
-
# All rights reserved.
|
8
|
-
#
|
9
|
-
|
10
|
-
module Unobtainium
|
11
|
-
##
|
12
|
-
# Provides recursive merge functions for hashes. Used in PathedHash.
|
13
|
-
module RecursiveMerge
|
14
|
-
##
|
15
|
-
# Recursively merge `:other` into this Hash.
|
16
|
-
#
|
17
|
-
# This starts by merging the leaf-most Hash entries. Arrays are merged
|
18
|
-
# by addition.
|
19
|
-
#
|
20
|
-
# For everything that's neither Hash or Array, if the `:overwrite`
|
21
|
-
# parameter is true, the entry from `:other` is used. Otherwise the entry
|
22
|
-
# from `:self` is used.
|
23
|
-
#
|
24
|
-
# @param other [Hash] the hash to merge into `:self`
|
25
|
-
# @param overwrite [Boolean] see method description.
|
26
|
-
def recursive_merge!(other, overwrite = true)
|
27
|
-
if other.nil?
|
28
|
-
return self
|
29
|
-
end
|
30
|
-
|
31
|
-
merger = proc do |_, v1, v2|
|
32
|
-
# rubocop:disable Style/GuardClause
|
33
|
-
if v1.is_a? Hash and v2.is_a? Hash
|
34
|
-
next v1.merge(v2, &merger)
|
35
|
-
elsif v1.is_a? Array and v2.is_a? Array
|
36
|
-
next v1 + v2
|
37
|
-
end
|
38
|
-
if overwrite
|
39
|
-
next v2
|
40
|
-
else
|
41
|
-
next v1
|
42
|
-
end
|
43
|
-
# rubocop:enable Style/GuardClause
|
44
|
-
end
|
45
|
-
merge!(other, &merger)
|
46
|
-
end
|
47
|
-
|
48
|
-
##
|
49
|
-
# Same as `dup.recursive_merge!`
|
50
|
-
# @param (see #recursive_merge!)
|
51
|
-
def recursive_merge(other, overwrite = true)
|
52
|
-
dup.recursive_merge!(other, overwrite)
|
53
|
-
end
|
54
|
-
end # module RecursiveMerge
|
55
|
-
end # module Unobtainium
|
data/spec/config_spec.rb
DELETED
@@ -1,119 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require_relative '../lib/unobtainium/config'
|
3
|
-
|
4
|
-
describe ::Unobtainium::Config do
|
5
|
-
before :each do
|
6
|
-
@data_path = File.join(File.dirname(__FILE__), 'data')
|
7
|
-
end
|
8
|
-
|
9
|
-
it "fails to load a nonexistent file" do
|
10
|
-
expect { ::Unobtainium::Config.load_config("_nope_.yaml") }.to \
|
11
|
-
raise_error Errno::ENOENT
|
12
|
-
end
|
13
|
-
|
14
|
-
it "is asked to load an unrecognized extension" do
|
15
|
-
expect { ::Unobtainium::Config.load_config("_nope_.cfg") }.to \
|
16
|
-
raise_error ArgumentError
|
17
|
-
end
|
18
|
-
|
19
|
-
it "loads a yaml config with a top-level hash correctly" do
|
20
|
-
config = File.join(@data_path, 'hash.yml')
|
21
|
-
cfg = ::Unobtainium::Config.load_config(config)
|
22
|
-
|
23
|
-
expect(cfg["foo"]).to eql "bar"
|
24
|
-
expect(cfg["baz"]).to eql "quux"
|
25
|
-
end
|
26
|
-
|
27
|
-
it "loads a yaml config with a top-level array correctly" do
|
28
|
-
config = File.join(@data_path, 'array.yaml')
|
29
|
-
cfg = ::Unobtainium::Config.load_config(config)
|
30
|
-
|
31
|
-
expect(cfg["config"]).to eql %w(foo bar)
|
32
|
-
end
|
33
|
-
|
34
|
-
it "loads a JSON config correctly" do
|
35
|
-
config = File.join(@data_path, 'test.json')
|
36
|
-
cfg = ::Unobtainium::Config.load_config(config)
|
37
|
-
|
38
|
-
expect(cfg["foo"]).to eql "bar"
|
39
|
-
expect(cfg["baz"]).to eql 42
|
40
|
-
end
|
41
|
-
|
42
|
-
it "merges a hashed config correctly" do
|
43
|
-
config = File.join(@data_path, 'hashmerge.yml')
|
44
|
-
cfg = ::Unobtainium::Config.load_config(config)
|
45
|
-
|
46
|
-
expect(cfg["asdf"]).to eql 1
|
47
|
-
expect(cfg["foo.bar"]).to eql "baz"
|
48
|
-
expect(cfg["foo.quux"]).to eql [1, 42]
|
49
|
-
expect(cfg["foo.baz"]).to eql 3.14
|
50
|
-
expect(cfg["blargh"]).to eql false
|
51
|
-
end
|
52
|
-
|
53
|
-
it "merges an array config correctly" do
|
54
|
-
config = File.join(@data_path, 'arraymerge.yaml')
|
55
|
-
cfg = ::Unobtainium::Config.load_config(config)
|
56
|
-
|
57
|
-
expect(cfg["config"]).to eql %w(foo bar baz)
|
58
|
-
end
|
59
|
-
|
60
|
-
it "merges an array and hash config" do
|
61
|
-
config = File.join(@data_path, 'mergefail.yaml')
|
62
|
-
cfg = ::Unobtainium::Config.load_config(config)
|
63
|
-
|
64
|
-
expect(cfg["config"]).to eql %w(array in main config)
|
65
|
-
expect(cfg["local"]).to eql "override is a hash"
|
66
|
-
end
|
67
|
-
|
68
|
-
it "overrides configuration variables from the environment" do
|
69
|
-
config = File.join(@data_path, 'hash.yml')
|
70
|
-
cfg = ::Unobtainium::Config.load_config(config)
|
71
|
-
|
72
|
-
ENV["BAZ"] = "override"
|
73
|
-
expect(cfg["foo"]).to eql "bar"
|
74
|
-
expect(cfg["baz"]).to eql "override"
|
75
|
-
end
|
76
|
-
|
77
|
-
it "treats an empty YAML file as an empty hash" do
|
78
|
-
config = File.join(@data_path, 'empty.yml')
|
79
|
-
cfg = ::Unobtainium::Config.load_config(config)
|
80
|
-
expect(cfg).to be_empty
|
81
|
-
end
|
82
|
-
|
83
|
-
it "extends configuration hashes" do
|
84
|
-
config = File.join(@data_path, 'driverconfig.yml')
|
85
|
-
cfg = ::Unobtainium::Config.load_config(config)
|
86
|
-
|
87
|
-
# First, test for non-extended values
|
88
|
-
expect(cfg["drivers.mock.mockoption"]).to eql 42
|
89
|
-
expect(cfg["drivers.branch1.branch1option"]).to eql "foo"
|
90
|
-
expect(cfg["drivers.branch2.branch2option"]).to eql "bar"
|
91
|
-
expect(cfg["drivers.leaf.leafoption"]).to eql "baz"
|
92
|
-
|
93
|
-
# Now test extended values
|
94
|
-
expect(cfg["drivers.branch1.mockoption"]).to eql 42
|
95
|
-
expect(cfg["drivers.branch2.mockoption"]).to eql 42
|
96
|
-
expect(cfg["drivers.leaf.mockoption"]).to eql 42
|
97
|
-
|
98
|
-
expect(cfg["drivers.branch2.branch1option"]).to eql "foo"
|
99
|
-
expect(cfg["drivers.leaf.branch1option"]).to eql "override" # not "foo" !
|
100
|
-
|
101
|
-
expect(cfg["drivers.leaf.branch2option"]).to eql "bar"
|
102
|
-
|
103
|
-
# Also test that all levels go back to base == mock
|
104
|
-
expect(cfg["drivers.branch1.base"]).to eql 'mock'
|
105
|
-
expect(cfg["drivers.branch2.base"]).to eql 'mock'
|
106
|
-
expect(cfg["drivers.leaf.base"]).to eql 'mock'
|
107
|
-
end
|
108
|
-
|
109
|
-
it "extends configuration hashes when the base does not exist" do
|
110
|
-
config = File.join(@data_path, 'driverconfig.yml')
|
111
|
-
cfg = ::Unobtainium::Config.load_config(config)
|
112
|
-
|
113
|
-
# Ensure the hash contains its own value
|
114
|
-
expect(cfg["drivers.base_does_not_exist.some"]).to eql "value"
|
115
|
-
|
116
|
-
# Also ensure the "base" is set properly
|
117
|
-
expect(cfg["drivers.base_does_not_exist.base"]).to eql "nonexistent_base"
|
118
|
-
end
|
119
|
-
end
|
data/spec/data/array.yaml
DELETED
data/spec/data/arraymerge.yaml
DELETED
data/spec/data/empty.yml
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
---
|
data/spec/data/hash.yml
DELETED
data/spec/data/hashmerge.yml
DELETED
data/spec/data/mergefail.yaml
DELETED
data/spec/data/test.json
DELETED
data/spec/data/world.yml
DELETED
data/spec/pathed_hash_spec.rb
DELETED
@@ -1,192 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require_relative '../lib/unobtainium/pathed_hash'
|
3
|
-
|
4
|
-
describe ::Unobtainium::PathedHash do
|
5
|
-
describe "#initialize" do
|
6
|
-
it "can be constructed without values" do
|
7
|
-
ph = ::Unobtainium::PathedHash.new
|
8
|
-
expect(ph.empty?).to eql true
|
9
|
-
end
|
10
|
-
|
11
|
-
it "can be constructed with values" do
|
12
|
-
ph = ::Unobtainium::PathedHash.new(foo: 42)
|
13
|
-
expect(ph.empty?).to eql false
|
14
|
-
expect(ph[:foo]).to eql 42
|
15
|
-
end
|
16
|
-
|
17
|
-
it "can be constructed with a nil value" do
|
18
|
-
ph = ::Unobtainium::PathedHash.new(nil)
|
19
|
-
expect(ph.empty?).to eql true
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
describe "Hash-like" do
|
24
|
-
it "responds to Hash functions" do
|
25
|
-
ph = ::Unobtainium::PathedHash.new
|
26
|
-
[:invert, :delete, :fetch].each do |meth|
|
27
|
-
expect(ph.respond_to?(meth)).to eql true
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
it "can be used like a hash" do
|
32
|
-
ph = ::Unobtainium::PathedHash.new(foo: 42)
|
33
|
-
inverted = ph.invert
|
34
|
-
expect(inverted.empty?).to eql false
|
35
|
-
expect(inverted[42]).to eql :foo
|
36
|
-
end
|
37
|
-
|
38
|
-
it "delegates to Hash if it's nothing to do with paths" do
|
39
|
-
ph = ::Unobtainium::PathedHash.new(foo: 42)
|
40
|
-
expect(ph.default).to be_nil
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
it "can recursively read entries via a path" do
|
45
|
-
sample = {
|
46
|
-
"foo" => 42,
|
47
|
-
"bar" => {
|
48
|
-
"baz" => "quux",
|
49
|
-
"blah" => [1, 2],
|
50
|
-
}
|
51
|
-
}
|
52
|
-
ph = ::Unobtainium::PathedHash.new(sample)
|
53
|
-
|
54
|
-
expect(ph["foo"]).to eql 42
|
55
|
-
expect(ph["bar.baz"]).to eql "quux"
|
56
|
-
expect(ph["bar.blah"]).to eql [1, 2]
|
57
|
-
|
58
|
-
expect(ph["nope"]).to eql nil
|
59
|
-
expect(ph["bar.nope"]).to eql nil
|
60
|
-
end
|
61
|
-
|
62
|
-
it "behaves consistently if in a path the first node cannot be found" do
|
63
|
-
sample = {
|
64
|
-
"foo" => 42,
|
65
|
-
}
|
66
|
-
ph = ::Unobtainium::PathedHash.new(sample)
|
67
|
-
|
68
|
-
expect(ph["nope.bar"]).to eql nil
|
69
|
-
end
|
70
|
-
|
71
|
-
it "can be used with indifferent access from string key" do
|
72
|
-
sample = {
|
73
|
-
"foo" => 42,
|
74
|
-
}
|
75
|
-
ph = ::Unobtainium::PathedHash.new(sample)
|
76
|
-
|
77
|
-
expect(ph["foo"]).to eql 42
|
78
|
-
expect(ph[:foo]).to eql 42
|
79
|
-
end
|
80
|
-
|
81
|
-
it "can be used with indifferent access from symbol key" do
|
82
|
-
sample = {
|
83
|
-
foo: 42,
|
84
|
-
bar: {
|
85
|
-
baz: 'quux',
|
86
|
-
}
|
87
|
-
}
|
88
|
-
ph = ::Unobtainium::PathedHash.new(sample)
|
89
|
-
|
90
|
-
expect(ph["foo"]).to eql 42
|
91
|
-
expect(ph[:foo]).to eql 42
|
92
|
-
|
93
|
-
expect(ph['bar.baz']).to eql 'quux'
|
94
|
-
end
|
95
|
-
|
96
|
-
it "treats a single separator as the root" do
|
97
|
-
sample = { "foo" => 42 }
|
98
|
-
ph = ::Unobtainium::PathedHash.new(sample)
|
99
|
-
|
100
|
-
expect(ph[ph.separator]["foo"]).to eql 42
|
101
|
-
end
|
102
|
-
|
103
|
-
it "treats an empty path as the root" do
|
104
|
-
sample = { "foo" => 42 }
|
105
|
-
ph = ::Unobtainium::PathedHash.new(sample)
|
106
|
-
|
107
|
-
expect(ph[""]["foo"]).to eql 42
|
108
|
-
end
|
109
|
-
|
110
|
-
it "can recursively write entries via a path" do
|
111
|
-
ph = ::Unobtainium::PathedHash.new
|
112
|
-
ph["foo.bar"] = 42
|
113
|
-
expect(ph["foo.bar"]).to eql 42
|
114
|
-
end
|
115
|
-
|
116
|
-
it "has the same string representation as the hash it's initialized from" do
|
117
|
-
h = { foo: 42 }
|
118
|
-
ph = ::Unobtainium::PathedHash.new(h)
|
119
|
-
expect(ph.to_s).to eql h.to_s
|
120
|
-
end
|
121
|
-
|
122
|
-
it "understands absolute paths (starting with separator)" do
|
123
|
-
sample = {
|
124
|
-
"foo" => 42,
|
125
|
-
"bar" => {
|
126
|
-
"baz" => "quux",
|
127
|
-
"blah" => [1, 2],
|
128
|
-
}
|
129
|
-
}
|
130
|
-
ph = ::Unobtainium::PathedHash.new(sample)
|
131
|
-
|
132
|
-
expect(ph["bar.baz"]).to eql "quux"
|
133
|
-
expect(ph[".bar.baz"]).to eql "quux"
|
134
|
-
end
|
135
|
-
|
136
|
-
it "recursively merges with overwriting" do
|
137
|
-
sample1 = {
|
138
|
-
"foo" => {
|
139
|
-
"bar" => 42,
|
140
|
-
"baz" => "quux",
|
141
|
-
}
|
142
|
-
}
|
143
|
-
sample2 = {
|
144
|
-
"foo" => {
|
145
|
-
"baz" => "override"
|
146
|
-
}
|
147
|
-
}
|
148
|
-
|
149
|
-
ph1 = ::Unobtainium::PathedHash.new(sample1)
|
150
|
-
ph2 = ph1.recursive_merge(sample2)
|
151
|
-
|
152
|
-
expect(ph2["foo.bar"]).to eql 42
|
153
|
-
expect(ph2["foo.baz"]).to eql "override"
|
154
|
-
end
|
155
|
-
|
156
|
-
it "recursively merges without overwriting" do
|
157
|
-
sample1 = {
|
158
|
-
"foo" => {
|
159
|
-
"bar" => 42,
|
160
|
-
"baz" => "quux",
|
161
|
-
}
|
162
|
-
}
|
163
|
-
sample2 = {
|
164
|
-
"foo" => {
|
165
|
-
"baz" => "override"
|
166
|
-
}
|
167
|
-
}
|
168
|
-
|
169
|
-
ph1 = ::Unobtainium::PathedHash.new(sample1)
|
170
|
-
ph2 = ph1.recursive_merge(sample2, false)
|
171
|
-
|
172
|
-
expect(ph2["foo.bar"]).to eql 42
|
173
|
-
expect(ph2["foo.baz"]).to eql "quux"
|
174
|
-
end
|
175
|
-
|
176
|
-
it "can write with indifferent access without overwriting" do
|
177
|
-
sample = {
|
178
|
-
foo: {
|
179
|
-
bar: 42,
|
180
|
-
baz: 'quux',
|
181
|
-
}
|
182
|
-
}
|
183
|
-
ph = ::Unobtainium::PathedHash.new(sample)
|
184
|
-
|
185
|
-
expect(ph['foo.bar']).to eql 42
|
186
|
-
expect(ph['foo.baz']).to eql 'quux'
|
187
|
-
|
188
|
-
ph['foo.bar'] = 123
|
189
|
-
expect(ph['foo.bar']).to eql 123
|
190
|
-
expect(ph['foo.baz']).to eql 'quux'
|
191
|
-
end
|
192
|
-
end
|