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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 02be76a89cdf9ca8717dff56712bff2479224757
4
- data.tar.gz: 419240c05de38a8552b5573ac2152b40714fd76c
3
+ metadata.gz: 083d1e07c143e83a9743bc203ab0386c0ca3d13d
4
+ data.tar.gz: 0c1017bc1a32a9ed99c26d2e83047e76b3a1dd11
5
5
  SHA512:
6
- metadata.gz: b1937b9751c90dd02b23bd092c345d0b2f8844876c16476900487512b9603c5e121b7aed6113070487ac3b948e9cb29c7b8e8053f1f04b213689d5447984ea50
7
- data.tar.gz: 188a2fba1f7034068c8d73b4e6847942b05c46bc2b1c56658631f149ffdb7ceef3c392f914cde35264ced0d78f1c5777abe87b39881a6f5718e82a44279df047
6
+ metadata.gz: 01108baa232347e7688bdc2c163f3e7c1e9870bd3156f5e8ead4497b594ba5803b1995c35d2e64847619c9a73578f5d6f414ca1ff478216cd85670077b6ae96c
7
+ data.tar.gz: acb2707844115bc986d0682a4aad72a181c8a2ffd001d0e84bf0797f9bbb6956c4def3bd8cd209b14c750adc616947f2deeb940f685beeaf66a2192f5a8f09af
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- lab42_nhash (0.1.0.pre)
4
+ lab42_nhash (0.1.0)
5
5
  forwarder2 (~> 0.2)
6
6
  ruby_parser (~> 3.6)
7
7
 
@@ -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.fetch!(:result).assert == 'the sum is 42'
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 result
15
- ERB.new( result ).result( binding )
39
+ def _expand_result value
40
+ ERB.new( value ).result( current_binding )
16
41
  end
17
42
 
18
43
  end # module Interpolation
@@ -1,5 +1,5 @@
1
1
  module Lab42
2
2
  class NHash
3
- VERSION = '0.1.0.pre'
3
+ VERSION = '0.1.0'
4
4
  end # class NHash
5
5
  end # module Lab42
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
- attr_reader :hashy
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
- case found
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 fallback keyexpr, k if default.empty? && defblock.nil?
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 ).import_options indifferent_access: true
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 = 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
@@ -1,4 +1,5 @@
1
- require_relative "../lib/lab42/nhash/auto_import"
1
+ # require_relative "../lib/lab42/nhash/auto_import"
2
+ require 'lab42/nhash/auto_import'
2
3
 
3
4
  PROJECT_ROOT = File.expand_path "../..", __FILE__
4
5
  Dir[File.join(PROJECT_ROOT,"spec/support/**/*.rb")].each {|f| require f}
@@ -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.pre
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-12 00:00:00.000000000 Z
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