lab42_nhash 0.1.0.pre → 0.1.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/Gemfile.lock +1 -1
- data/demo/080-interpolation.md +42 -1
- data/demo/090-compound-values.md +50 -0
- data/demo/100-hierarchies.md +128 -0
- data/lib/lab42/nhash/class_methods.rb +19 -0
- data/lib/lab42/nhash/enum.rb +37 -0
- data/lib/lab42/nhash/hierarchies.rb +30 -0
- data/lib/lab42/nhash/interpolation.rb +28 -3
- data/lib/lab42/nhash/version.rb +1 -1
- data/lib/lab42/nhash.rb +39 -14
- data/spec/integration/hierarchie_and_lookup_spec.rb +47 -0
- data/spec/regression_spec.rb +18 -0
- data/spec/spec_helper.rb +2 -1
- data/spec/unit/compound/enum_spec.rb +41 -0
- data/spec/unit/compound/nhash_spec.rb +18 -0
- data/spec/unit/hierarchies/base_spec.rb +34 -0
- data/spec/unit/hierarchies/tree_form_spec.rb +73 -0
- data/spec/unit/interpolation/binding_spec.rb +70 -0
- data/spec/unit/interpolation/enum_binding_spec.rb +27 -0
- data/spec/unit/parentship_spec.rb +30 -0
- metadata +27 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 083d1e07c143e83a9743bc203ab0386c0ca3d13d
|
|
4
|
+
data.tar.gz: 0c1017bc1a32a9ed99c26d2e83047e76b3a1dd11
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 01108baa232347e7688bdc2c163f3e7c1e9870bd3156f5e8ead4497b594ba5803b1995c35d2e64847619c9a73578f5d6f414ca1ff478216cd85670077b6ae96c
|
|
7
|
+
data.tar.gz: acb2707844115bc986d0682a4aad72a181c8a2ffd001d0e84bf0797f9bbb6956c4def3bd8cd209b14c750adc616947f2deeb940f685beeaf66a2192f5a8f09af
|
data/Gemfile.lock
CHANGED
data/demo/080-interpolation.md
CHANGED
|
@@ -29,9 +29,50 @@ However `get!/fetch!` are made from a completely different kind.
|
|
|
29
29
|
|
|
30
30
|
```ruby
|
|
31
31
|
nh.get!(:sum).assert == '42'
|
|
32
|
-
nh.
|
|
32
|
+
nh.get!(:result).assert == 'the sum is 42'
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
+
### Interpolation Context
|
|
35
36
|
|
|
37
|
+
As could be seen in the example above, the context in which the `<% ... %>` body is evaluated is that
|
|
38
|
+
of the `NHash` instance itself. However there is a possibility to provide any binding as a parameter
|
|
36
39
|
|
|
40
|
+
```ruby
|
|
41
|
+
def get *args; 1 end
|
|
42
|
+
nh.push_binding binding
|
|
43
|
+
nh.get!(:sum).assert == '2'
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Again this implements the _with_ pattern:
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
nh.pop_binding
|
|
50
|
+
nh.get!(:sum).assert == '42'
|
|
51
|
+
nh.with_binding binding do
|
|
52
|
+
get!(:sum).assert == '2'
|
|
53
|
+
end
|
|
54
|
+
nh.get!(:sum).assert == '42'
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
But an even more concise form is availaible, a single method called `get_with_binding`
|
|
58
|
+
|
|
59
|
+
```ruby
|
|
60
|
+
nh.get_with_binding(:sum, binding).assert = '2'
|
|
61
|
+
nh.get!(:sum).assert == '42'
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Block Form For Current Binding
|
|
65
|
+
|
|
66
|
+
In order to replace the frequent use case:
|
|
67
|
+
|
|
68
|
+
```ruby
|
|
69
|
+
nh.get_with_binding :sum, binding
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
with a more concise form a method acceting a block parameter, from which it can
|
|
73
|
+
get the current binding can be used as follows:
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
nh.get_with_current{ :sum }.assert == '2'
|
|
77
|
+
```
|
|
37
78
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
|
|
2
|
+
# Lab42::NHash QED
|
|
3
|
+
|
|
4
|
+
## Compound Values
|
|
5
|
+
|
|
6
|
+
All Scalar values will be left untouched, however, as shown earlier, subhashes are transformed into
|
|
7
|
+
`NHash` instances and instances of `Enumerable` will get an `NHash::Enum` wrapper, that allows to
|
|
8
|
+
maintain necessary context information.
|
|
9
|
+
|
|
10
|
+
This demonstration will, well, demonstrate how that works and what we get from this additional layer
|
|
11
|
+
of complexity:
|
|
12
|
+
|
|
13
|
+
### A case of enumertion
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
```ruby
|
|
17
|
+
nh = NHash.new(
|
|
18
|
+
a: 42,
|
|
19
|
+
entries: [
|
|
20
|
+
{ value: '<%= get :a %>' },
|
|
21
|
+
'<%= get( :a ) * 2 %>',
|
|
22
|
+
21
|
|
23
|
+
])
|
|
24
|
+
.with_indifferent_access
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
First thing to show is that indeed we get the `NHash::Enum` instance
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
entries = nh.get :entries
|
|
32
|
+
entries.assert.instance_of?( NHash::Enum )
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
And that we can use the result within the context of the original
|
|
36
|
+
`NHash` instance, e.g. with indifferent access in our case:
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
entries[0].get('value').assert == '<%= get :a %>'
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
And that we can also use interpolation:
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
# entries[0].get!(:value).assert == '42'
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
|
|
2
|
+
# Lab42::NHash QED
|
|
3
|
+
|
|
4
|
+
## Hierarchies
|
|
5
|
+
|
|
6
|
+
Hierarchies are chains of `NHash` instances that are *looked_up* if a `get` fails.
|
|
7
|
+
|
|
8
|
+
As each instance can have mant hierarchies they hierarchy lookup consists of a Depth First Tree Search.
|
|
9
|
+
|
|
10
|
+
Here is an example:
|
|
11
|
+
|
|
12
|
+
```ruby
|
|
13
|
+
root = NHash.new( a: 1 ).with_indifferent_access
|
|
14
|
+
one = NHash.new( b: 2 ).with_indifferent_access
|
|
15
|
+
two = NHash.new( b: :never_found )
|
|
16
|
+
.with_indifferent_access
|
|
17
|
+
.add_hierarchy( 'c' => 3 )
|
|
18
|
+
three = NHash.new( c: :never_found )
|
|
19
|
+
|
|
20
|
+
root.add_hierarchies one, two, three
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
This constructed a tree like the following (denoting `:never_found` with a `*`)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
# +------+
|
|
28
|
+
# | a: 1 |
|
|
29
|
+
# +------+
|
|
30
|
+
# /|\
|
|
31
|
+
# / | \
|
|
32
|
+
# +--------------. | .--------------+
|
|
33
|
+
# | | |
|
|
34
|
+
# +---------+ +---------+ +---------+
|
|
35
|
+
# | b: 2 + + b: * + + c: * +
|
|
36
|
+
# +---------+ +---------+ +---------+
|
|
37
|
+
# |
|
|
38
|
+
# |
|
|
39
|
+
# +---------+
|
|
40
|
+
# + c: 3 +
|
|
41
|
+
# +---------+
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Therefore the following assertions hold:
|
|
45
|
+
|
|
46
|
+
```ruby
|
|
47
|
+
root.get( :a ).assert == 1
|
|
48
|
+
root.get( :b ).assert == 2
|
|
49
|
+
root.get( :c ).assert == 3
|
|
50
|
+
KeyError.assert.raised? do
|
|
51
|
+
root.get :d
|
|
52
|
+
end
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
### Hierarchies and Lookup Chains
|
|
57
|
+
|
|
58
|
+
At first sight these might be easily confused with _Lookup Chains_. However the difference is easy to explain:
|
|
59
|
+
|
|
60
|
+
While a _lookup_ (or a _fallback_ for that matter) *always* operates on the same NHash hierarchie, meaning
|
|
61
|
+
that we will never find anything outside our underling hash, or that of our parent in case of intepolation,
|
|
62
|
+
the hierarchies define a lookup chain at an outer level.
|
|
63
|
+
|
|
64
|
+
They allow us to define different datasets to be searched for the same key combinations. And they operate only
|
|
65
|
+
if a key combination was not found.
|
|
66
|
+
|
|
67
|
+
The concept is very easy to understand when demonstrated on an example, so what are we waiting for?
|
|
68
|
+
|
|
69
|
+
```ruby
|
|
70
|
+
nh1 = NHash.new(
|
|
71
|
+
en: { '1' => :one, '2' => :two },
|
|
72
|
+
fr: { '2' => :deux })
|
|
73
|
+
.with_indifferent_access
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Let us at first demonstrate the lookup chain for a reminder.
|
|
78
|
+
|
|
79
|
+
```ruby
|
|
80
|
+
nh1.push_prefix_lookup :fr, :en
|
|
81
|
+
nh1.with_prefix :de do
|
|
82
|
+
nh1.get( '.1' ).assert == :one
|
|
83
|
+
end
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
And now we add another `NHash` instance as a hierarchy:
|
|
87
|
+
|
|
88
|
+
```ruby
|
|
89
|
+
nh1.add_hierarchy NHash.new(
|
|
90
|
+
de: { '0' => :null },
|
|
91
|
+
fr: { '1' => :un })
|
|
92
|
+
.with_indifferent_access
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
First of all let us demonstrate that the lookup chain takes precedence over the hierarchy search
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
nh1.with_prefix :fr do
|
|
99
|
+
get( '.1' ).assert == :one
|
|
100
|
+
end
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
However, this does not hold if we do not use prefixes and the hierarchy lookup will kick in instead:
|
|
104
|
+
|
|
105
|
+
```ruby
|
|
106
|
+
nh1.get( 'fr.1' ).assert == :un
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
The same holds if prefix lookup fails, as in these cases:
|
|
111
|
+
|
|
112
|
+
```ruby
|
|
113
|
+
nh1.get( 'de.0' ).assert == :null
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
If, however a hiearchy is used for a lookup, it does not inherite the other lookup strategies
|
|
118
|
+
from its root, e.g. affix chains or fallbacks.
|
|
119
|
+
|
|
120
|
+
```ruby
|
|
121
|
+
KeyError.assert.raised? do
|
|
122
|
+
nh1.with_prefix :de do
|
|
123
|
+
get( '.0' ).assert == :null
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Interpolation Context
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require_relative 'enum'
|
|
2
|
+
module Lab42
|
|
3
|
+
class NHash
|
|
4
|
+
module ClassMethods
|
|
5
|
+
|
|
6
|
+
def from_value value, options={}
|
|
7
|
+
case value
|
|
8
|
+
when Hash
|
|
9
|
+
Lab42::NHash.new( value ).import_options options
|
|
10
|
+
when Enumerable
|
|
11
|
+
Lab42::NHash::Enum.new value, options
|
|
12
|
+
else
|
|
13
|
+
value
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end # module ClassMethods
|
|
17
|
+
extend ClassMethods
|
|
18
|
+
end # class NHash
|
|
19
|
+
end # module Lab42
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'forwarder'
|
|
2
|
+
|
|
3
|
+
module Lab42
|
|
4
|
+
class NHash
|
|
5
|
+
class Enum
|
|
6
|
+
extend Forwarder
|
|
7
|
+
forward :each, to: :@enum
|
|
8
|
+
forward :first, to_object: :self, as: :[], with: 0
|
|
9
|
+
forward :first!, to_object: :self, as: :fetch!, with: 0
|
|
10
|
+
forward :last, to_object: :self, as: :[], with: -1
|
|
11
|
+
forward :last!, to_object: :self, as: :fetch!, with: -1
|
|
12
|
+
|
|
13
|
+
include Enumerable
|
|
14
|
+
|
|
15
|
+
attr_reader :parent
|
|
16
|
+
|
|
17
|
+
def [] idx
|
|
18
|
+
Lab42::NHash.from_value @enum[idx], @options
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def fetch! idx
|
|
22
|
+
result = self[ idx ]
|
|
23
|
+
return result unless String === result
|
|
24
|
+
|
|
25
|
+
ERB.new( result ).result @parent.current_binding
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
def initialize enum, options
|
|
30
|
+
@enum = enum
|
|
31
|
+
@parent = options[:parent]
|
|
32
|
+
@options = options
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end # class Enum
|
|
36
|
+
end # class NHash
|
|
37
|
+
end # module Lab42
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Lab42
|
|
2
|
+
class NHash
|
|
3
|
+
module Hierarchy
|
|
4
|
+
def add_hierarchy a_hierarchy={}
|
|
5
|
+
a_hierarchy = self.class.new a_hierarchy if Hash === a_hierarchy
|
|
6
|
+
raise ArgumentError, 'not an NHash instance' unless self.class === a_hierarchy
|
|
7
|
+
@hierarchies << a_hierarchy
|
|
8
|
+
self
|
|
9
|
+
end
|
|
10
|
+
def add_hierarchies *some_hierarchies
|
|
11
|
+
some_hierarchies.each do | a_hierarchy |
|
|
12
|
+
add_hierarchy a_hierarchy
|
|
13
|
+
end
|
|
14
|
+
self
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def get_form_hierarchies keyexpr, keyexc
|
|
18
|
+
@hierarchies.each do | h |
|
|
19
|
+
begin
|
|
20
|
+
return h.get keyexpr
|
|
21
|
+
rescue KeyError
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
raise KeyError, keyexc
|
|
25
|
+
end
|
|
26
|
+
end # module Hierarchy
|
|
27
|
+
|
|
28
|
+
include Hierarchy
|
|
29
|
+
end # class NHash
|
|
30
|
+
end # module Lab42
|
|
@@ -5,14 +5,39 @@ module Lab42
|
|
|
5
5
|
module Interpolation
|
|
6
6
|
def get! *args, &blk
|
|
7
7
|
result = get( *args, &blk )
|
|
8
|
-
return result unless String === result
|
|
9
8
|
_expand_result result
|
|
10
9
|
end
|
|
11
10
|
alias_method :fetch!, :get!
|
|
12
11
|
|
|
12
|
+
def get_with_binding key, a_binding, *rst, &blk
|
|
13
|
+
with_binding a_binding do
|
|
14
|
+
get!( key, *rst, &blk )
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def get_with_current &blk
|
|
19
|
+
with_binding blk.binding do
|
|
20
|
+
get! blk.()
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def with_binding a_binding, &blk
|
|
25
|
+
push_binding a_binding
|
|
26
|
+
_invoke blk, self
|
|
27
|
+
ensure
|
|
28
|
+
pop_binding
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def current_binding
|
|
32
|
+
@binding_stack.last || ( root? ? binding : parent.current_binding )
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def root?
|
|
36
|
+
parent == self || parent.nil?
|
|
37
|
+
end
|
|
13
38
|
private
|
|
14
|
-
def _expand_result
|
|
15
|
-
ERB.new(
|
|
39
|
+
def _expand_result value
|
|
40
|
+
ERB.new( value ).result( current_binding )
|
|
16
41
|
end
|
|
17
42
|
|
|
18
43
|
end # module Interpolation
|
data/lib/lab42/nhash/version.rb
CHANGED
data/lib/lab42/nhash.rb
CHANGED
|
@@ -1,38 +1,60 @@
|
|
|
1
|
+
require 'forwarder'
|
|
2
|
+
|
|
3
|
+
require_relative './nhash/class_methods'
|
|
4
|
+
|
|
1
5
|
require_relative './nhash/exceptions'
|
|
2
6
|
require_relative './nhash/invocation'
|
|
3
7
|
require_relative './nhash/affixes'
|
|
4
8
|
require_relative './nhash/fallbacks'
|
|
5
9
|
require_relative './nhash/lookup_chains'
|
|
6
10
|
require_relative './nhash/interpolation'
|
|
11
|
+
require_relative './nhash/enum'
|
|
12
|
+
require_relative './nhash/hierarchies'
|
|
7
13
|
|
|
8
14
|
module Lab42
|
|
9
15
|
class NHash
|
|
10
16
|
|
|
11
|
-
|
|
17
|
+
extend Forwarder
|
|
18
|
+
forward :pop_binding, to: :@binding_stack, as: :pop
|
|
19
|
+
forward :push_binding, to: :@binding_stack, as: :push
|
|
20
|
+
|
|
21
|
+
attr_reader :hashy, :parent
|
|
22
|
+
|
|
23
|
+
def export_options
|
|
24
|
+
{ indifferent_access: @indifferent_access,
|
|
25
|
+
binding_stack: @binding_stack.dup,
|
|
26
|
+
parent: @parent
|
|
27
|
+
}
|
|
28
|
+
end
|
|
12
29
|
|
|
13
30
|
def get keyexpr, *default, &defblock
|
|
14
31
|
keys = keyexpr.to_s.split( '.' )
|
|
15
32
|
keys = complete_keys keys.reject(&:empty?), use_prefix: keys.first.empty?, use_suffix: keyexpr[-1] == '.'
|
|
16
33
|
found = @indifferent_access ? _get_indiff( keys ) : _get( keys )
|
|
17
|
-
|
|
18
|
-
when Hash
|
|
19
|
-
self.class.new( found ).import_options export_options
|
|
20
|
-
else
|
|
21
|
-
found
|
|
22
|
-
end
|
|
34
|
+
self.class.from_value found, export_options
|
|
23
35
|
rescue KeyError => k
|
|
24
|
-
return
|
|
36
|
+
return fallback_or_hierarchy keyexpr, k if default.empty? && defblock.nil?
|
|
25
37
|
default.empty? ? defblock.(keyexpr) : default.first
|
|
26
38
|
end
|
|
27
39
|
alias_method :fetch, :get
|
|
28
40
|
|
|
41
|
+
def fallback_or_hierarchy keyexpr, keyexc
|
|
42
|
+
fallback keyexpr, keyexc
|
|
43
|
+
rescue KeyError => k
|
|
44
|
+
get_form_hierarchies keyexpr, k
|
|
45
|
+
end
|
|
46
|
+
|
|
29
47
|
def import_options options
|
|
30
48
|
@indifferent_access = options[:indifferent_access]
|
|
49
|
+
@binding_stack = options[:binding_stack] || []
|
|
50
|
+
@parent = options[:parent]
|
|
31
51
|
self
|
|
32
52
|
end
|
|
33
53
|
|
|
34
54
|
def with_indifferent_access
|
|
35
|
-
self.class.new( @hashy ).
|
|
55
|
+
self.class.new( @hashy ).tap do |rv|
|
|
56
|
+
rv.instance_variable_set :@indifferent_access, true
|
|
57
|
+
end
|
|
36
58
|
end
|
|
37
59
|
|
|
38
60
|
private
|
|
@@ -56,21 +78,24 @@ module Lab42
|
|
|
56
78
|
partial_hash.fetch( key_element ){ partial_hash.fetch key_element.to_sym }
|
|
57
79
|
end
|
|
58
80
|
end
|
|
59
|
-
def export_options
|
|
60
|
-
{ indifferent_access: @indifferent_access }
|
|
61
|
-
end
|
|
62
81
|
|
|
63
|
-
def initialize hashy
|
|
64
|
-
@hashy
|
|
82
|
+
def initialize hashy={}
|
|
83
|
+
@hashy = hashy
|
|
65
84
|
init_options
|
|
66
85
|
end
|
|
67
86
|
|
|
68
87
|
def init_options
|
|
88
|
+
@parent = self
|
|
89
|
+
|
|
69
90
|
@indifferent_access = false
|
|
70
91
|
@suffix_stack = []
|
|
71
92
|
@prefix_stack = []
|
|
93
|
+
@binding_stack = []
|
|
94
|
+
|
|
72
95
|
@fallbacks = []
|
|
73
96
|
@current_fallback_pointer = 0
|
|
97
|
+
|
|
98
|
+
@hierarchies = []
|
|
74
99
|
end
|
|
75
100
|
end # class NHash
|
|
76
101
|
end # module Lab42
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe NHash do
|
|
4
|
+
context 'Hierarchy and Lookup' do
|
|
5
|
+
|
|
6
|
+
let :level2 do
|
|
7
|
+
described_class.new(
|
|
8
|
+
en: { '2' => :two },
|
|
9
|
+
fr: { '1' => :un, '3' => :trois })
|
|
10
|
+
.with_indifferent_access
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
subject do
|
|
14
|
+
described_class.new(
|
|
15
|
+
en: { '1' => :one }
|
|
16
|
+
)
|
|
17
|
+
.with_indifferent_access
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
before do
|
|
21
|
+
subject.push_prefix_lookup :en
|
|
22
|
+
level2.push_prefix_lookup :fr, :en
|
|
23
|
+
subject.add_hierarchy level2
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'finds with prefix lookup in the root' do
|
|
27
|
+
subject.with_prefix :fr do | s |
|
|
28
|
+
expect( s.get '.1' ).to eq :one
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
it 'finds in the hierarchy' do
|
|
32
|
+
expect( subject.get 'fr.3' ).to eq :trois
|
|
33
|
+
end
|
|
34
|
+
it 'finds it in the hierarchy if local lookup fails' do
|
|
35
|
+
subject.with_prefix :fr do | s |
|
|
36
|
+
expect( s.get '.3' ).to eq :trois
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
it 'finds in the hierarchy with lookup' do
|
|
41
|
+
subject.with_prefix :fr do | s |
|
|
42
|
+
expect( s.get '.2' ).to eq :two
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
end # context 'Hierarchy and Lookup'
|
|
47
|
+
end # describe NHash
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe NHash, :regression do
|
|
4
|
+
subject do
|
|
5
|
+
NHash.new(
|
|
6
|
+
sum: '<%= get("a")+get("b") %>',
|
|
7
|
+
a: 41,
|
|
8
|
+
b: 1,
|
|
9
|
+
result: 'the sum is <%= get! :sum %>'
|
|
10
|
+
)
|
|
11
|
+
.with_indifferent_access
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'works (debugging)' do
|
|
15
|
+
expect( subject.fetch! :result ).to eq 'the sum is 42'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end # describe NHash
|
data/spec/spec_helper.rb
CHANGED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe NHash::Enum do
|
|
4
|
+
subject do
|
|
5
|
+
nh = NHash.new(
|
|
6
|
+
a: 42,
|
|
7
|
+
entries: [
|
|
8
|
+
{ val: '<%= get :a %>' },
|
|
9
|
+
'<%= get(:a) * 2 %>',
|
|
10
|
+
21
|
|
11
|
+
] )
|
|
12
|
+
.with_indifferent_access
|
|
13
|
+
end
|
|
14
|
+
let(:enum){ subject.get :entries }
|
|
15
|
+
|
|
16
|
+
it 'is returned for arrays' do
|
|
17
|
+
expect( subject.get :entries ).to be_instance_of described_class
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
context 'behaves like an array' do
|
|
21
|
+
it 'for #[]' do
|
|
22
|
+
expect( enum[2] ).to eq 21
|
|
23
|
+
end
|
|
24
|
+
it 'for #first' do
|
|
25
|
+
expect( enum.first.get :val ).to eq '<%= get :a %>'
|
|
26
|
+
end
|
|
27
|
+
it 'for #last' do
|
|
28
|
+
expect( enum.last ).to eq 21
|
|
29
|
+
end
|
|
30
|
+
end # context :behaves
|
|
31
|
+
|
|
32
|
+
context 'preservers the context' do
|
|
33
|
+
it 'evaluates with indifferent access' do
|
|
34
|
+
expect( enum.first.get('val') ).to eq '<%= get :a %>'
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'interpolates in the context of the original object' do
|
|
38
|
+
expect( enum.first.get!('val') ).to eq '42'
|
|
39
|
+
end
|
|
40
|
+
end # context 'preservers the context'
|
|
41
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe NHash do
|
|
4
|
+
context :compound do
|
|
5
|
+
subject do
|
|
6
|
+
NHash.new(
|
|
7
|
+
a: 42,
|
|
8
|
+
b: { a: 41 })
|
|
9
|
+
.with_indifferent_access
|
|
10
|
+
end
|
|
11
|
+
it 'gets scalars' do
|
|
12
|
+
expect( subject.get :a ).to eq 42
|
|
13
|
+
end
|
|
14
|
+
it '... or not' do
|
|
15
|
+
expect( subject.get :b ).to be_instance_of described_class
|
|
16
|
+
end
|
|
17
|
+
end # context :compound
|
|
18
|
+
end # describe NHash
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe NHash do
|
|
4
|
+
context :hierarchies do
|
|
5
|
+
subject do
|
|
6
|
+
described_class.new( a: 1 )
|
|
7
|
+
.with_indifferent_access
|
|
8
|
+
.add_hierarchy(
|
|
9
|
+
described_class.new( b: 2 )
|
|
10
|
+
.with_indifferent_access
|
|
11
|
+
)
|
|
12
|
+
end
|
|
13
|
+
it 'finds key in base instance' do
|
|
14
|
+
expect( subject.get :a ).to eq 1
|
|
15
|
+
end
|
|
16
|
+
it 'finds key in first hierarchy' do
|
|
17
|
+
expect( subject.get :b ).to eq 2
|
|
18
|
+
end
|
|
19
|
+
it 'still raises KeyError or returns default values if key is not in any hierarchy' do
|
|
20
|
+
expect( ->{
|
|
21
|
+
subject.get :c
|
|
22
|
+
} ).to raise_error KeyError
|
|
23
|
+
expect( subject.get :c, 42 ).to eq 42
|
|
24
|
+
expect( subject.get( :c ){ 42 } ).to eq 42
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
context 'implicit' do
|
|
28
|
+
it 'convertion of hashes in add_hierarchy' do
|
|
29
|
+
n = NHash.new().add_hierarchy
|
|
30
|
+
expect( n.instance_variable_get( :@hierarchies ).first ).to be_instance_of described_class
|
|
31
|
+
end
|
|
32
|
+
end # context 'implicit creation'
|
|
33
|
+
end # context :hierarchies
|
|
34
|
+
end # describe NHash
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe NHash do
|
|
4
|
+
describe :hierarchies do
|
|
5
|
+
context 'tree form' do
|
|
6
|
+
|
|
7
|
+
let(:ints){
|
|
8
|
+
described_class.new( twelve: 'i12' )
|
|
9
|
+
.with_indifferent_access
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let(:odds){
|
|
13
|
+
described_class.new(
|
|
14
|
+
one: 'o1', three: 'o3')
|
|
15
|
+
.with_indifferent_access
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let(:evens){
|
|
19
|
+
described_class.new( four: 'e4' )
|
|
20
|
+
.with_indifferent_access
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let(:fizzes){
|
|
24
|
+
described_class.new( three: 'f3' )
|
|
25
|
+
.with_indifferent_access
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
before do
|
|
29
|
+
odds.add_hierarchy ints
|
|
30
|
+
fizzes.add_hierarchy evens
|
|
31
|
+
subject.add_hierarchies fizzes, odds
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
subject do
|
|
35
|
+
described_class
|
|
36
|
+
.new( zero: '0' )
|
|
37
|
+
.with_indifferent_access
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
context 'no impact at root level' do
|
|
41
|
+
it 'for root' do
|
|
42
|
+
expect( subject.get(:zero) ).to eq '0'
|
|
43
|
+
end
|
|
44
|
+
it 'for hierarchies accessed as root' do
|
|
45
|
+
expect( odds.get :three ).to eq 'o3'
|
|
46
|
+
end
|
|
47
|
+
end # context 'no impact at root level'
|
|
48
|
+
|
|
49
|
+
context 'hierarchy chains' do
|
|
50
|
+
it 'finds them at the next level' do
|
|
51
|
+
expect( subject.get( :one ) ).to eq( 'o1' )
|
|
52
|
+
end
|
|
53
|
+
it 'according to the the FIFO principle' do
|
|
54
|
+
expect( subject.get( :three ) ).to eq( 'f3' )
|
|
55
|
+
end
|
|
56
|
+
it 'or on later levels' do
|
|
57
|
+
expect( subject.get( :four ) ).to eq( 'e4' )
|
|
58
|
+
expect( subject.get( :twelve ) ).to eq( 'i12' )
|
|
59
|
+
end
|
|
60
|
+
end # context 'hierarchy chains'
|
|
61
|
+
|
|
62
|
+
context 'eventually we still might' do
|
|
63
|
+
it 'convert to default values' do
|
|
64
|
+
expect( subject.get( :hundred, 100 ) ).to eq 100
|
|
65
|
+
expect( subject.get( :hundred ){ 100 } ).to eq 100
|
|
66
|
+
end
|
|
67
|
+
it 'or even raise a KeyError' do
|
|
68
|
+
expect( ->{ subject.get :hundred } ).to raise_error KeyError
|
|
69
|
+
end
|
|
70
|
+
end # context 'eventually we still might'
|
|
71
|
+
end # context 'tree form'
|
|
72
|
+
end # describe :hierarchies
|
|
73
|
+
end # describe NHash
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe NHash do
|
|
4
|
+
|
|
5
|
+
context :Interpolation do
|
|
6
|
+
context :binding do
|
|
7
|
+
|
|
8
|
+
# get for local binding
|
|
9
|
+
def get *args; 1 end
|
|
10
|
+
|
|
11
|
+
let(:b){
|
|
12
|
+
Class.new do
|
|
13
|
+
def get *args; 2 end
|
|
14
|
+
def b; binding end
|
|
15
|
+
end.new
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
subject do
|
|
19
|
+
NHash.new(
|
|
20
|
+
a: 1,
|
|
21
|
+
b: 42,
|
|
22
|
+
sum: %{<%= get(:a) + get(:b) %>} )
|
|
23
|
+
.with_indifferent_access
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "evaluates in the instance's binding" do
|
|
27
|
+
expect( subject.get! :sum ).to eq "43"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'can be evaluated in the current binding' do
|
|
31
|
+
subject.push_binding binding
|
|
32
|
+
expect( subject.get! :sum ).to eq '2'
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'can be evaluated in any binding' do
|
|
36
|
+
subject.push_binding b.b
|
|
37
|
+
expect( subject.get! :sum ).to eq '4'
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
context :with_binding do
|
|
41
|
+
it 'is temporary' do
|
|
42
|
+
outside = subject.get! :sum
|
|
43
|
+
inside = subject.with_binding binding do
|
|
44
|
+
get! :sum
|
|
45
|
+
end
|
|
46
|
+
outside += subject.get! :sum
|
|
47
|
+
|
|
48
|
+
expect( outside + inside ).to eq '43432'
|
|
49
|
+
end
|
|
50
|
+
end # context :with_binding
|
|
51
|
+
|
|
52
|
+
context :get_with_binding do
|
|
53
|
+
it 'is also temporary' do
|
|
54
|
+
outside = subject.get! :sum
|
|
55
|
+
inside = subject.get_with_binding :sum, binding
|
|
56
|
+
outside += subject.get! :sum
|
|
57
|
+
expect( outside + inside ).to eq '43432'
|
|
58
|
+
end
|
|
59
|
+
end # context :get_with_binding
|
|
60
|
+
|
|
61
|
+
context :get_with_current do
|
|
62
|
+
it 'evaluates with the current binding' do
|
|
63
|
+
expect( subject.get_with_current{ :sum } ).to eq '2'
|
|
64
|
+
end
|
|
65
|
+
end # context :get_with_current
|
|
66
|
+
|
|
67
|
+
end # context :binding
|
|
68
|
+
end # context :Interpolation
|
|
69
|
+
|
|
70
|
+
end # describe NHash
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe NHash do
|
|
4
|
+
context :Interpolation do
|
|
5
|
+
context :enum_binding do
|
|
6
|
+
subject do
|
|
7
|
+
NHash.new(
|
|
8
|
+
a: 31,
|
|
9
|
+
entries:
|
|
10
|
+
[ 'a: <%= get(:a) %>',
|
|
11
|
+
{ b: '<%= get(:a) + 11 %>' }])
|
|
12
|
+
.with_indifferent_access
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'retains the binding from the parent in subhashes' do
|
|
16
|
+
expect( subject.get( :entries ).last!.get!(:b) ).to eq "42"
|
|
17
|
+
expect( subject.get( :entries ).fetch!(1).get!(:b) ).to eq "42"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'even in flat enum entries' do
|
|
21
|
+
expect( subject.get( :entries ).first! ).to eq 'a: 31'
|
|
22
|
+
expect( subject.get( :entries ).fetch!(0) ).to eq 'a: 31'
|
|
23
|
+
end
|
|
24
|
+
end # context :enum_binding
|
|
25
|
+
end # context :Interpolation
|
|
26
|
+
end # describe NHash
|
|
27
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe NHash do
|
|
4
|
+
context :parentship do
|
|
5
|
+
subject do
|
|
6
|
+
NHash.new(
|
|
7
|
+
a: 42,
|
|
8
|
+
b: { c: 43 },
|
|
9
|
+
d: [ 1, {e: 44} ]
|
|
10
|
+
)
|
|
11
|
+
.with_indifferent_access
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'new instance is its own parent' do
|
|
15
|
+
expect( subject.parent ).to eq subject
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'a subhashes parent is the whole nhash instance' do
|
|
19
|
+
expect( subject.get( :b ).parent ).to eq subject
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "an enum's parent too" do
|
|
23
|
+
expect( subject.get( :d ).parent ).to eq subject
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "and an enum's subhash also" do
|
|
27
|
+
expect( subject.get( :d )[1].parent ).to eq subject
|
|
28
|
+
end
|
|
29
|
+
end # context :parentship
|
|
30
|
+
end # describe NHash
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lab42_nhash
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.0
|
|
4
|
+
version: 0.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Robert Dober
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2014-05-
|
|
11
|
+
date: 2014-05-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: forwarder2
|
|
@@ -141,23 +141,37 @@ files:
|
|
|
141
141
|
- demo/030-lookup-chains.md
|
|
142
142
|
- demo/060-fallback.md
|
|
143
143
|
- demo/080-interpolation.md
|
|
144
|
+
- demo/090-compound-values.md
|
|
145
|
+
- demo/100-hierarchies.md
|
|
144
146
|
- demo/applique/require_ae.rb
|
|
145
147
|
- demo/applique/require_nhash.rb
|
|
146
148
|
- lab42_nhash.gemspec
|
|
147
149
|
- lib/lab42/nhash.rb
|
|
148
150
|
- lib/lab42/nhash/affixes.rb
|
|
149
151
|
- lib/lab42/nhash/auto_import.rb
|
|
152
|
+
- lib/lab42/nhash/class_methods.rb
|
|
153
|
+
- lib/lab42/nhash/enum.rb
|
|
150
154
|
- lib/lab42/nhash/exceptions.rb
|
|
151
155
|
- lib/lab42/nhash/fallbacks.rb
|
|
156
|
+
- lib/lab42/nhash/hierarchies.rb
|
|
152
157
|
- lib/lab42/nhash/interpolation.rb
|
|
153
158
|
- lib/lab42/nhash/invocation.rb
|
|
154
159
|
- lib/lab42/nhash/lookup_chains.rb
|
|
155
160
|
- lib/lab42/nhash/version.rb
|
|
161
|
+
- spec/integration/hierarchie_and_lookup_spec.rb
|
|
162
|
+
- spec/regression_spec.rb
|
|
156
163
|
- spec/spec_helper.rb
|
|
157
164
|
- spec/unit/afixes/affix_spec.rb
|
|
158
165
|
- spec/unit/afixes/prefix_spec.rb
|
|
159
166
|
- spec/unit/afixes/suffix_spec.rb
|
|
167
|
+
- spec/unit/compound/enum_spec.rb
|
|
168
|
+
- spec/unit/compound/nhash_spec.rb
|
|
160
169
|
- spec/unit/fallback/fallback_spec.rb
|
|
170
|
+
- spec/unit/hierarchies/base_spec.rb
|
|
171
|
+
- spec/unit/hierarchies/tree_form_spec.rb
|
|
172
|
+
- spec/unit/interpolation/binding_spec.rb
|
|
173
|
+
- spec/unit/interpolation/enum_binding_spec.rb
|
|
174
|
+
- spec/unit/parentship_spec.rb
|
|
161
175
|
homepage: https://github.com/RobertDober/lab42_nested_hash
|
|
162
176
|
licenses:
|
|
163
177
|
- MIT
|
|
@@ -190,10 +204,21 @@ test_files:
|
|
|
190
204
|
- demo/030-lookup-chains.md
|
|
191
205
|
- demo/060-fallback.md
|
|
192
206
|
- demo/080-interpolation.md
|
|
207
|
+
- demo/090-compound-values.md
|
|
208
|
+
- demo/100-hierarchies.md
|
|
193
209
|
- demo/applique/require_ae.rb
|
|
194
210
|
- demo/applique/require_nhash.rb
|
|
211
|
+
- spec/integration/hierarchie_and_lookup_spec.rb
|
|
212
|
+
- spec/regression_spec.rb
|
|
195
213
|
- spec/spec_helper.rb
|
|
196
214
|
- spec/unit/afixes/affix_spec.rb
|
|
197
215
|
- spec/unit/afixes/prefix_spec.rb
|
|
198
216
|
- spec/unit/afixes/suffix_spec.rb
|
|
217
|
+
- spec/unit/compound/enum_spec.rb
|
|
218
|
+
- spec/unit/compound/nhash_spec.rb
|
|
199
219
|
- spec/unit/fallback/fallback_spec.rb
|
|
220
|
+
- spec/unit/hierarchies/base_spec.rb
|
|
221
|
+
- spec/unit/hierarchies/tree_form_spec.rb
|
|
222
|
+
- spec/unit/interpolation/binding_spec.rb
|
|
223
|
+
- spec/unit/interpolation/enum_binding_spec.rb
|
|
224
|
+
- spec/unit/parentship_spec.rb
|