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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 363367721272caa3b82603de2d83346a509a4a5f
4
- data.tar.gz: 5a3b97d5394ae7c46e6f926e8e72cd5f37b3856b
3
+ metadata.gz: 865d5c744eb989eb6faa51b252a52b83ac31fd49
4
+ data.tar.gz: a69e87efba10818c1af7036e02d75327210dbc3b
5
5
  SHA512:
6
- metadata.gz: 8a89f6e9a054164233456eabe2a4e14ce9ef42f4d6c80a2ebb3c42e185412ab39ba56dda24079293593238c089762444583cdfaaaaa955c5a33442abc7991eda
7
- data.tar.gz: 609ed7d5b46af9cf45c52783efd0f97420280eb42dfb2b9c204805e9dd4a5036163244c1aebab428065e5a5d12b2f711491d45b0af7ae9d4dc8621933239b7fd
6
+ metadata.gz: 1e2285af349146a905c6bcc53f5bdacc9d644f2ca48e05e69d700e18082d2285dd1b49e16670808a470e72e6ced4795ceb614d6d4eeded4310e61c9f1ffb559e
7
+ data.tar.gz: 993a3e986b50d2c9c34bd2ce153f2a25c10d0530962cca561880361f9080d83cdbb87f83de34b3dc11029ed88a5cf479d9de259396dc7d09b089f00d9820a336
data/Gemfile CHANGED
@@ -1,2 +1,7 @@
1
1
  source 'https://rubygems.org'
2
+
2
3
  gemspec
4
+
5
+ group :test do
6
+ gem 'rspec'
7
+ end
data/README.md CHANGED
@@ -2,145 +2,107 @@
2
2
 
3
3
  # Hashr
4
4
 
5
- Hashr is a very simple and tiny class derived from Ruby's core Hash class which makes using nested hashes for configuration (and other purposes) easier and less repetive and error prone.
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
- * easy inclusion of modules into nested hashes
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
- config = Hashr.new('foo' => { 'bar' => 'bar' })
19
+ ```ruby
20
+ config = Hashr.new(foo: { bar: 'bar' })
20
21
 
21
- config.foo? # => true
22
- config.foo # => { :bar => 'bar' }
22
+ config.foo? # => true
23
+ config.foo # => { bar: 'bar' }
23
24
 
24
- config.foo.bar? # => true
25
- config.foo.bar # => 'bar'
25
+ config.foo.bar? # => true
26
+ config.foo.bar # => 'bar'
26
27
 
27
- config.foo.bar = 'bar!'
28
- config.foo.bar # => 'bar!'
28
+ config.foo.bar = 'bar'
29
+ config.foo.bar # => 'bar'
29
30
 
30
- config.foo.baz = 'baz'
31
- config.foo.baz # => 'baz'
31
+ config.foo.baz = 'baz'
32
+ config.foo.baz # => 'baz'
33
+ ```
32
34
 
33
- Be aware that by default missing keys won't raise an exception but instead behave like Hash access:
35
+ Hash core methods are not available but assume you mean to look up keys with
36
+ the same name:
34
37
 
35
- config = Hashr.new
36
- config.foo? # => false
37
- config.foo # => nil
38
+ ```ruby
39
+ config = Hashr.new(count: 1, key: 'key')
40
+ config.count # => 1
41
+ config.key # => 'key'
42
+ ```
38
43
 
39
- You can make Hashr raise an `IndexError` though like this:
44
+ In order to check a hash stored on a certain key you can convert it to a Ruby
45
+ Hash:
40
46
 
41
- Hashr.raise_missing_keys = true
42
- config = Hashr.new
43
- config.foo? # => false
44
- config.foo # => raises an IndexError "Key :foo is not defined."
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
- You can also anonymously overwrite core Hash methods like this:
53
+ Missing keys won't raise an exception but instead behave like Hash access:
47
54
 
48
- config = Hashr.new(:count => 3) do
49
- def count
50
- self[:count]
51
- end
52
- end
53
- config.count # => 3
55
+ ```ruby
56
+ config = Hashr.new
57
+ config.foo? # => false
58
+ config.foo # => nil
59
+ ```
54
60
 
55
- And you can anonymously provide defaults like this:
61
+ ## Defaults
56
62
 
57
- data = { :foo => 'foo' }
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
- But you can obvioulsy also derive a custom class to define defaults and overwrite core Hash methods like this:
65
+ ```ruby
66
+ class Config < Hashr
67
+ default boxes: { memory: '1024' }
68
+ end
64
69
 
65
- class Config < Hashr
66
- define :foo => { :bar => 'bar' }
70
+ config = Config.new
71
+ config.boxes.memory # => 1024
72
+ ```
67
73
 
68
- def count
69
- self[:count]
70
- end
71
- end
74
+ Or passed to the instance:
72
75
 
