lab42_ihash 0.0.1.beta
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 +7 -0
- data/.gitignore +35 -0
- data/.rspec +3 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +49 -0
- data/LICENSE +21 -0
- data/README.md +35 -0
- data/demo/000-introduction.md +109 -0
- data/demo/050-constraints.md +70 -0
- data/demo/080-business-logic.md +12 -0
- data/demo/100-caching.md +77 -0
- data/demo/applique/require_ae.rb +1 -0
- data/demo/applique/require_ihash.rb +4 -0
- data/lab42_ihash.gemspec +37 -0
- data/lib/lab42/ihash/version.rb +5 -0
- data/lib/lab42/ihash.rb +89 -0
- metadata +151 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 340ee6217f0f1fabdcc7d5af631fe6c40982b6e9
|
4
|
+
data.tar.gz: ec83fb030f1ae017e193525fcedae4c8e335606b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 90f46bf55fb52af4d0d6deda21421d66e2f530c2f27869a8f2348eb48160387f35ce15fb1af1f83e9e529af76258013836367eb98aebf23ac9d711c150fee9fd
|
7
|
+
data.tar.gz: 1f67e0a1699424b0e0e45a21fe740cadb0e0db02e5cc94420c7ca7562cc0ad10ec90417468fe82c678120f66888286a54d441e92805ac021bf5769b09a7b8176
|
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
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
lab42_intellihash (0.0.1)
|
5
|
+
forwarder2 (~> 0.2)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
ae (1.8.2)
|
11
|
+
ansi
|
12
|
+
ansi (1.4.3)
|
13
|
+
brass (1.2.1)
|
14
|
+
coderay (1.1.0)
|
15
|
+
diff-lcs (1.2.5)
|
16
|
+
facets (2.9.3)
|
17
|
+
forwarder2 (0.2.0)
|
18
|
+
method_source (0.8.2)
|
19
|
+
pry (0.9.12.6)
|
20
|
+
coderay (~> 1.0)
|
21
|
+
method_source (~> 0.8)
|
22
|
+
slop (~> 3.4)
|
23
|
+
pry-nav (0.2.3)
|
24
|
+
pry (~> 0.9.10)
|
25
|
+
qed (2.9.1)
|
26
|
+
ansi
|
27
|
+
brass
|
28
|
+
facets (>= 2.8)
|
29
|
+
rspec (2.14.1)
|
30
|
+
rspec-core (~> 2.14.0)
|
31
|
+
rspec-expectations (~> 2.14.0)
|
32
|
+
rspec-mocks (~> 2.14.0)
|
33
|
+
rspec-core (2.14.8)
|
34
|
+
rspec-expectations (2.14.5)
|
35
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
36
|
+
rspec-mocks (2.14.6)
|
37
|
+
slop (3.5.0)
|
38
|
+
|
39
|
+
PLATFORMS
|
40
|
+
ruby
|
41
|
+
|
42
|
+
DEPENDENCIES
|
43
|
+
ae (~> 1.8)
|
44
|
+
bundler (~> 1.6)
|
45
|
+
lab42_intellihash!
|
46
|
+
pry (~> 0.9)
|
47
|
+
pry-nav (~> 0.2)
|
48
|
+
qed (~> 2.9)
|
49
|
+
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,35 @@
|
|
1
|
+
# lab42_ihash
|
2
|
+
|
3
|
+
A Hash with Business Logic ( call it intelligent ).
|
4
|
+
|
5
|
+
Actually it is a view layer over a Hash like object, which needs to obey the simple protocol
|
6
|
+
|
7
|
+
`Hash#fetch`.
|
8
|
+
|
9
|
+
|
10
|
+
The services `IHash` provides are of the number of four:
|
11
|
+
|
12
|
+
* Default values per key.
|
13
|
+
* Validation via _Constraints_.
|
14
|
+
* Business Logic or Lookup Logic.
|
15
|
+
* Caching of Computed Values.
|
16
|
+
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
```sh
|
21
|
+
gem install lab42_ihash --pre
|
22
|
+
```
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
require 'lab42/ihash'
|
28
|
+
|
29
|
+
IHash = Lab42::IHash
|
30
|
+
```
|
31
|
+
|
32
|
+
|
33
|
+
## Manual (QED Driven)
|
34
|
+
|
35
|
+
Details are explained in the [QED](https://github.com/rubyworks/qed) demos [here](https://github.com/RobertDober/lab42_intellihash/tree/master/demo)
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# IHash QED
|
2
|
+
|
3
|
+
## Introduction
|
4
|
+
|
5
|
+
An `IHash` is an object that lets us define an _intelligent_ way to access a Hash like object.
|
6
|
+
|
7
|
+
|
8
|
+
The services `IHash` provides are of the number of four:
|
9
|
+
|
10
|
+
* Default values per key.
|
11
|
+
* Validation via _Constraints_.
|
12
|
+
* Business Logic or Lookup Logic.
|
13
|
+
* Caching of Computed Values.
|
14
|
+
|
15
|
+
## Defaults
|
16
|
+
|
17
|
+
|
18
|
+
### Predefined
|
19
|
+
|
20
|
+
The simplest meaningful example could look like the following:
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
ihash = IHash.new a: 1, b: 2
|
24
|
+
|
25
|
+
ihash.get( :a ).assert == 1
|
26
|
+
ihash.get( :b ).assert == 2
|
27
|
+
ihash.set_values a: 42
|
28
|
+
ihash.get( :a ).assert == 42
|
29
|
+
# Defaults are not changed:
|
30
|
+
ihash.get( :b ).assert == 2
|
31
|
+
```
|
32
|
+
|
33
|
+
Alternatively the `set_defaults` method can be used:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
ihash.set_defaults a: 1, c: 2
|
37
|
+
# values are not touched
|
38
|
+
ihash.get( :a ).assert == 42
|
39
|
+
# new default values are in place
|
40
|
+
ihash.get( :c ).assert == 2
|
41
|
+
# But the old defaults have been lost
|
42
|
+
KeyError.assert.raised? do
|
43
|
+
ihash.get :b
|
44
|
+
end
|
45
|
+
```
|
46
|
+
|
47
|
+
### Defaults provided via a parameter
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
ihash = IHash.new a: 1
|
51
|
+
|
52
|
+
ihash.get( :a ).assert == 1
|
53
|
+
ihash.get( :a, 42 ).assert == 42
|
54
|
+
|
55
|
+
# However values override the default of course
|
56
|
+
ihash.set_values a: 2
|
57
|
+
ihash.get( :a, 42 ).assert == 2
|
58
|
+
|
59
|
+
```
|
60
|
+
|
61
|
+
|
62
|
+
### Computed Defaults
|
63
|
+
|
64
|
+
They are the main reason for caching, but as defaults provided
|
65
|
+
via parameters override cached values, we will need an alternative
|
66
|
+
calculation mechanisme for business logic, which we will discuss
|
67
|
+
later on.
|
68
|
+
|
69
|
+
For know all we need to know to understand the following example
|
70
|
+
is that `Proc` instances as default values will be evaluated
|
71
|
+
in the `IHash` instance's context.
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
rectangle = IHash.new \
|
75
|
+
height: ->{ get( :surface ) / get( :width ) },
|
76
|
+
width: ->{ get( :surface ) / get( :height ) },
|
77
|
+
surface: ->{ get( :width ) * get( :height ) }
|
78
|
+
|
79
|
+
# Obviously we need some data here. The following code
|
80
|
+
# will consume the whole stack before crashing.
|
81
|
+
# Later on we will see how to avoid this by means
|
82
|
+
# of defaults and constraints, and hopefully a later version
|
83
|
+
# of IHash will detect the mutual infinite recursion.
|
84
|
+
|
85
|
+
# rectangle.get :surface
|
86
|
+
|
87
|
+
# So let us provide some data
|
88
|
+
rectangle.set_values width: 20, height: 30
|
89
|
+
rectangle.get( :surface ).assert == 600
|
90
|
+
```
|
91
|
+
|
92
|
+
Now it would be nice if we could avoid the potential stack overflow
|
93
|
+
by means of defaults.
|
94
|
+
And, as promised, we can:
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
|
98
|
+
rectangle = IHash.new \
|
99
|
+
height: ->{ get( :surface, 600 ) / get( :width, 20 ) },
|
100
|
+
width: ->{ get( :surface, 600 ) / get( :height ) },
|
101
|
+
surface: ->{ get( :width ) * get( :height ) }
|
102
|
+
|
103
|
+
# These defaults shall be sufficent
|
104
|
+
rectangle.get( :surface ).assert == 600
|
105
|
+
# and they are.
|
106
|
+
|
107
|
+
```
|
108
|
+
|
109
|
+
To see how to do this with constraints, please refer to the [constraints demo](https://github.com/RobertDober/lab42_intellihash/blob/master/demo/050-constraints.md)
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# IHash QED
|
2
|
+
|
3
|
+
## Constraints
|
4
|
+
|
5
|
+
Constraints are evaluated whenever the values of an `IHash` instance are set via the `set_values` method.
|
6
|
+
|
7
|
+
The result of this evaluation is stored for later validation.
|
8
|
+
|
9
|
+
Let us dive into a simple example:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
ihash = IHash.new
|
13
|
+
.set_constraints(
|
14
|
+
a: ->(eventual_value_for_a){ eventual_value_for_a == 42 } )
|
15
|
+
.set_values a: 43
|
16
|
+
|
17
|
+
ihash.valid?.assert == false
|
18
|
+
ihash.errors.empty?.assert == false
|
19
|
+
ihash.errors.first.assert =~ /value error for key :a and value 43/
|
20
|
+
|
21
|
+
ihash.set_values a: 42
|
22
|
+
ihash.valid?.assert == true
|
23
|
+
ihash.errors.empty?.assert == true
|
24
|
+
|
25
|
+
```
|
26
|
+
|
27
|
+
In order to implement an early failure policy you need to raise exceptions yourself.
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
ArgumentError.assert.raised? do
|
31
|
+
ihash =
|
32
|
+
IHash.new
|
33
|
+
.set_constraints\
|
34
|
+
a: ->(value){ value == 42 or raise ArgumentError }
|
35
|
+
|
36
|
+
ihash.set_values a: 41
|
37
|
+
end
|
38
|
+
```
|
39
|
+
|
40
|
+
Maybe a `set_constraints!` method doing something like that for you can be
|
41
|
+
implemented later on.
|
42
|
+
|
43
|
+
Oh and I am so glad you asked, before setting values, constraints are not enforced and
|
44
|
+
the instance is therefore valid in all cases:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
ihash = IHash.new( a: nil ).set_constraints a: ->{ raise ArgumentError }
|
48
|
+
|
49
|
+
ihash.valid?.assert == true
|
50
|
+
|
51
|
+
ArgumentError.assert.raised? do
|
52
|
+
ihash.set_values b: nil
|
53
|
+
end
|
54
|
+
ihash.valid?.assert == false
|
55
|
+
ihash.errors.first.assert == 'constraint for key :a raised an error ArgumentError'
|
56
|
+
```
|
57
|
+
|
58
|
+
And this works for calculated values too, of course:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
ihash = IHash
|
62
|
+
.new( a: ->{ - get( :b ) } )
|
63
|
+
.set_constraints( a: ->(val){ val > 0 } )
|
64
|
+
.set_values b: 42
|
65
|
+
|
66
|
+
ihash.valid?.assert == false
|
67
|
+
|
68
|
+
```
|
69
|
+
|
70
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# IHash QED
|
2
|
+
|
3
|
+
## Business Logic
|
4
|
+
|
5
|
+
_Not implemented_
|
6
|
+
|
7
|
+
For the time being the defaults semantics serve all YHS' needs. But he believes
|
8
|
+
that more complex computations, recursion detection, a seperate cache that
|
9
|
+
would override default parameters and other subtle differences might come forth
|
10
|
+
one day.
|
11
|
+
|
12
|
+
|
data/demo/100-caching.md
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# IHash QED
|
2
|
+
|
3
|
+
## Caching
|
4
|
+
|
5
|
+
Will only occur when lookup values or default values are accessed.
|
6
|
+
|
7
|
+
Actually `#cache` is exposed as an attribute, but manipulate it not (or at your
|
8
|
+
own risk).
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
ihash = IHash.new \
|
12
|
+
a: ->{ 42 },
|
13
|
+
b: 42
|
14
|
+
|
15
|
+
# The cache is empty
|
16
|
+
ihash.cache.assert.empty?
|
17
|
+
|
18
|
+
# Let us access :b
|
19
|
+
ihash.get :b
|
20
|
+
# The cache is not empty any more
|
21
|
+
ihash.cache.refute.empty?
|
22
|
+
# It has been filled with :b only
|
23
|
+
ihash.cache.assert.has_key? :b
|
24
|
+
ihash.cache.refute.has_key? :a
|
25
|
+
# And now we add :a
|
26
|
+
ihash.get :a
|
27
|
+
ihash.cache.assert.has_key? :b
|
28
|
+
ihash.cache.assert.has_key? :a
|
29
|
+
|
30
|
+
```
|
31
|
+
|
32
|
+
### Setting values invalidates the cache
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
ihash.set_values b: 56
|
36
|
+
|
37
|
+
# Cache is empty again
|
38
|
+
ihash.cache.assert.empty?
|
39
|
+
# Values come before the cache, but don't do that
|
40
|
+
ihash.cache[ :b ] = 22
|
41
|
+
ihash.get( :b ).assert == 56
|
42
|
+
# We did not even break anything
|
43
|
+
ihash.get( :a ).assert == 42
|
44
|
+
ihash.cache.assert.has_key? :a
|
45
|
+
|
46
|
+
```
|
47
|
+
|
48
|
+
### Value access does not populate the cache
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
ihash.set_values b: 29
|
52
|
+
|
53
|
+
ihash.cache.assert.empty?
|
54
|
+
ihash.get( :b ).assert == 29
|
55
|
+
ihash.cache.assert.empty?
|
56
|
+
```
|
57
|
+
|
58
|
+
|
59
|
+
### Setting defaults invalidates the cache too
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
ihash.get :a
|
63
|
+
ihash.cache.keys.assert == [:a]
|
64
|
+
ihash.set_defaults
|
65
|
+
# The following is not very wise, maybe setting values, defaults
|
66
|
+
# or constraints will reset the instance.
|
67
|
+
# Not sure: ihash.get( :b ).assert == 29
|
68
|
+
ihash.cache.assert.empty?
|
69
|
+
|
70
|
+
```
|
71
|
+
|
72
|
+
|
73
|
+
This behavior might change with minor version updates as it is not very
|
74
|
+
intuitive.
|
75
|
+
|
76
|
+
Now that we are aware how caching and default values work let us go further with constraints
|
77
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'ae'
|
data/lab42_ihash.gemspec
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
base = File.dirname __FILE__
|
3
|
+
$:.unshift File.join( base, 'lib' )
|
4
|
+
|
5
|
+
require 'lab42/ihash/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do | spec |
|
8
|
+
spec.name = 'lab42_ihash'
|
9
|
+
spec.version = Lab42::IHash::VERSION
|
10
|
+
spec.authors = ['Robert Dober']
|
11
|
+
spec.email = %w{ robert.dober@gmail.com }
|
12
|
+
spec.description = %{A Hash with Business Logic (call it intelligent).}
|
13
|
+
spec.summary = %{A view over Hash like objects (needs to implement #fetch only).
|
14
|
+
Allowing to specify defaults, constraints and business logic. Implements caching for computed values.}
|
15
|
+
spec.homepage = %{https://github.com/RobertDober/lab42_ihash}
|
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
|
+
|
30
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
31
|
+
spec.add_development_dependency 'rspec', '~> 2.14'
|
32
|
+
spec.add_development_dependency 'pry', '~> 0.9'
|
33
|
+
spec.add_development_dependency 'pry-nav', '~> 0.2'
|
34
|
+
spec.add_development_dependency 'ae', '~> 1.8'
|
35
|
+
spec.add_development_dependency 'qed', '~> 2.9'
|
36
|
+
|
37
|
+
end
|
data/lib/lab42/ihash.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
module Lab42
|
2
|
+
class IHash
|
3
|
+
attr_reader :cache, :constraints, :defaults, :errors, :values
|
4
|
+
|
5
|
+
def get key, *default
|
6
|
+
values.fetch key do
|
7
|
+
lookup( key, *default )
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def set_constraints(**constraints)
|
12
|
+
@constraints << constraints
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def set_defaults(**defaults)
|
17
|
+
invalidate_cache_and_errors
|
18
|
+
@defaults = defaults
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def set_values a_hashy={}
|
23
|
+
invalidate_cache_and_errors
|
24
|
+
|
25
|
+
@values = a_hashy.dup rescue a_hashy
|
26
|
+
validate_constraints
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def valid?; errors.empty? end
|
31
|
+
|
32
|
+
private
|
33
|
+
def evaluate val_or_proc
|
34
|
+
return evaluate_proc val_or_proc if Proc===val_or_proc
|
35
|
+
val_or_proc
|
36
|
+
end
|
37
|
+
|
38
|
+
def evaluate_proc a_proc
|
39
|
+
a_proc.arity == 1 ? instance_eval( &a_proc ) : instance_exec( &a_proc )
|
40
|
+
end
|
41
|
+
|
42
|
+
def initialize(**defaults)
|
43
|
+
set_defaults(**defaults)
|
44
|
+
|
45
|
+
@cache = {}
|
46
|
+
@values = {}
|
47
|
+
|
48
|
+
@constraints = []
|
49
|
+
@errors = []
|
50
|
+
end
|
51
|
+
|
52
|
+
def invalidate_cache_and_errors
|
53
|
+
@cache = {}
|
54
|
+
@errors = []
|
55
|
+
end
|
56
|
+
|
57
|
+
# Precondition: key not in values and key not in cache
|
58
|
+
# This explains the default value provided priority, we reason:
|
59
|
+
# An explicit default value in a get shall override the predefined defaults
|
60
|
+
# **But** that means that later caching will change the behavior of get not
|
61
|
+
# my preferred programming style. This might change in the future. (E.g.
|
62
|
+
# default comes before cache lookup).
|
63
|
+
def lookup key, *default
|
64
|
+
return default.first unless default.empty?
|
65
|
+
cache.fetch key do
|
66
|
+
raise KeyError, "#{key} not found in values, defaults or logic" unless defaults.has_key? key
|
67
|
+
cache[ key ] = evaluate( defaults[key] )
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def validate_constraints
|
72
|
+
constraints.each do | constraint |
|
73
|
+
constraint.each do | k, c |
|
74
|
+
v = get k
|
75
|
+
if c.arity == 1
|
76
|
+
c.(v) || errors << "value error for key #{k.inspect} and value #{v.inspect}"
|
77
|
+
else
|
78
|
+
begin
|
79
|
+
instance_exec(&c) || errors << "value error for key #{k.inspect} and value #{v.inspect}"
|
80
|
+
rescue StandardError => e
|
81
|
+
errors << "constraint for key #{k.inspect} raised an error #{e}"
|
82
|
+
raise
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end # class IHash
|
89
|
+
end # module Lab42
|
metadata
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lab42_ihash
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.beta
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Robert Dober
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-04-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.14'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.14'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.9'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.9'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry-nav
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.2'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.2'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: ae
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.8'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.8'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: qed
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '2.9'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2.9'
|
97
|
+
description: A Hash with Business Logic (call it intelligent).
|
98
|
+
email:
|
99
|
+
- robert.dober@gmail.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- .gitignore
|
105
|
+
- .rspec
|
106
|
+
- Gemfile
|
107
|
+
- Gemfile.lock
|
108
|
+
- LICENSE
|
109
|
+
- README.md
|
110
|
+
- demo/000-introduction.md
|
111
|
+
- demo/050-constraints.md
|
112
|
+
- demo/080-business-logic.md
|
113
|
+
- demo/100-caching.md
|
114
|
+
- demo/applique/require_ae.rb
|
115
|
+
- demo/applique/require_ihash.rb
|
116
|
+
- lab42_ihash.gemspec
|
117
|
+
- lib/lab42/ihash.rb
|
118
|
+
- lib/lab42/ihash/version.rb
|
119
|
+
homepage: https://github.com/RobertDober/lab42_ihash
|
120
|
+
licenses:
|
121
|
+
- MIT
|
122
|
+
metadata: {}
|
123
|
+
post_install_message:
|
124
|
+
rdoc_options: []
|
125
|
+
require_paths:
|
126
|
+
- lib
|
127
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 2.0.0
|
132
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - '>='
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: 2.2.2
|
137
|
+
requirements: []
|
138
|
+
rubyforge_project:
|
139
|
+
rubygems_version: 2.2.2
|
140
|
+
signing_key:
|
141
|
+
specification_version: 4
|
142
|
+
summary: 'A view over Hash like objects (needs to implement #fetch only). Allowing
|
143
|
+
to specify defaults, constraints and business logic. Implements caching for computed
|
144
|
+
values.'
|
145
|
+
test_files:
|
146
|
+
- demo/000-introduction.md
|
147
|
+
- demo/050-constraints.md
|
148
|
+
- demo/080-business-logic.md
|
149
|
+
- demo/100-caching.md
|
150
|
+
- demo/applique/require_ae.rb
|
151
|
+
- demo/applique/require_ihash.rb
|