lab42_nhash 0.1.0.pre → 0.1.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 +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
|