hashr 1.0.0 → 2.0.0.rc1
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 +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