73
- config = Config.new
74
- config.foo.bar # => 'bar'
76
+ ```ruby
77
+ data = {}
78
+ defaults = { boxes: { memory: '1024' } }
75
79
 
76
- Include modules to nested hashes like this:
77
-
78
- class Config < Hashr
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 defaults from environment variables:
86
+ Hashr includes a simple module that makes it easy to overwrite configuration
87
+ defaults from environment variables:
120
88
 
121
- class Config < Hashr
122
- extend Hashr::EnvDefaults
89
+ ```ruby
90
+ class Config < Hashr
91
+ extend Hashr::Env
123
92
 
124
- self.env_namespace = 'foo'
93
+ self.env_namespace = 'foo'
125
94
 
126
- define :boxes => { :memory => '1024' }
127
- end
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
- ENV['FOO_BOXES_MEMORY'] = '2048'
132
- config = Config.new
133
- config.boxes.memory # => '2048'
134
-
135
- ## Running the tests
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
 
@@ -1,132 +1,108 @@
1
1
  require 'hashr/core_ext/ruby/hash'
2
2
 
3
- class Hashr < Hash
4
- autoload :EnvDefaults, 'hashr/env_defaults'
5
-
6
- TEMPLATE = new
3
+ class Hashr < BasicObject
4
+ require 'hashr/delegate/conditional'
5
+ require 'hashr/env'
7
6
 
8
7
  class << self
9
- attr_accessor :raise_missing_keys
8
+ attr_reader :defaults
10
9
 
11
- def define(definition)
12
- @definition = deep_accessorize(definition.deep_symbolize_keys)
10
+ def inherited(other)
11
+ other.default(defaults)
13
12
  end
14
13
 
15
- def definition
16
- @definition ||= {}
14
+ def new(*args)
15
+ super(self, *args)
17
16
  end
18
17
 
19
18
  def default(defaults)
20
- @defaults = deep_accessorize(defaults)
19
+ @defaults = (self.defaults || {}).deep_merge(defaults || {})
21
20
  end
21
+ alias :define :default
22
22
 
23
- def defaults
24
- @defaults ||= {}
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
- def deep_accessorize(hash)
28
- hash.each do |key, value|
29
- next unless value.is_a?(Hash)
30
- value[:_access] ||= []
31
- value[:_access] = Array(value[:_access])
32
- value.keys.each { |key| value[:_access] << key if value.respond_to?(key) }
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
- undef :id if method_defined?(:id) # undefine deprecated method #id on 1.8.x
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, default = nil)
48
- store(key.to_sym, Hashr.new(default)) if default && !key?(key)
49
- super(key.to_sym)
46
+ def [](key)
47
+ @data[to_key(key)]
50
48
  end
51
49
 
52
50
  def []=(key, value)
53
- super(key.to_sym, value.is_a?(Hash) ? self.class.new(value, {}) : value)
51
+ @data.store(to_key(key), value.is_a?(::Hash) ? ::Hashr.new(value, {}) : value)
54
52
  end
55
53
 
56
- def set(path, value, stack = [])
57
- tokens = path.to_s.split('.')
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?(method)
62
- if self.class.raise_missing_keys
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].to_sym]
65
+ !!self[name.to_s[0..-2]]
73
66
  when '='
74
- self[name.to_s[0..-2].to_sym] = args.first
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 include_modules(modules)
82
- Array(modules).each { |mod| meta_class.send(:include, mod) } if modules
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 include_accessors(accessors)
86
- Array(accessors).each { |accessor| meta_class.send(:define_method, accessor) { self[accessor] } } if accessors
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 meta_class
90
- class << self; self end
84
+ def ==(other)
85
+ to_h == other.to_h if other.respond_to?(:to_h)
91
86
  end
92
87
 
93
- def to_hash
94
- inject({}) do |hash, (key, value)|
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
- protected
101
-
102
- def deep_hashrize(hash)
103
- hash.inject(TEMPLATE.dup) do |result, (key, value)|
104
- case key.to_sym
105
- when :_include
106
- result.include_modules(value)
107
- when :_access
108
- result.include_accessors(value)
109
- else
110
- result.store(key.to_sym, value.is_a?(Hash) ? deep_hashrize(value) : value)
111
- end
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 deep_defaultize(hash)
117
- self.class.defaults.each do |key, value|
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 Hash.method_defined?(:deep_merge)
23
+ end unless instance_methods.include?(:deep_merge)
38
24
  end
@@ -0,0 +1,13 @@
1
+ class Hashr
2
+ module Delegate
3
+ module Conditional
4
+ def method_missing(name, *args, &block)
5
+ if (args.any? || block) && @data.respond_to?(name)
6
+ @data.send(name, *args, &block)
7
+ else
8
+ super
9
+ end
10
+ end
11
+ end
12
+ end
13
+ 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
@@ -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
@@ -1,3 +1,3 @@
1
- class Hashr < Hash
2
- VERSION = '1.0.0'
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: 1.0.0
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-04 00:00:00.000000000 Z
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@artweb-design.de
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/env_defaults.rb
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: '0'
45
+ version: 1.3.1
92
46
  requirements: []
93
47
  rubyforge_project: "[none]"
94
48
  rubygems_version: 2.4.5
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -1,4 +0,0 @@
1
- require 'bundler/setup'
2
- require 'minitest/autorun'
3
- require 'test_declarative'
4
- require 'hashr'