lab42_nhash 0.1.0.pre

Sign up to get free protection for your applications and to get access to all the features.
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