hashr 1.0.0 → 2.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +5 -0
- data/README.md +64 -102
- data/lib/hashr.rb +60 -84
- data/lib/hashr/core_ext/ruby/hash.rb +2 -16
- data/lib/hashr/delegate/conditional.rb +13 -0
- data/lib/hashr/delegate/hash.rb +48 -0
- data/lib/hashr/env.rb +48 -0
- data/lib/hashr/version.rb +2 -2
- metadata +9 -55
- data/Gemfile.lock +0 -21
- data/Rakefile +0 -12
- data/hashr.gemspec +0 -23
- data/lib/hashr/env_defaults.rb +0 -31
- data/test/core_ext_test.rb +0 -28
- data/test/hashr_test.rb +0 -230
- data/test/test_helper.rb +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 865d5c744eb989eb6faa51b252a52b83ac31fd49
|
4
|
+
data.tar.gz: a69e87efba10818c1af7036e02d75327210dbc3b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1e2285af349146a905c6bcc53f5bdacc9d644f2ca48e05e69d700e18082d2285dd1b49e16670808a470e72e6ced4795ceb614d6d4eeded4310e61c9f1ffb559e
|
7
|
+
data.tar.gz: 993a3e986b50d2c9c34bd2ce153f2a25c10d0530962cca561880361f9080d83cdbb87f83de34b3dc11029ed88a5cf479d9de259396dc7d09b089f00d9820a336
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -2,145 +2,107 @@
|
|
2
2
|
|
3
3
|
# Hashr
|
4
4
|
|
5
|
-
Hashr is a very simple and tiny class
|
5
|
+
Hashr is a very simple and tiny class which makes using nested hashes for
|
6
|
+
configuration (and other purposes) easier.
|
6
7
|
|
7
8
|
It supports the following features:
|
8
9
|
|
9
10
|
* method read and write access
|
10
11
|
* automatic predicate (boolean, i.e. `?`) methods
|
11
12
|
* easy defaults
|
12
|
-
*
|
13
|
-
* automatic symbolized keys
|
13
|
+
* indifferent (strings vs symbols) keys
|
14
14
|
|
15
15
|
## Usage
|
16
16
|
|
17
17
|
Directly use Hashr instances like this:
|
18
18
|
|
19
|
-
|
19
|
+
```ruby
|
20
|
+
config = Hashr.new(foo: { bar: 'bar' })
|
20
21
|
|
21
|
-
|
22
|
-
|
22
|
+
config.foo? # => true
|
23
|
+
config.foo # => { bar: 'bar' }
|
23
24
|
|
24
|
-
|
25
|
-
|
25
|
+
config.foo.bar? # => true
|
26
|
+
config.foo.bar # => 'bar'
|
26
27
|
|
27
|
-
|
28
|
-
|
28
|
+
config.foo.bar = 'bar'
|
29
|
+
config.foo.bar # => 'bar'
|
29
30
|
|
30
|
-
|
31
|
-
|
31
|
+
config.foo.baz = 'baz'
|
32
|
+
config.foo.baz # => 'baz'
|
33
|
+
```
|
32
34
|
|
33
|
-
|
35
|
+
Hash core methods are not available but assume you mean to look up keys with
|
36
|
+
the same name:
|
34
37
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
+
```ruby
|
39
|
+
config = Hashr.new(count: 1, key: 'key')
|
40
|
+
config.count # => 1
|
41
|
+
config.key # => 'key'
|
42
|
+
```
|
38
43
|
|
39
|
-
|
44
|
+
In order to check a hash stored on a certain key you can convert it to a Ruby
|
45
|
+
Hash:
|
40
46
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
47
|
+
```ruby
|
48
|
+
config = Hashr.new(count: 1, key: 'key')
|
49
|
+
config.to_h.count # => 2
|
50
|
+
config.to_h.key # => raises ArgumentError: "wrong number of arguments (0 for 1)"
|
51
|
+
```
|
45
52
|
|
46
|
-
|
53
|
+
Missing keys won't raise an exception but instead behave like Hash access:
|
47
54
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
config.count # => 3
|
55
|
+
```ruby
|
56
|
+
config = Hashr.new
|
57
|
+
config.foo? # => false
|
58
|
+
config.foo # => nil
|
59
|
+
```
|
54
60
|
|
55
|
-
|
61
|
+
## Defaults
|
56
62
|
|
57
|
-
|
58
|
-
defaults = { :bar => 'bar' }
|
59
|
-
config = Hashr.new(data, defaults)
|
60
|
-
config.foo # => 'foo'
|
61
|
-
config.bar # => 'bar'
|
63
|
+
Defaults can be defined per class:
|
62
64
|
|
63
|
-
|
65
|
+
```ruby
|
66
|
+
class Config < Hashr
|
67
|
+
default boxes: { memory: '1024' }
|
68
|
+
end
|
64
69
|
|
65
|
-
|
66
|
-
|
70
|
+
config = Config.new
|
71
|
+
config.boxes.memory # => 1024
|
72
|
+
```
|
67
73
|
|
68
|
-
|
69
|
-
self[:count]
|
70
|
-
end
|
71
|
-
end
|
74
|
+
Or passed to the instance:
|
72
75
|
|
73
|
-
|
74
|
-
|
76
|
+
```ruby
|
77
|
+
data = {}
|
78
|
+
defaults = { boxes: { memory: '1024' } }
|
75
79
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
module Boxes
|
80
|
-
def count
|
81
|
-
self[:count] # overwrites a Hash method to return the Hash's content here
|
82
|
-
end
|
83
|
-
|
84
|
-
def names
|
85
|
-
@names ||= (1..count).map { |num| "box-#{num}" }
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
define :boxes => { :count => 3, :_include => Boxes }
|
90
|
-
end
|
91
|
-
|
92
|
-
config = Config.new
|
93
|
-
config.boxes # => { :count => 3 }
|
94
|
-
config.boxes.count # => 3
|
95
|
-
config.boxes.names # => ["box-1", "box-2", "box-3"]
|
96
|
-
|
97
|
-
As overwriting Hash methods for method access to keys is a common pattern there's a short cut to it:
|
98
|
-
|
99
|
-
class Config < Hashr
|
100
|
-
define :_access => [:count, :key]
|
101
|
-
end
|
102
|
-
|
103
|
-
config = Config.new(:count => 3, :key => 'key')
|
104
|
-
config.count # => 3
|
105
|
-
config.key # => 'key'
|
106
|
-
|
107
|
-
Both `:_include` and `:_access` can be defined as defaults, i.e. so that they will be used on all nested hashes:
|
108
|
-
|
109
|
-
class Config < Hashr
|
110
|
-
default :_access => :key
|
111
|
-
end
|
112
|
-
|
113
|
-
config = Config.new(:key => 'key', :foo => { :key => 'foo.key' })
|
114
|
-
config.key # => 'key'
|
115
|
-
config.foo.key # => 'foo.key'
|
80
|
+
config = Hashr.new(data, defaults)
|
81
|
+
config.boxes.memory # => 1024
|
82
|
+
```
|
116
83
|
|
117
84
|
## Environment defaults
|
118
85
|
|
119
|
-
Hashr includes a simple module that makes it easy to overwrite configuration
|
86
|
+
Hashr includes a simple module that makes it easy to overwrite configuration
|
87
|
+
defaults from environment variables:
|
120
88
|
|
121
|
-
|
122
|
-
|
89
|
+
```ruby
|
90
|
+
class Config < Hashr
|
91
|
+
extend Hashr::Env
|
123
92
|
|
124
|
-
|
93
|
+
self.env_namespace = 'foo'
|
125
94
|
|
126
|
-
|
127
|
-
|
95
|
+
default boxes: { memory: '1024' }
|
96
|
+
end
|
97
|
+
```
|
128
98
|
|
129
99
|
Now when an environment variable is defined then it will overwrite the default:
|
130
100
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
You can run the tests as follows:
|
138
|
-
|
139
|
-
# going through bundler
|
140
|
-
bundle exec rake
|
141
|
-
|
142
|
-
# using just ruby
|
143
|
-
ruby -rubygems -Ilib:test test/hashr_test.rb
|
101
|
+
```ruby
|
102
|
+
ENV['FOO_BOXES_MEMORY'] = '2048'
|
103
|
+
config = Config.new
|
104
|
+
config.boxes.memory # => '2048'
|
105
|
+
```
|
144
106
|
|
145
107
|
## Other libraries
|
146
108
|
|
data/lib/hashr.rb
CHANGED
@@ -1,132 +1,108 @@
|
|
1
1
|
require 'hashr/core_ext/ruby/hash'
|
2
2
|
|
3
|
-
class Hashr <
|
4
|
-
|
5
|
-
|
6
|
-
TEMPLATE = new
|
3
|
+
class Hashr < BasicObject
|
4
|
+
require 'hashr/delegate/conditional'
|
5
|
+
require 'hashr/env'
|
7
6
|
|
8
7
|
class << self
|
9
|
-
|
8
|
+
attr_reader :defaults
|
10
9
|
|
11
|
-
def
|
12
|
-
|
10
|
+
def inherited(other)
|
11
|
+
other.default(defaults)
|
13
12
|
end
|
14
13
|
|
15
|
-
def
|
16
|
-
|
14
|
+
def new(*args)
|
15
|
+
super(self, *args)
|
17
16
|
end
|
18
17
|
|
19
18
|
def default(defaults)
|
20
|
-
@defaults =
|
19
|
+
@defaults = (self.defaults || {}).deep_merge(defaults || {})
|
21
20
|
end
|
21
|
+
alias :define :default
|
22
22
|
|
23
|
-
def
|
24
|
-
|
23
|
+
def const_missing(name)
|
24
|
+
Kernel.const_get(name)
|
25
25
|
end
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_reader :class
|
29
|
+
|
30
|
+
def initialize(klass, data = nil, defaults = nil, &block)
|
31
|
+
::Kernel.fail ::ArgumentError.new("Invalid input #{data.inspect}") unless data.nil? || data.is_a?(::Hash)
|
26
32
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
deep_accessorize(value)
|
34
|
-
end
|
33
|
+
data = (data || {}).deep_symbolize_keys
|
34
|
+
defaults = (defaults || klass.defaults || {}).deep_symbolize_keys
|
35
|
+
|
36
|
+
@class = klass
|
37
|
+
@data = defaults.deep_merge(data).inject({}) do |result, (key, value)|
|
38
|
+
result.merge(key => value.is_a?(::Hash) ? ::Hashr.new(value, {}) : value)
|
35
39
|
end
|
36
40
|
end
|
37
41
|
|
38
|
-
|
39
|
-
|
40
|
-
def initialize(data = {}, definition = self.class.definition, &block)
|
41
|
-
raise(ArgumentError.new("Invalid input #{data.inspect}")) unless data.nil? || data.is_a?(Hash)
|
42
|
-
replace((deep_hashrize(definition.deep_merge((data || {}).deep_symbolize_keys))))
|
43
|
-
deep_defaultize(self)
|
44
|
-
(class << self; self; end).class_eval(&block) if block_given?
|
42
|
+
def defined?(key)
|
43
|
+
@data.key?(to_key(key))
|
45
44
|
end
|
46
45
|
|
47
|
-
def [](key
|
48
|
-
|
49
|
-
super(key.to_sym)
|
46
|
+
def [](key)
|
47
|
+
@data[to_key(key)]
|
50
48
|
end
|
51
49
|
|
52
50
|
def []=(key, value)
|
53
|
-
|
51
|
+
@data.store(to_key(key), value.is_a?(::Hash) ? ::Hashr.new(value, {}) : value)
|
54
52
|
end
|
55
53
|
|
56
|
-
def
|
57
|
-
|
58
|
-
tokens.size == 1 ? self[path] = value : self[tokens.shift, Hashr.new].set(tokens.join('.'), value, stack)
|
54
|
+
def values_at(*keys)
|
55
|
+
keys.map { |key| self[key] }
|
59
56
|
end
|
60
57
|
|
61
|
-
def respond_to?(
|
62
|
-
|
63
|
-
key?(method)
|
64
|
-
else
|
65
|
-
true
|
66
|
-
end
|
58
|
+
def respond_to?(*args)
|
59
|
+
true
|
67
60
|
end
|
68
61
|
|
69
62
|
def method_missing(name, *args, &block)
|
70
63
|
case name.to_s[-1, 1]
|
71
64
|
when '?'
|
72
|
-
!!self[name.to_s[0..-2]
|
65
|
+
!!self[name.to_s[0..-2]]
|
73
66
|
when '='
|
74
|
-
self[name.to_s[0..-2]
|
67
|
+
self[name.to_s[0..-2]] = args.first
|
75
68
|
else
|
76
|
-
raise(IndexError.new("Key #{name.inspect} is not defined.")) if !key?(name) && self.class.raise_missing_keys
|
77
69
|
self[name]
|
78
70
|
end
|
79
71
|
end
|
80
72
|
|
81
|
-
def
|
82
|
-
|
73
|
+
def try(key)
|
74
|
+
defined?(key) ? self[key] : nil # TODO needs to look for to_h etc
|
83
75
|
end
|
84
76
|
|
85
|
-
def
|
86
|
-
|
77
|
+
def to_h
|
78
|
+
@data.inject({}) do |hash, (key, value)|
|
79
|
+
hash.merge(key => value.respond_to?(:to_h) ? value.to_h : value)
|
80
|
+
end
|
87
81
|
end
|
82
|
+
alias to_hash to_h
|
88
83
|
|
89
|
-
def
|
90
|
-
|
84
|
+
def ==(other)
|
85
|
+
to_h == other.to_h if other.respond_to?(:to_h)
|
91
86
|
end
|
92
87
|
|
93
|
-
def
|
94
|
-
|
95
|
-
hash[key] = value.is_a?(Hashr) ? value.to_hash : value
|
96
|
-
hash
|
97
|
-
end
|
88
|
+
def instance_of?(const)
|
89
|
+
self.class == const
|
98
90
|
end
|
99
91
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
result
|
113
|
-
end
|
114
|
-
end
|
92
|
+
def is_a?(const)
|
93
|
+
consts = [self.class]
|
94
|
+
consts << consts.last.superclass while consts.last.superclass
|
95
|
+
consts.include?(const)
|
96
|
+
end
|
97
|
+
alias kind_of? is_a?
|
98
|
+
|
99
|
+
def inspect
|
100
|
+
"<#{self.class.name} #{@data.inspect}>"
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
115
104
|
|
116
|
-
def
|
117
|
-
|
118
|
-
case key.to_sym
|
119
|
-
when :_include
|
120
|
-
hash.include_modules(value)
|
121
|
-
when :_access
|
122
|
-
hash.include_accessors(value)
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
hash.each do |key, value|
|
127
|
-
deep_defaultize(value) if value.is_a?(Hash)
|
128
|
-
end
|
129
|
-
|
130
|
-
hash
|
105
|
+
def to_key(key)
|
106
|
+
key.respond_to?(:to_sym) ? key.to_sym : key
|
131
107
|
end
|
132
108
|
end
|
@@ -1,14 +1,4 @@
|
|
1
1
|
class Hash
|
2
|
-
def slice(*keep_keys)
|
3
|
-
h = {}
|
4
|
-
keep_keys.each { |key| h[key] = fetch(key) if key?(key) }
|
5
|
-
h
|
6
|
-
end unless Hash.method_defined?(:slice)
|
7
|
-
|
8
|
-
def except(*less_keys)
|
9
|
-
slice(*keys - less_keys)
|
10
|
-
end unless Hash.method_defined?(:except)
|
11
|
-
|
12
2
|
def deep_symbolize_keys
|
13
3
|
symbolizer = lambda do |value|
|
14
4
|
case value
|
@@ -24,15 +14,11 @@ class Hash
|
|
24
14
|
result[(key.to_sym rescue key) || key] = symbolizer.call(value)
|
25
15
|
result
|
26
16
|
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def deep_symbolize_keys!
|
30
|
-
replace(deep_symbolize_keys)
|
31
|
-
end
|
17
|
+
end unless instance_methods.include?(:deep_symbolize_keys)
|
32
18
|
|
33
19
|
# deep_merge_hash! by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
|
34
20
|
def deep_merge(other)
|
35
21
|
merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
|
36
22
|
merge(other, &merger)
|
37
|
-
end unless
|
23
|
+
end unless instance_methods.include?(:deep_merge)
|
38
24
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class Hashr
|
2
|
+
module Delegation
|
3
|
+
module Hash
|
4
|
+
METHODS = [
|
5
|
+
:all?,
|
6
|
+
:any?,
|
7
|
+
:clear,
|
8
|
+
:delete,
|
9
|
+
:delete_if,
|
10
|
+
:detect,
|
11
|
+
:drop,
|
12
|
+
:drop_while,
|
13
|
+
:each,
|
14
|
+
:empty?,
|
15
|
+
:fetch,
|
16
|
+
:find,
|
17
|
+
:flat_map,
|
18
|
+
:grep,
|
19
|
+
:group_by,
|
20
|
+
:hash,
|
21
|
+
:inject,
|
22
|
+
:invert,
|
23
|
+
:is_a?,
|
24
|
+
:keep_if,
|
25
|
+
:key,
|
26
|
+
:key?,
|
27
|
+
:keys,
|
28
|
+
:length,
|
29
|
+
:map,
|
30
|
+
:merge,
|
31
|
+
:nil?,
|
32
|
+
:none?,
|
33
|
+
:one?,
|
34
|
+
:reduce,
|
35
|
+
:reject,
|
36
|
+
:select,
|
37
|
+
:size,
|
38
|
+
:value?,
|
39
|
+
:values,
|
40
|
+
:values_at
|
41
|
+
]
|
42
|
+
|
43
|
+
METHODS.each do |name|
|
44
|
+
define_method(name) { |*args, &block| @data.send(name, *args, &block }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/hashr/env.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
class Hashr
|
2
|
+
module Env
|
3
|
+
class Vars < Struct.new(:defaults, :namespace)
|
4
|
+
FALSE = [false, nil, 'false', 'nil', '']
|
5
|
+
|
6
|
+
def to_h
|
7
|
+
defaults.deep_merge(read_env(defaults, namespace.dup))
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def read_env(defaults, namespace)
|
13
|
+
defaults.inject({}) do |result, (key, default)|
|
14
|
+
keys = namespace + [key]
|
15
|
+
value = default.is_a?(Hash) ? read_env(default, keys) : var(keys, default)
|
16
|
+
result.merge(key => value)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def var(keys, default)
|
21
|
+
key = keys.map(&:upcase).join('_')
|
22
|
+
value = ENV.fetch(key, default)
|
23
|
+
cast(value, default, keys)
|
24
|
+
end
|
25
|
+
|
26
|
+
def cast(value, default, keys)
|
27
|
+
case default
|
28
|
+
when Array
|
29
|
+
value.respond_to?(:split) ? value.split(',') : Array(value)
|
30
|
+
when true, false
|
31
|
+
not FALSE.include?(value)
|
32
|
+
else
|
33
|
+
value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def namespace
|
38
|
+
Array(super && super.upcase)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
attr_accessor :env_namespace
|
43
|
+
|
44
|
+
def defaults
|
45
|
+
Vars.new(super, env_namespace).to_h
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/hashr/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
class Hashr <
|
2
|
-
VERSION = '
|
1
|
+
class Hashr < BasicObject
|
2
|
+
VERSION = '2.0.0.rc1'
|
3
3
|
end
|
metadata
CHANGED
@@ -1,77 +1,31 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hashr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sven Fuchs
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-10-
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: rake
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: test_declarative
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: 0.0.2
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: 0.0.2
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: minitest
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: 5.0.0
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: 5.0.0
|
11
|
+
date: 2015-10-07 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
55
13
|
description: Simple Hash extension to make working with nested hashes (e.g. for configuration)
|
56
14
|
easier and less error-prone.
|
57
|
-
email: svenfuchs
|
15
|
+
email: me@svenfuchs.com
|
58
16
|
executables: []
|
59
17
|
extensions: []
|
60
18
|
extra_rdoc_files: []
|
61
19
|
files:
|
62
20
|
- Gemfile
|
63
|
-
- Gemfile.lock
|
64
21
|
- MIT-LICENSE
|
65
22
|
- README.md
|
66
|
-
- Rakefile
|
67
|
-
- hashr.gemspec
|
68
23
|
- lib/hashr.rb
|
69
24
|
- lib/hashr/core_ext/ruby/hash.rb
|
70
|
-
- lib/hashr/
|
25
|
+
- lib/hashr/delegate/conditional.rb
|
26
|
+
- lib/hashr/delegate/hash.rb
|
27
|
+
- lib/hashr/env.rb
|
71
28
|
- lib/hashr/version.rb
|
72
|
-
- test/core_ext_test.rb
|
73
|
-
- test/hashr_test.rb
|
74
|
-
- test/test_helper.rb
|
75
29
|
homepage: http://github.com/svenfuchs/hashr
|
76
30
|
licenses: []
|
77
31
|
metadata: {}
|
@@ -86,9 +40,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
86
40
|
version: '0'
|
87
41
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
42
|
requirements:
|
89
|
-
- - "
|
43
|
+
- - ">"
|
90
44
|
- !ruby/object:Gem::Version
|
91
|
-
version:
|
45
|
+
version: 1.3.1
|
92
46
|
requirements: []
|
93
47
|
rubyforge_project: "[none]"
|
94
48
|
rubygems_version: 2.4.5
|
data/Gemfile.lock
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
hashr (0.0.22)
|
5
|
-
|
6
|
-
GEM
|
7
|
-
remote: https://rubygems.org/
|
8
|
-
specs:
|
9
|
-
minitest (5.3.4)
|
10
|
-
rake (0.9.2)
|
11
|
-
test_declarative (0.0.5)
|
12
|
-
|
13
|
-
PLATFORMS
|
14
|
-
java
|
15
|
-
ruby
|
16
|
-
|
17
|
-
DEPENDENCIES
|
18
|
-
hashr!
|
19
|
-
minitest (>= 5.0.0)
|
20
|
-
rake
|
21
|
-
test_declarative (>= 0.0.2)
|
data/Rakefile
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
require 'rake'
|
2
|
-
require 'rake/testtask'
|
3
|
-
|
4
|
-
Rake::TestTask.new do |t|
|
5
|
-
t.libs << 'lib' << 'test'
|
6
|
-
# this currently does not work in rake 0.9.2, 0.9.3 will fix it
|
7
|
-
# t.pattern = 'test/**/*_test.rb'
|
8
|
-
t.test_files = FileList['test/**/*_test.rb']
|
9
|
-
t.verbose = false
|
10
|
-
end
|
11
|
-
|
12
|
-
task :default => :test
|
data/hashr.gemspec
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
$:.unshift File.expand_path('../lib', __FILE__)
|
4
|
-
require 'hashr/version'
|
5
|
-
|
6
|
-
Gem::Specification.new do |s|
|
7
|
-
s.name = "hashr"
|
8
|
-
s.version = Hashr::VERSION
|
9
|
-
s.authors = ["Sven Fuchs"]
|
10
|
-
s.email = "svenfuchs@artweb-design.de"
|
11
|
-
s.homepage = "http://github.com/svenfuchs/hashr"
|
12
|
-
s.summary = "Simple Hash extension to make working with nested hashes (e.g. for configuration) easier and less error-prone"
|
13
|
-
s.description = "Simple Hash extension to make working with nested hashes (e.g. for configuration) easier and less error-prone."
|
14
|
-
|
15
|
-
s.files = Dir['{lib/**/*,test/**/*,[A-Z]*}']
|
16
|
-
s.platform = Gem::Platform::RUBY
|
17
|
-
s.require_path = 'lib'
|
18
|
-
s.rubyforge_project = '[none]'
|
19
|
-
|
20
|
-
s.add_development_dependency 'rake'
|
21
|
-
s.add_development_dependency 'test_declarative', '>=0.0.2'
|
22
|
-
s.add_development_dependency 'minitest', '>=5.0.0'
|
23
|
-
end
|
data/lib/hashr/env_defaults.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
class Hashr < Hash
|
2
|
-
module EnvDefaults
|
3
|
-
attr_writer :env_namespace
|
4
|
-
|
5
|
-
def env_namespace=(env_namespace)
|
6
|
-
@env_namespace = [env_namespace.upcase]
|
7
|
-
end
|
8
|
-
|
9
|
-
def env_namespace
|
10
|
-
@env_namespace ||= []
|
11
|
-
end
|
12
|
-
|
13
|
-
def definition
|
14
|
-
deep_enverize(super)
|
15
|
-
end
|
16
|
-
|
17
|
-
protected
|
18
|
-
|
19
|
-
def deep_enverize(hash, nesting = env_namespace)
|
20
|
-
hash.each do |key, value|
|
21
|
-
nesting << key.to_s.upcase
|
22
|
-
hash[key] = case value
|
23
|
-
when Hash
|
24
|
-
deep_enverize(value, nesting)
|
25
|
-
else
|
26
|
-
ENV.fetch(nesting.join('_'), value)
|
27
|
-
end.tap { nesting.pop }
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
data/test/core_ext_test.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class CoreExtTest < Minitest::Test
|
4
|
-
test 'Hash#deep_symbolize_keys walks arrays, too' do
|
5
|
-
hash = { 'foo' => [{ 'bar' => 'bar', 'baz' => { 'buz' => 'buz' } }] }
|
6
|
-
expected = { :foo => [{ :bar => 'bar', :baz => { :buz => 'buz' } }] }
|
7
|
-
assert_equal expected, hash.deep_symbolize_keys
|
8
|
-
end
|
9
|
-
|
10
|
-
test 'Hash#deep_symbolize_keys! replaces with deep_symbolize' do
|
11
|
-
hash = { 'foo' => { 'bar' => 'baz' } }
|
12
|
-
expected = { :foo => { :bar => 'baz' } }
|
13
|
-
hash.deep_symbolize_keys!
|
14
|
-
assert_equal expected, hash
|
15
|
-
end
|
16
|
-
|
17
|
-
test 'Hash#slice returns a new hash containing the given keys' do
|
18
|
-
hash = { :foo => 'foo', :bar => 'bar', :baz => 'baz' }
|
19
|
-
expected = { :foo => 'foo', :bar => 'bar' }
|
20
|
-
assert_equal expected, hash.slice(:foo, :bar)
|
21
|
-
end
|
22
|
-
|
23
|
-
test 'Hash#slice does not explode on a missing key' do
|
24
|
-
hash = {}
|
25
|
-
expected = {}
|
26
|
-
assert_equal expected, hash.slice(:foo)
|
27
|
-
end
|
28
|
-
end
|
data/test/hashr_test.rb
DELETED
@@ -1,230 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class HashrTest < Minitest::Test
|
4
|
-
def teardown
|
5
|
-
Hashr.raise_missing_keys = false
|
6
|
-
end
|
7
|
-
|
8
|
-
test 'initialize takes nil' do
|
9
|
-
Hashr.new(nil)
|
10
|
-
end
|
11
|
-
|
12
|
-
test 'initialize raises an ArgumentError when given a string' do
|
13
|
-
assert_raises(ArgumentError) { Hashr.new("foo") }
|
14
|
-
end
|
15
|
-
|
16
|
-
test 'method access on an existing key returns the value' do
|
17
|
-
assert_equal 'foo', Hashr.new(:foo => 'foo').foo
|
18
|
-
end
|
19
|
-
|
20
|
-
test 'method access on a non-existing key returns nil when raise_missing_keys is false' do
|
21
|
-
Hashr.raise_missing_keys = false
|
22
|
-
assert_nil Hashr.new(:foo => 'foo').bar
|
23
|
-
end
|
24
|
-
|
25
|
-
test 'method access on a non-existing key raises an IndexError when raise_missing_keys is true' do
|
26
|
-
Hashr.raise_missing_keys = true
|
27
|
-
assert_raises(IndexError) { Hashr.new(:foo => 'foo').bar }
|
28
|
-
end
|
29
|
-
|
30
|
-
test 'method access on an existing nested key returns the value' do
|
31
|
-
assert_equal 'bar', Hashr.new(:foo => { :bar => 'bar' }).foo.bar
|
32
|
-
end
|
33
|
-
|
34
|
-
test 'method access on a non-existing nested key returns nil when raise_missing_keys is false' do
|
35
|
-
Hashr.raise_missing_keys = false
|
36
|
-
assert_nil Hashr.new(:foo => { :bar => 'bar' }).foo.baz
|
37
|
-
end
|
38
|
-
|
39
|
-
test 'method access on a non-existing nested key raises an IndexError when raise_missing_keys is true' do
|
40
|
-
Hashr.raise_missing_keys = true
|
41
|
-
assert_raises(IndexError) { Hashr.new(:foo => { :bar => 'bar' }).foo.baz }
|
42
|
-
end
|
43
|
-
|
44
|
-
test 'method access with a question mark returns true if the key has a value' do
|
45
|
-
assert_equal true, Hashr.new(:foo => { :bar => 'bar' }).foo.bar?
|
46
|
-
end
|
47
|
-
|
48
|
-
test 'method access with a question mark returns false if the key does not have a value' do
|
49
|
-
assert_equal false, Hashr.new(:foo => { :bar => 'bar' }).foo.baz?
|
50
|
-
end
|
51
|
-
|
52
|
-
test 'hash access is indifferent about symbols/strings (string data given, symbol keys used)' do
|
53
|
-
assert_equal 'bar', Hashr.new('foo' => { 'bar' => 'bar' })[:foo][:bar]
|
54
|
-
end
|
55
|
-
|
56
|
-
test 'hash access is indifferent about symbols/strings (symbol data given, string keys used)' do
|
57
|
-
assert_equal 'bar', Hashr.new(:foo => { :bar => 'bar' })['foo']['bar']
|
58
|
-
end
|
59
|
-
|
60
|
-
test 'mixing symbol and string keys in defaults and data' do
|
61
|
-
Symbolized = Class.new(Hashr) { define :foo => 'foo' }
|
62
|
-
Stringified = Class.new(Hashr) { define 'foo' => 'foo' }
|
63
|
-
NoDefault = Class.new(Hashr)
|
64
|
-
|
65
|
-
assert_equal 'foo', Symbolized.new.foo
|
66
|
-
assert_equal 'foo', Stringified.new.foo
|
67
|
-
assert_nil NoDefault.new.foo
|
68
|
-
|
69
|
-
assert_equal 'foo', Symbolized.new(:foo => 'foo').foo
|
70
|
-
assert_equal 'foo', Stringified.new(:foo => 'foo').foo
|
71
|
-
assert_equal 'foo', NoDefault.new(:foo => 'foo').foo
|
72
|
-
|
73
|
-
assert_equal 'foo', Symbolized.new('foo' => 'foo').foo
|
74
|
-
assert_equal 'foo', Stringified.new('foo' => 'foo').foo
|
75
|
-
assert_equal 'foo', NoDefault.new('foo' => 'foo').foo
|
76
|
-
end
|
77
|
-
|
78
|
-
test 'method assignment works' do
|
79
|
-
hashr = Hashr.new
|
80
|
-
hashr.foo = 'foo'
|
81
|
-
assert_equal 'foo', hashr.foo
|
82
|
-
end
|
83
|
-
|
84
|
-
test 'method using a string key works' do
|
85
|
-
hashr = Hashr.new
|
86
|
-
hashr['foo'] = 'foo'
|
87
|
-
assert_equal 'foo', hashr.foo
|
88
|
-
end
|
89
|
-
|
90
|
-
test 'using a symbol key works' do
|
91
|
-
hashr = Hashr.new
|
92
|
-
hashr[:foo] = 'foo'
|
93
|
-
assert_equal 'foo', hashr.foo
|
94
|
-
end
|
95
|
-
|
96
|
-
test 'respond_to? returns true if raise_missing_keys is off' do
|
97
|
-
Hashr.raise_missing_keys = false
|
98
|
-
hashr = Hashr.new
|
99
|
-
assert hashr.respond_to?(:foo)
|
100
|
-
end
|
101
|
-
|
102
|
-
test 'respond_to? returns false for missing keys if raise_missing_keys is on' do
|
103
|
-
Hashr.raise_missing_keys = true
|
104
|
-
hashr = Hashr.new
|
105
|
-
assert_equal false, hashr.respond_to?(:foo)
|
106
|
-
end
|
107
|
-
|
108
|
-
test 'respond_to? returns true for extant keys if raise_missing_keys is on' do
|
109
|
-
Hashr.raise_missing_keys = true
|
110
|
-
hashr = Hashr.new
|
111
|
-
hashr[:foo] = 'bar'
|
112
|
-
assert hashr.respond_to?(:foo)
|
113
|
-
end
|
114
|
-
|
115
|
-
test 'defining defaults' do
|
116
|
-
klass = Class.new(Hashr) do
|
117
|
-
define :foo => 'foo', :bar => { :baz => 'baz' }
|
118
|
-
end
|
119
|
-
assert_equal 'foo', klass.new.foo
|
120
|
-
assert_equal 'baz', klass.new.bar.baz
|
121
|
-
end
|
122
|
-
|
123
|
-
test 'defining different defaults on different classes ' do
|
124
|
-
foo = Class.new(Hashr) { define :foo => 'foo' }
|
125
|
-
bar = Class.new(Hashr) { define :bar => 'bar' }
|
126
|
-
|
127
|
-
assert_equal 'foo', foo.definition[:foo]
|
128
|
-
assert_equal 'bar', bar.definition[:bar]
|
129
|
-
end
|
130
|
-
|
131
|
-
test 'defining different env_namespaces on different classes ' do
|
132
|
-
foo = Class.new(Hashr) {extend Hashr::EnvDefaults; self.env_namespace = 'foo' }
|
133
|
-
bar = Class.new(Hashr) {extend Hashr::EnvDefaults; self.env_namespace = 'bar' }
|
134
|
-
|
135
|
-
assert_equal ['FOO'], foo.env_namespace
|
136
|
-
assert_equal ['BAR'], bar.env_namespace
|
137
|
-
end
|
138
|
-
|
139
|
-
test 'defaults to env vars' do
|
140
|
-
klass = Class.new(Hashr) do
|
141
|
-
extend Hashr::EnvDefaults
|
142
|
-
self.env_namespace = 'hashr'
|
143
|
-
define :foo => 'foo', :bar => { :baz => 'baz' }
|
144
|
-
end
|
145
|
-
|
146
|
-
ENV['HASHR_FOO'] = 'env foo'
|
147
|
-
ENV['HASHR_BAR_BAZ'] = 'env bar baz'
|
148
|
-
|
149
|
-
hashr = klass.new
|
150
|
-
assert_equal 'env foo', hashr.foo
|
151
|
-
assert_equal 'env bar baz', hashr.bar.baz
|
152
|
-
|
153
|
-
# ENV.delete('HASHR_FOO')
|
154
|
-
# ENV.delete('HASHR_BAR_BAZ')
|
155
|
-
|
156
|
-
# hashr = klass.new
|
157
|
-
# assert_equal 'foo', hashr.foo
|
158
|
-
# assert_equal 'bar baz', hashr.bar.baz
|
159
|
-
end
|
160
|
-
|
161
|
-
test 'a key :_include includes the given modules' do
|
162
|
-
klass = Class.new(Hashr) do
|
163
|
-
define :foo => { :_include => Module.new { def helper; 'helper'; end } }
|
164
|
-
end
|
165
|
-
hashr = klass.new(:foo => { 'helper' => 'foo' })
|
166
|
-
assert_equal 'helper', klass.new(:foo => { 'helper' => 'foo' }).foo.helper
|
167
|
-
end
|
168
|
-
|
169
|
-
test 'a key :_include includes the given modules (using defaults)' do
|
170
|
-
klass = Class.new(Hashr) do
|
171
|
-
define :foo => { :_include => Module.new { def helper; 'helper'; end } }
|
172
|
-
end
|
173
|
-
assert_equal 'helper', klass.new.foo.helper
|
174
|
-
end
|
175
|
-
|
176
|
-
test 'a key :_access includes an anonymous module with accessors' do
|
177
|
-
klass = Class.new(Hashr) do
|
178
|
-
define :foo => { :_access => [:key] }
|
179
|
-
end
|
180
|
-
|
181
|
-
assert_equal 'key', klass.new(:foo => { :key => 'key' }).foo.key
|
182
|
-
end
|
183
|
-
|
184
|
-
test 'defining defaults always also makes sure an accessor is used' do
|
185
|
-
klass = Class.new(Hashr) do
|
186
|
-
define :foo => { :default => 'default' }
|
187
|
-
end
|
188
|
-
|
189
|
-
assert_equal 'default', klass.new().foo.default
|
190
|
-
end
|
191
|
-
|
192
|
-
test 'all: allows to define :_include modules which will be included into all nested hashes' do
|
193
|
-
klass = Class.new(Hashr) do
|
194
|
-
default :_include => Module.new { def helper; 'helper'; end }
|
195
|
-
end
|
196
|
-
assert_equal 'helper', klass.new.helper
|
197
|
-
assert_equal 'helper', klass.new(:foo => { :bar => {} }).foo.bar.helper
|
198
|
-
end
|
199
|
-
|
200
|
-
test 'all: allows to define :_access which will include an anonymous module with accessors into all nested hashes' do
|
201
|
-
klass = Class.new(Hashr) do
|
202
|
-
default :_access => :key
|
203
|
-
end
|
204
|
-
assert_equal 'key', klass.new(:key => 'key').key
|
205
|
-
assert_equal 'key', klass.new(:foo => { :bar => { :key => 'key' } }).foo.bar.key
|
206
|
-
end
|
207
|
-
|
208
|
-
test 'anonymously overwriting core Hash methods' do
|
209
|
-
hashr = Hashr.new(:count => 5) do
|
210
|
-
def count
|
211
|
-
self[:count]
|
212
|
-
end
|
213
|
-
end
|
214
|
-
assert_equal 5, hashr.count
|
215
|
-
end
|
216
|
-
|
217
|
-
test 'to_hash converts the Hashr instance and all of its children to Hashes' do
|
218
|
-
hash = Hashr.new(:foo => { :bar => { :baz => 'baz' } }).to_hash
|
219
|
-
|
220
|
-
assert hash.instance_of?(Hash)
|
221
|
-
assert hash[:foo].instance_of?(Hash)
|
222
|
-
assert hash[:foo][:bar].instance_of?(Hash)
|
223
|
-
end
|
224
|
-
|
225
|
-
test 'set sets a dot separated path to nested hashes' do
|
226
|
-
hashr = Hashr.new(:foo => { :bar => 'bar' })
|
227
|
-
hashr.set('foo.baz', 'baz')
|
228
|
-
assert_equal 'baz', hashr.foo.baz
|
229
|
-
end
|
230
|
-
end
|
data/test/test_helper.rb
DELETED