lab42_nhash 0.1.0.pre

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 02be76a89cdf9ca8717dff56712bff2479224757
4
+ data.tar.gz: 419240c05de38a8552b5573ac2152b40714fd76c
5
+ SHA512:
6
+ metadata.gz: b1937b9751c90dd02b23bd092c345d0b2f8844876c16476900487512b9603c5e121b7aed6113070487ac3b948e9cb29c7b8e8053f1f04b213689d5447984ea50
7
+ data.tar.gz: 188a2fba1f7034068c8d73b4e6847942b05c46bc2b1c56658631f149ffdb7ceef3c392f914cde35264ced0d78f1c5777abe87b39881a6f5718e82a44279df047
data/.gitignore ADDED
@@ -0,0 +1,35 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+ ## Specific to RubyMotion:
13
+ .dat*
14
+ .repl_history
15
+ build/
16
+
17
+ ## Documentation cache and generated files:
18
+ /.yardoc/
19
+ /_yardoc/
20
+ /doc/
21
+ /rdoc/
22
+
23
+ ## Environment normalisation:
24
+ /.bundle/
25
+ /lib/bundler/man/
26
+
27
+ # for a library or gem, you might want to ignore these files since the code is
28
+ # intended to run in multiple environments; otherwise, check them in:
29
+ # Gemfile.lock
30
+ # .ruby-version
31
+ # .ruby-gemset
32
+
33
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
34
+ .rvmrc
35
+ .tmux
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --colour
2
+ --format documentation
3
+ --tag ~js
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ # A sample Gemfile
2
+ source "https://rubygems.org"
3
+
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,61 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ lab42_nhash (0.1.0.pre)
5
+ forwarder2 (~> 0.2)
6
+ ruby_parser (~> 3.6)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ ae (1.8.2)
12
+ ansi
13
+ ansi (1.4.3)
14
+ brass (1.2.1)
15
+ coderay (1.1.0)
16
+ columnize (0.8.9)
17
+ debugger (1.6.6)
18
+ columnize (>= 0.3.1)
19
+ debugger-linecache (~> 1.2.0)
20
+ debugger-ruby_core_source (~> 1.3.2)
21
+ debugger-linecache (1.2.0)
22
+ debugger-ruby_core_source (1.3.4)
23
+ diff-lcs (1.2.5)
24
+ facets (2.9.3)
25
+ forwarder2 (0.2.0)
26
+ method_source (0.8.2)
27
+ pry (0.9.12.6)
28
+ coderay (~> 1.0)
29
+ method_source (~> 0.8)
30
+ slop (~> 3.4)
31
+ pry-debugger (0.2.2)
32
+ debugger (~> 1.3)
33
+ pry (~> 0.9.10)
34
+ qed (2.9.1)
35
+ ansi
36
+ brass
37
+ facets (>= 2.8)
38
+ rspec (2.14.1)
39
+ rspec-core (~> 2.14.0)
40
+ rspec-expectations (~> 2.14.0)
41
+ rspec-mocks (~> 2.14.0)
42
+ rspec-core (2.14.8)
43
+ rspec-expectations (2.14.5)
44
+ diff-lcs (>= 1.1.3, < 2.0)
45
+ rspec-mocks (2.14.6)
46
+ ruby_parser (3.6.0)
47
+ sexp_processor (~> 4.1)
48
+ sexp_processor (4.4.3)
49
+ slop (3.5.0)
50
+
51
+ PLATFORMS
52
+ ruby
53
+
54
+ DEPENDENCIES
55
+ ae (~> 1.8)
56
+ bundler (~> 1.6)
57
+ lab42_nhash!
58
+ pry (~> 0.9)
59
+ pry-debugger (~> 0.2)
60
+ qed (~> 2.9)
61
+ rspec (~> 2.14)
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Robert Dober
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # lab42_nested_hash
2
+
3
+ A nested hash view with dotted deep access à la I18n.t of Rails and with optional string interpolation. Typically YML loaded Hashes are used.
4
+
5
+
6
+ ## Get Access (combined keys with dot notation)
7
+
8
+ Please see the [QED](http://rubyworks.github.io/qed/) demos [here](https://github.com/RobertDober/lab42_nested_hash/blob/master/demo/000-basic-examples.md) for detailed explainations and specifications:
9
+
10
+ ```ruby
11
+ h = { "a" => 1,
12
+ "b" => { "c" => 2, "d" => true } }
13
+
14
+ nh = NHash.new h
15
+
16
+ ```
17
+
18
+ Then `get` lets us access elements
19
+
20
+ ```ruby
21
+ nh.get('a').assert == 1
22
+ nh.get('b.c').assert == 2
23
+ ```
24
+
25
+ or _subhashes_:
26
+
27
+ ```ruby
28
+ nh.get('b').assert.kind_of? NHash
29
+ ```
30
+
31
+
32
+ ### Indifferent Access
33
+
34
+ ### Prefix and Suffix Stacks
35
+
36
+ ## Fallbacks
@@ -0,0 +1,45 @@
1
+
2
+ # Lab42::NHash QED
3
+
4
+ ## Basic Example Dotted Access
5
+
6
+
7
+ ```ruby
8
+ h = { "a" => 1,
9
+ "b" => { "c" => 2, "d" => true } }
10
+
11
+ nh = NHash.new h
12
+
13
+ ```
14
+
15
+ Then `get` lets us access elements
16
+
17
+ ```ruby
18
+ nh.get('a').assert == 1
19
+ nh.get('b.c').assert == 2
20
+ ```
21
+
22
+ or _subhashes_:
23
+
24
+ ```ruby
25
+ nh.get('b').assert.kind_of? NHash
26
+ ```
27
+
28
+ ## Non existing keys
29
+
30
+ `get` raises a KeyError if there is no such key
31
+
32
+ ```ruby
33
+ KeyError.assert.raised? do
34
+ nh.get 'c'
35
+ end
36
+ ```
37
+
38
+ but like `Hash#fetch` a default value or block can remedy at that
39
+
40
+ ```ruby
41
+ nh.get( 'c', :default ).assert == :default
42
+ nh.get( 'c' ){ |key| key + key }.assert == 'cc'
43
+ ```
44
+
45
+
@@ -0,0 +1,48 @@
1
+
2
+ # Lab42::NHash QED
3
+
4
+ ## Indifferent Access
5
+
6
+
7
+ ```ruby
8
+ h = { "a" => 1,
9
+ b: { "c" => 2, d: true } }
10
+
11
+ nh = NHash.new h
12
+
13
+ ```
14
+
15
+ is not available by default
16
+
17
+ ```ruby
18
+ KeyError.assert.raised? do
19
+ nh.get 'b.c'
20
+ end
21
+ ```
22
+
23
+ but it can be enabled
24
+
25
+ ```ruby
26
+ nia = nh.with_indifferent_access
27
+ nia.get( 'b.c' ).assert == 2
28
+ ```
29
+
30
+ without affecting the original object
31
+
32
+ ```ruby
33
+ KeyError.assert.raised? do
34
+ nh.get 'b.c'
35
+ end
36
+ ```
37
+
38
+ and of course KeyErrors are still raised in case
39
+
40
+ ```ruby
41
+ KeyError.assert.raised? do
42
+ nia.get 'a.b'
43
+ end
44
+ KeyError.assert.raised? do
45
+ nh.get 'a.b'
46
+ end
47
+ ```
48
+
@@ -0,0 +1,108 @@
1
+
2
+ # Lab42::NHash QED
3
+
4
+ ## Afixed Access
5
+
6
+
7
+ ### Prefixes
8
+
9
+ YAML documents are typically quite nested and often
10
+ a common prefix (and less often a common suffix) is
11
+ used in a given context.
12
+
13
+ ```ruby
14
+ h = { "a" => {
15
+ "b" =>
16
+ { "c" => 'abc',
17
+ "d" => 'abd' } },
18
+ "x" => {
19
+ "b" =>
20
+ { "c" => 'xbc' } } }
21
+
22
+ nh = NHash.new h
23
+ nh.push_prefix 'a'
24
+
25
+ ```
26
+
27
+ Now `get` keys that start with a '.' are prefixed with a, let us demonstrate:
28
+
29
+ ```ruby
30
+ nh.get( '.b.c' ).assert == 'abc'
31
+ ```
32
+
33
+ but not using the '.' as a prefix I can still access with an _absolute_ key path.
34
+
35
+ ```ruby
36
+ nh.get( 'a.b.c').assert == 'abc'
37
+ ```
38
+
39
+ ### Suffixes and Combination of both
40
+
41
+ ```ruby
42
+ nh.push_suffix 'c'
43
+ nh.get( 'a.b.' ).assert == 'abc'
44
+ nh.get( 'x.b.' ).assert == 'xbc'
45
+
46
+ nh.get( '.b.' ).assert == 'abc'
47
+ nh.get( 'x.b.' ).assert == 'xbc'
48
+ ```
49
+
50
+
51
+ ### The Stack
52
+
53
+ As the name suggests we can push and pop suffices and prefices
54
+ This is convenient as often the suffix/Prefix correspond to
55
+ a given context in the program.
56
+
57
+ ```ruby
58
+ nh.push_prefix 'a.b'
59
+ nh.get('.d').assert == 'abd'
60
+ 2.times{ nh.pop_prefix }
61
+ KeyError.assert.raised? do
62
+ nh.get('.d').assert == 'abd'
63
+ end
64
+ ```
65
+
66
+ ### Temporary Stack Modifiaction with the With Pattern
67
+
68
+ An often more convenient way to push values to the stacks is the `with` pattern that will
69
+ ensure that the stack is popped again at the end of the provided block.
70
+
71
+
72
+ ```ruby
73
+ nh.with_prefix 'a' do |x|
74
+ # self is passed into the block for convenience
75
+ x.assert == nh
76
+
77
+ # fetch is an alias to get
78
+ x.fetch( '.b.c' ).assert == 'abc'
79
+ end
80
+ ```
81
+
82
+ Outside the block the stack will have been restored
83
+
84
+ ```ruby
85
+ KeyError.assert.raised? do
86
+ nh.fetch '.b.c'
87
+ end
88
+ ```
89
+
90
+ Works with suffix too
91
+
92
+ ```ruby
93
+ nh.with_suffix 'd' do
94
+ nh.get('a.b.').assert == 'abd'
95
+ end
96
+ ```
97
+
98
+ And as it would be combersome to nest a `with_prefix` and a `with_suffix` there is a convenience method
99
+ `with_afixes` at your disposal:
100
+
101
+ ```ruby
102
+ nh.with_affixes 'a', 'c' do
103
+ nh.get('.b.').assert == 'abc'
104
+ end
105
+ ```
106
+
107
+
108
+
@@ -0,0 +1,137 @@
1
+ # Lab42::NHash QED
2
+
3
+ ## Lookup Chains
4
+
5
+ These are convenience methods using the more general, but also much more comlicated
6
+ fallback mechanism that is demonstrated [here](https://github.com/RobertDober/lab42_nested_hash/blob/master/demo/060-fallback.md)
7
+
8
+ ### Prefix Chains
9
+
10
+ This defines a mechanism that will use different prefixes until a prefixed key is found.
11
+
12
+ ```ruby
13
+ nh = NHash.new(
14
+ en: { '1' => 'one', '2' => 'two', '3' => 'three' },
15
+ fr: { '2' => 'deux', '3' => 'trois' },
16
+ it: { '3' => 'tre' })
17
+ .with_indifferent_access
18
+ ```
19
+
20
+ Now we can simply define a prefix lookup chain as follows:
21
+
22
+ ```ruby
23
+ nh.push_prefix_lookup :it, :fr, :en
24
+ nh.with_prefix :de do
25
+ get( '.1' ).assert == 'one'
26
+ get( '.2' ).assert == 'deux'
27
+ get( '.3' ).assert == 'tre'
28
+ KeyError.assert.raised? do
29
+ get( '.4' )
30
+ end
31
+ end
32
+ ```
33
+
34
+ ### Suffix Chains
35
+
36
+ are not different of course.
37
+
38
+ ```ruby
39
+ nh = NHash.new(
40
+ en: {
41
+ dog: { one: 'dog', many: 'dogs' },
42
+ cat: { one: 'cat', many: 'cats' },
43
+ sheep: { one: 'sheep' }
44
+ },
45
+ fr: {
46
+ dog: { one: 'chien', many: 'chiens' },
47
+ cat: { one: 'chat' }, # for demonstration purpose we ommit "chats"
48
+ money: { one: 'argent' }
49
+ } ).with_indifferent_access
50
+ ```
51
+
52
+ Now we define the suffix chain
53
+
54
+ ```ruby
55
+ nh.push_suffix_lookup :many, :one
56
+ ```
57
+
58
+ proof that this works
59
+
60
+ ```ruby
61
+ nh.with_prefix :fr do
62
+ with_suffix :many do
63
+ get( '.money.').assert == 'argent'
64
+ end
65
+ end
66
+ ```
67
+
68
+ However the suffix chain masks the prefix chain, thus we will not find any bovines here:
69
+
70
+ ```ruby
71
+ nh.with_prefix :fr do
72
+ with_suffix :many do
73
+ KeyError.assert.raised? do
74
+ get( '.sheep.' )
75
+ end
76
+ end
77
+ end
78
+ ```
79
+
80
+
81
+ ### Combining Chains
82
+
83
+ In order to make both work together we need to implement a backtracking mechanism (or a
84
+ carthesian product fallback definition). This is done via the `push_affix_lookup` method
85
+
86
+ First we reset both lookup chains, for the time being there is only one method that accomplishes
87
+ this:
88
+
89
+ ```ruby
90
+ nh.clear_fallbacks!
91
+ nh.push_affix_lookup prefixes: %w{fr en}, suffixes: %w{many one}
92
+ ```
93
+
94
+ Now our sleep endorsing animals are found again:
95
+
96
+ ```ruby
97
+ nh.with_prefix :fr do
98
+ with_suffix :many do
99
+ get( '.sheep.' ).assert == 'sheep'
100
+ end
101
+ end
102
+ ```
103
+
104
+ This searches for an entry by varying the prefixes first, if you want to vary over the suffixes first
105
+ the named parameter `suffixes_first` can be set to true. Let us prove the difference by giving yet
106
+ another demo.
107
+
108
+ First we do not set `suffixes_first` and
109
+
110
+ ```ruby
111
+ nh.with_prefix :fr do
112
+ with_suffix :many do
113
+ get( '.cat.' ).assert == 'chat'
114
+ end
115
+ end
116
+ ```
117
+
118
+ `'chat'` is found (sorry for the grammatical error).
119
+
120
+ Now if we inverse the lookup order
121
+
122
+ ```ruby
123
+ nh.clear_fallbacks!
124
+ nh.push_affix_lookup prefixes: %w{fr en}, suffixes: %w{many one}, suffixes_first: true
125
+ ```
126
+
127
+ and try again, we will get
128
+
129
+ ```ruby
130
+ nh.with_prefix :fr do
131
+ with_suffix :many do
132
+ get( '.cat.' ).assert == 'cats'
133
+ end
134
+ end
135
+ ```
136
+
137
+
@@ -0,0 +1,89 @@
1
+
2
+ # Lab42::NHash QED
3
+
4
+ ## Fallbacks
5
+
6
+ Fallbacks are registred procedures, on a stack, that are executed in LIFO order.
7
+ They are triggered whenever a `get/fetch` invocation would trigger a `KeyError`.
8
+
9
+ The special `again` method is available inside the fallback procedure (and inside *only*)
10
+ to reexecute the `get/fetch` invocation that triggered the callback (it might trigger the next fallback
11
+ if applicable)
12
+
13
+ A simple example would be to define a fallback that searches with a different affix.
14
+
15
+ ```ruby
16
+ numbers = NHash.new(
17
+ en: { '1' => 'one', '2' => 'two', '3' => 'three' },
18
+ fr: { '1' => 'un', '2' => 'deux' },
19
+ it: { '1' => 'uno' } )
20
+ .with_indifferent_access
21
+ ```
22
+
23
+ It stands to reason that looking for an italian '2' will get us a `KeyError` raised.
24
+
25
+ ```ruby
26
+ KeyError.assert.raised? do
27
+ numbers.with_prefix :it do
28
+ get '.2'
29
+ end
30
+ end
31
+ ```
32
+
33
+ We will use _fallbacks_ to implement a lookup strategy. By using `push_fallback`
34
+ we will have to define them in the reverse execution order, we could also use `unshift_fallback`
35
+ if that suits your style better.
36
+
37
+ ```ruby
38
+ # use English as last resort...
39
+ numbers.push_fallback do |n|
40
+ n.with_prefix :en do
41
+ again
42
+ end
43
+ end
44
+ # But try French first
45
+ numbers.push_fallback do
46
+ with_prefix( :fr ){ again }
47
+ end
48
+ ```
49
+
50
+ Please note the two possible styles, a block with arity one ( or negative arity ) will be called
51
+ with the `NHash` instance as a parameter, all other blocks will be instance_execed with the same
52
+ instance as receiver.
53
+
54
+ These fallbacks will cause the following assertions to hold
55
+
56
+ ```ruby
57
+ numbers.get( '.1' ).assert == 'un'
58
+ numbers.with_prefix :it do
59
+ get( '.1' ).assert == 'uno'
60
+ get( '.2' ).assert == 'deux'
61
+ get( '.3' ).assert == 'three'
62
+ end
63
+ ```
64
+
65
+ ### With Pattern
66
+
67
+ Now if we can limit the validity of a defined fallback stack to a certain scope, we can use
68
+ `with_fallbacks` so that we do not need to call `clear_fallbacks!` explicitely.
69
+
70
+
71
+ ```ruby
72
+ numbers.clear_fallbacks!
73
+ KeyError.assert.raised? do
74
+ numbers.get('.1')
75
+ end
76
+
77
+ numbers.with_fallback do
78
+ push_fallback do
79
+ with_prefix( :fr ){ again }
80
+ end
81
+ with_prefix :it do
82
+ get( '.2' ).assert == 'deux'
83
+ end
84
+ end
85
+ KeyError.assert.raised? do
86
+ numbers.get('.1')
87
+ end
88
+ ```
89
+
@@ -0,0 +1,37 @@
1
+ # Lab42::NHash QED
2
+
3
+ ## Interpolation
4
+
5
+ In order to be able to use string interpolation we can use the alternative
6
+ `get!/fetch!` methods which will expand [ERB](http://www.ruby-doc.org/stdlib-2.1.1/libdoc/erb/rdoc/ERB.html) templates inside
7
+ string values.
8
+
9
+
10
+ Here is a short demonstration of that feature:
11
+
12
+ ```ruby
13
+ nh = NHash.new(
14
+ sum: '<%= get("a")+get("b") %>',
15
+ a: 41,
16
+ b: 1,
17
+ result: 'the sum is <%= get! :sum %>'
18
+ )
19
+ .with_indifferent_access
20
+ ```
21
+
22
+ `get/fetch` do not do anything
23
+
24
+ ```ruby
25
+ nh.get(:result).assert == 'the sum is <%= get! :sum %>'
26
+ ```
27
+
28
+ However `get!/fetch!` are made from a completely different kind.
29
+
30
+ ```ruby
31
+ nh.get!(:sum).assert == '42'
32
+ nh.fetch!(:result).assert == 'the sum is 42'
33
+ ```
34
+
35
+
36
+
37
+
@@ -0,0 +1 @@
1
+ require 'ae'
@@ -0,0 +1,3 @@
1
+ LIBRARY_PATH = File.expand_path '../../lib'
2
+
3
+ require 'lab42/nhash/auto_import'
@@ -0,0 +1,38 @@
1
+
2
+ base = File.dirname __FILE__
3
+ $:.unshift File.join( base, 'lib' )
4
+
5
+ require 'lab42/nhash/version'
6
+
7
+ Gem::Specification.new do | spec |
8
+ spec.name = 'lab42_nhash'
9
+ spec.version = Lab42::NHash::VERSION
10
+ spec.authors = ['Robert Dober']
11
+ spec.email = %w{ robert.dober@gmail.com }
12
+ spec.description = %{A Nested Hash with dotted access à la I18n in Rails}
13
+ spec.summary = %{A nested hash view with dotted deep access à la I18n.t of Rails and with optional string interpolation.
14
+ Typically YML loaded Hashes are used.}
15
+ spec.homepage = %{https://github.com/RobertDober/lab42_nested_hash}
16
+ spec.license = 'MIT'
17
+
18
+ spec.files = `git ls-files`.split($/)
19
+ spec.test_files = spec.files.grep(%r{\Atest|\Aspec|\Afeatures|\Ademo/})
20
+ spec.require_paths = %w{lib}
21
+
22
+ # spec.post_install_message = %q{ }
23
+
24
+
25
+ spec.required_ruby_version = '>= 2.0.0'
26
+ spec.required_rubygems_version = '>= 2.2.2'
27
+
28
+ spec.add_dependency 'forwarder2', '~> 0.2'
29
+ spec.add_dependency 'ruby_parser', '~> 3.6'
30
+
31
+ spec.add_development_dependency 'bundler', '~> 1.6'
32
+ spec.add_development_dependency 'rspec', '~> 2.14'
33
+ spec.add_development_dependency 'pry', '~> 0.9'
34
+ spec.add_development_dependency 'pry-debugger', '~> 0.2'
35
+ spec.add_development_dependency 'ae', '~> 1.8'
36
+ spec.add_development_dependency 'qed', '~> 2.9'
37
+
38
+ end