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 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