acts_as_hashable 1.1.0 → 1.3.0
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/.gitignore +1 -0
- data/.rubocop.yml +3 -1
- data/.ruby-version +1 -1
- data/.travis.yml +8 -4
- data/CHANGELOG.md +12 -0
- data/README.md +16 -1
- data/Rakefile +3 -1
- data/acts_as_hashable.gemspec +14 -5
- data/exe/.gitkeep +0 -0
- data/lib/acts_as_hashable.rb +1 -0
- data/lib/acts_as_hashable/constant_resolver.rb +34 -0
- data/lib/acts_as_hashable/factory.rb +15 -8
- data/lib/acts_as_hashable/hash_refinements.rb +1 -1
- data/lib/acts_as_hashable/hashable.rb +20 -4
- data/lib/acts_as_hashable/type_factory.rb +26 -6
- data/lib/acts_as_hashable/version.rb +1 -1
- metadata +31 -20
- data/Gemfile.lock +0 -110
- data/spec/acts_as_hashable_spec.rb +0 -203
- data/spec/examples.rb +0 -80
- data/spec/factory_spec.rb +0 -49
- data/spec/spec_helper.rb +0 -24
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 512a071f89b95030d2e3669230a444aeb2c84bfa3eba507e922364f92d00d484
|
|
4
|
+
data.tar.gz: a6a796fbbcbae31bcd528bf1300e1ef74c61b3db1a104898320569a68c48e378
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3cdcb125699f45bf9165bf92008eb42eede26e983d4ed82335116c72640b8a1356a1202b366c294e75966312d9b02e1bd0703fbf9dfea74318c459b78a73efe6
|
|
7
|
+
data.tar.gz: b7b0e7304fec6cf66c4a034a0575244b06ee3c7f5381274df3fbbe5bcc33b12982d67f6ad9271f8ab196acf77bd37e42a90c6f068038e9890eafd6997d5b22bc
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
|
@@ -3,9 +3,11 @@ Metrics/LineLength:
|
|
|
3
3
|
|
|
4
4
|
Metrics/BlockLength:
|
|
5
5
|
ExcludedMethods: ['it', 'describe', 'context', 'let', 'specify']
|
|
6
|
+
Exclude:
|
|
7
|
+
- acts_as_hashable.gemspec
|
|
6
8
|
|
|
7
9
|
AllCops:
|
|
8
|
-
TargetRubyVersion: 2.
|
|
10
|
+
TargetRubyVersion: 2.5
|
|
9
11
|
|
|
10
12
|
Style/RescueModifier:
|
|
11
13
|
Enabled: false
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.6.
|
|
1
|
+
2.6.6
|
data/.travis.yml
CHANGED
|
@@ -4,10 +4,9 @@ env:
|
|
|
4
4
|
language: ruby
|
|
5
5
|
rvm:
|
|
6
6
|
# Build on the latest stable of all supported Rubies (https://www.ruby-lang.org/en/downloads/):
|
|
7
|
-
- 2.
|
|
8
|
-
- 2.
|
|
9
|
-
- 2.
|
|
10
|
-
- 2.6.0
|
|
7
|
+
- 2.5.8
|
|
8
|
+
- 2.6.6
|
|
9
|
+
- 2.7.1
|
|
11
10
|
cache: bundler
|
|
12
11
|
before_script:
|
|
13
12
|
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
|
@@ -18,3 +17,8 @@ script:
|
|
|
18
17
|
- bundle exec rspec
|
|
19
18
|
after_script:
|
|
20
19
|
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
|
20
|
+
addons:
|
|
21
|
+
# https://docs.travis-ci.com/user/uploading-artifacts/
|
|
22
|
+
artifacts:
|
|
23
|
+
paths:
|
|
24
|
+
- Gemfile.lock
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
# 1.3.0 (September 5th, 2020)
|
|
2
|
+
|
|
3
|
+
Additions:
|
|
4
|
+
|
|
5
|
+
* Added dynamic class constantization when a string is registered for a Factory.
|
|
6
|
+
|
|
7
|
+
# 1.2.0 (June 9th, 2020)
|
|
8
|
+
|
|
9
|
+
* Bumped minimum Ruby version to >= 2.5
|
|
10
|
+
* Do not pass in any constructor arguments unless we have at least one key.
|
|
11
|
+
* Add more detail to construction errors.
|
|
12
|
+
|
|
1
13
|
# 1.1.0 (May 3rd, 2019)
|
|
2
14
|
|
|
3
15
|
* Added acts_as_hashable_factory to dynamically create objects.
|
data/README.md
CHANGED
|
@@ -19,7 +19,7 @@ The two class-level factory methods are just meant to create a syntactic hash-li
|
|
|
19
19
|
To install through Rubygems:
|
|
20
20
|
|
|
21
21
|
````
|
|
22
|
-
gem install
|
|
22
|
+
gem install acts_as_hashable
|
|
23
23
|
````
|
|
24
24
|
|
|
25
25
|
You can also add this to your Gemfile:
|
|
@@ -197,6 +197,21 @@ end
|
|
|
197
197
|
|
|
198
198
|
In case you need full control of the registry you can also choose to simply override the class-level `registry` method which will simply return a hash of keys (names) and values (class constants).
|
|
199
199
|
|
|
200
|
+
### Resolving Constants at Runtime
|
|
201
|
+
|
|
202
|
+
Factories can also be resolved using Ruby's Object#const_get and Object#const_missing. Simply register a string representing the class in order to use these mechanics, such as:
|
|
203
|
+
|
|
204
|
+
```ruby
|
|
205
|
+
class ExampleFactory
|
|
206
|
+
acts_as_hashable_factory
|
|
207
|
+
|
|
208
|
+
type_key 'object_type'
|
|
209
|
+
|
|
210
|
+
register 'Pet', 'Pet'
|
|
211
|
+
|
|
212
|
+
register 'HeadOfHousehold', ->(_key) { HeadOfHousehold }
|
|
213
|
+
end
|
|
214
|
+
|
|
200
215
|
## Contributing
|
|
201
216
|
|
|
202
217
|
### Development Environment Configuration
|
data/Rakefile
CHANGED
data/acts_as_hashable.gemspec
CHANGED
|
@@ -14,19 +14,28 @@ Gem::Specification.new do |s|
|
|
|
14
14
|
DESCRIPTION
|
|
15
15
|
|
|
16
16
|
s.authors = ['Matthew Ruggio']
|
|
17
|
+
s.bindir = 'exe'
|
|
17
18
|
s.email = ['mruggio@bluemarblepayroll.com']
|
|
18
|
-
s.
|
|
19
|
-
s.
|
|
20
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
|
19
|
+
s.executables = []
|
|
20
|
+
s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
21
21
|
s.homepage = 'https://github.com/bluemarblepayroll/acts_as_hashable'
|
|
22
22
|
s.license = 'MIT'
|
|
23
|
+
s.metadata = {
|
|
24
|
+
'bug_tracker_uri' => 'https://github.com/bluemarblepayroll/acts_as_hashable/issues',
|
|
25
|
+
'changelog_uri' => 'https://github.com/bluemarblepayroll/acts_as_hashable/blob/master/CHANGELOG.md',
|
|
26
|
+
'documentation_uri' => 'https://www.rubydoc.info/gems/acts_as_hashable',
|
|
27
|
+
'homepage_uri' => s.homepage,
|
|
28
|
+
'source_code_uri' => s.homepage
|
|
29
|
+
}
|
|
23
30
|
|
|
24
|
-
s.required_ruby_version = '>= 2.
|
|
31
|
+
s.required_ruby_version = '>= 2.5'
|
|
32
|
+
|
|
33
|
+
s.add_dependency('caution', '~>1')
|
|
25
34
|
|
|
26
35
|
s.add_development_dependency('guard-rspec', '~>4.7')
|
|
27
36
|
s.add_development_dependency('pry')
|
|
28
37
|
s.add_development_dependency('pry-byebug', '~> 3')
|
|
29
|
-
s.add_development_dependency
|
|
38
|
+
s.add_development_dependency('rake', '~> 13.0')
|
|
30
39
|
s.add_development_dependency('rspec')
|
|
31
40
|
s.add_development_dependency('rubocop', '~> 0.63.1')
|
|
32
41
|
s.add_development_dependency('simplecov', '~>0.16.1')
|
data/exe/.gitkeep
ADDED
|
File without changes
|
data/lib/acts_as_hashable.rb
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
|
5
|
+
#
|
|
6
|
+
# This source code is licensed under the MIT license found in the
|
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
|
8
|
+
#
|
|
9
|
+
|
|
10
|
+
module ActsAsHashable
|
|
11
|
+
# This class is responsible for turning strings and symbols into constants.
|
|
12
|
+
# It does not deal with inflection, simply just constant resolution.
|
|
13
|
+
class ConstantResolver # :nodoc: all
|
|
14
|
+
# Only use Module constant resolution if a string or symbol was passed in.
|
|
15
|
+
# Any other type is defined as an acceptable constant and is simply returned.
|
|
16
|
+
def constantize(value)
|
|
17
|
+
value.is_a?(String) || value.is_a?(Symbol) ? object_constant(value) : value
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
# If the constant has been loaded, we can safely use it through const_get.
|
|
23
|
+
# If the constant has not been loaded, we need to defer to const_missing to resolve it.
|
|
24
|
+
# If we blindly call const_get, it may return false positives for namespaced constants
|
|
25
|
+
# or anything nested.
|
|
26
|
+
def object_constant(value)
|
|
27
|
+
if Object.const_defined?(value, false)
|
|
28
|
+
Object.const_get(value, false)
|
|
29
|
+
else
|
|
30
|
+
Object.const_missing(value)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -19,34 +19,41 @@ module ActsAsHashable
|
|
|
19
19
|
# }
|
|
20
20
|
# end
|
|
21
21
|
# You can also use the 'register' DSL:
|
|
22
|
-
#
|
|
22
|
+
# register 'some_class_name', SomeClassName
|
|
23
|
+
# register 'some_class_name', '', SomeClassName
|
|
23
24
|
# or:
|
|
24
|
-
#
|
|
25
|
+
# register 'some_class_name', ->(_key) { SomeClassName }
|
|
25
26
|
module Factory
|
|
26
27
|
extend Forwardable
|
|
27
28
|
|
|
28
29
|
def_delegators :factory, :array, :make
|
|
29
30
|
|
|
30
|
-
def register(
|
|
31
|
-
|
|
31
|
+
def register(*args)
|
|
32
|
+
raise ArgumentError, "missing at least one key and value: #{args}" if args.length < 2
|
|
33
|
+
|
|
34
|
+
value = args.last
|
|
35
|
+
|
|
36
|
+
args[0..-2].each do |key|
|
|
37
|
+
registry[key] = value
|
|
38
|
+
end
|
|
32
39
|
end
|
|
33
40
|
|
|
34
|
-
def registry
|
|
41
|
+
def registry # :nodoc:
|
|
35
42
|
@registry ||= {}
|
|
36
43
|
end
|
|
37
44
|
|
|
38
|
-
def materialize_registry
|
|
45
|
+
def materialize_registry # :nodoc:
|
|
39
46
|
@registry.map do |k, v|
|
|
40
47
|
value = v.is_a?(Proc) ? v.call(k) : v
|
|
41
48
|
[k, value]
|
|
42
49
|
end.to_h
|
|
43
50
|
end
|
|
44
51
|
|
|
45
|
-
def type_key(key)
|
|
52
|
+
def type_key(key) # :nodoc:
|
|
46
53
|
@typed_with = key
|
|
47
54
|
end
|
|
48
55
|
|
|
49
|
-
def typed_with
|
|
56
|
+
def typed_with # :nodoc:
|
|
50
57
|
@typed_with || TypeFactory::DEFAULT_TYPE_KEY
|
|
51
58
|
end
|
|
52
59
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
module ActsAsHashable
|
|
11
11
|
# Let's provide a refinenment instead of monkey-patching Hash. That way we can stop
|
|
12
12
|
# polluting other libraries and internalize our specific needs.
|
|
13
|
-
module HashRefinements
|
|
13
|
+
module HashRefinements # :nodoc: all
|
|
14
14
|
refine Hash do
|
|
15
15
|
def symbolize_keys
|
|
16
16
|
map { |k, v| [k.to_sym, v] }.to_h
|
|
@@ -13,11 +13,19 @@ module ActsAsHashable
|
|
|
13
13
|
module Hashable
|
|
14
14
|
using HashRefinements
|
|
15
15
|
|
|
16
|
+
class HydrationError < Caution::CauseInjectingError; end
|
|
17
|
+
|
|
16
18
|
HASHABLE_HYDRATORS = [
|
|
17
19
|
{
|
|
18
20
|
condition: ->(_context, object, _nullable) { object.is_a?(Hash) },
|
|
19
21
|
converter: lambda do |context, object, _nullable|
|
|
20
|
-
|
|
22
|
+
args = (object || {}).symbolize_keys
|
|
23
|
+
|
|
24
|
+
if args.keys.any?
|
|
25
|
+
context.new(**args)
|
|
26
|
+
else
|
|
27
|
+
context.new
|
|
28
|
+
end
|
|
21
29
|
end
|
|
22
30
|
},
|
|
23
31
|
{
|
|
@@ -45,12 +53,20 @@ module ActsAsHashable
|
|
|
45
53
|
|
|
46
54
|
def make(object, nullable: true)
|
|
47
55
|
HASHABLE_HYDRATORS.each do |hydrator|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
56
|
+
next unless hydrator[:condition].call(self, object, nullable)
|
|
57
|
+
|
|
58
|
+
return hydrate(hydrator, object, nullable)
|
|
51
59
|
end
|
|
52
60
|
|
|
53
61
|
raise ArgumentError, "Cannot create hashable object with class name: #{object.class.name}"
|
|
54
62
|
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def hydrate(hydrator, object, nullable)
|
|
67
|
+
hydrator[:converter].call(self, object, nullable)
|
|
68
|
+
rescue ArgumentError
|
|
69
|
+
raise HydrationError, "#{name} cannot be hydrated using arguments: #{object}"
|
|
70
|
+
end
|
|
55
71
|
end
|
|
56
72
|
end
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
# LICENSE file in the root directory of this source tree.
|
|
8
8
|
#
|
|
9
9
|
|
|
10
|
+
require_relative 'constant_resolver'
|
|
11
|
+
|
|
10
12
|
module ActsAsHashable
|
|
11
13
|
# A TypeFactory object understands how to build objects using a special designated 'type' key.
|
|
12
14
|
class TypeFactory
|
|
@@ -17,8 +19,9 @@ module ActsAsHashable
|
|
|
17
19
|
attr_reader :registry, :type_key
|
|
18
20
|
|
|
19
21
|
def initialize(registry = {}, type_key = DEFAULT_TYPE_KEY)
|
|
20
|
-
@
|
|
21
|
-
@
|
|
22
|
+
@constant_resolver = ConstantResolver.new
|
|
23
|
+
@registry = registry.symbolize_keys
|
|
24
|
+
@type_key = type_key.to_s.to_sym
|
|
22
25
|
|
|
23
26
|
freeze
|
|
24
27
|
end
|
|
@@ -34,13 +37,30 @@ module ActsAsHashable
|
|
|
34
37
|
def make(config = {})
|
|
35
38
|
config = (config || {}).symbolize_keys
|
|
36
39
|
type = config[type_key].to_s.to_sym
|
|
37
|
-
object_class =
|
|
38
|
-
|
|
39
|
-
raise ArgumentError, "cannot find section from type: #{type}" unless object_class
|
|
40
|
+
object_class = resolve_object_class(type)
|
|
40
41
|
|
|
41
42
|
config_without_type = config.reject { |k| k == type_key }
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
# We want to defer to the classes proper maker if it exists.
|
|
45
|
+
# Technically, this factory should only make classes that include Hashable, but just to be
|
|
46
|
+
# sure we do not break any existing compatibility, lets make it work for both.
|
|
47
|
+
method_name = object_class.respond_to?(:make) ? :make : :new
|
|
48
|
+
|
|
49
|
+
object_class.send(method_name, config_without_type)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
attr_reader :constant_resolver
|
|
55
|
+
|
|
56
|
+
def resolve_object_class(type)
|
|
57
|
+
object_class = registry[type]
|
|
58
|
+
|
|
59
|
+
raise ArgumentError, "cannot find registration for: '#{type}'" unless object_class
|
|
60
|
+
|
|
61
|
+
return object_class unless object_class.is_a?(String)
|
|
62
|
+
|
|
63
|
+
constant_resolver.constantize(object_class)
|
|
44
64
|
end
|
|
45
65
|
end
|
|
46
66
|
end
|
metadata
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: acts_as_hashable
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Matthew Ruggio
|
|
8
8
|
autorequire:
|
|
9
|
-
bindir:
|
|
9
|
+
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2020-09-05 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: caution
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1'
|
|
13
27
|
- !ruby/object:Gem::Dependency
|
|
14
28
|
name: guard-rspec
|
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -58,14 +72,14 @@ dependencies:
|
|
|
58
72
|
requirements:
|
|
59
73
|
- - "~>"
|
|
60
74
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '
|
|
75
|
+
version: '13.0'
|
|
62
76
|
type: :development
|
|
63
77
|
prerelease: false
|
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
79
|
requirements:
|
|
66
80
|
- - "~>"
|
|
67
81
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '
|
|
82
|
+
version: '13.0'
|
|
69
83
|
- !ruby/object:Gem::Dependency
|
|
70
84
|
name: rspec
|
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -128,8 +142,7 @@ description: |2
|
|
|
128
142
|
class-level methods that you can use with hashes.
|
|
129
143
|
email:
|
|
130
144
|
- mruggio@bluemarblepayroll.com
|
|
131
|
-
executables:
|
|
132
|
-
- console
|
|
145
|
+
executables: []
|
|
133
146
|
extensions: []
|
|
134
147
|
extra_rdoc_files: []
|
|
135
148
|
files:
|
|
@@ -141,27 +154,29 @@ files:
|
|
|
141
154
|
- CHANGELOG.md
|
|
142
155
|
- CODE_OF_CONDUCT.md
|
|
143
156
|
- Gemfile
|
|
144
|
-
- Gemfile.lock
|
|
145
157
|
- Guardfile
|
|
146
158
|
- LICENSE
|
|
147
159
|
- README.md
|
|
148
160
|
- Rakefile
|
|
149
161
|
- acts_as_hashable.gemspec
|
|
150
162
|
- bin/console
|
|
163
|
+
- exe/.gitkeep
|
|
151
164
|
- lib/acts_as_hashable.rb
|
|
165
|
+
- lib/acts_as_hashable/constant_resolver.rb
|
|
152
166
|
- lib/acts_as_hashable/factory.rb
|
|
153
167
|
- lib/acts_as_hashable/hash_refinements.rb
|
|
154
168
|
- lib/acts_as_hashable/hashable.rb
|
|
155
169
|
- lib/acts_as_hashable/type_factory.rb
|
|
156
170
|
- lib/acts_as_hashable/version.rb
|
|
157
|
-
- spec/acts_as_hashable_spec.rb
|
|
158
|
-
- spec/examples.rb
|
|
159
|
-
- spec/factory_spec.rb
|
|
160
|
-
- spec/spec_helper.rb
|
|
161
171
|
homepage: https://github.com/bluemarblepayroll/acts_as_hashable
|
|
162
172
|
licenses:
|
|
163
173
|
- MIT
|
|
164
|
-
metadata:
|
|
174
|
+
metadata:
|
|
175
|
+
bug_tracker_uri: https://github.com/bluemarblepayroll/acts_as_hashable/issues
|
|
176
|
+
changelog_uri: https://github.com/bluemarblepayroll/acts_as_hashable/blob/master/CHANGELOG.md
|
|
177
|
+
documentation_uri: https://www.rubydoc.info/gems/acts_as_hashable
|
|
178
|
+
homepage_uri: https://github.com/bluemarblepayroll/acts_as_hashable
|
|
179
|
+
source_code_uri: https://github.com/bluemarblepayroll/acts_as_hashable
|
|
165
180
|
post_install_message:
|
|
166
181
|
rdoc_options: []
|
|
167
182
|
require_paths:
|
|
@@ -170,19 +185,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
170
185
|
requirements:
|
|
171
186
|
- - ">="
|
|
172
187
|
- !ruby/object:Gem::Version
|
|
173
|
-
version: 2.
|
|
188
|
+
version: '2.5'
|
|
174
189
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
175
190
|
requirements:
|
|
176
191
|
- - ">="
|
|
177
192
|
- !ruby/object:Gem::Version
|
|
178
193
|
version: '0'
|
|
179
194
|
requirements: []
|
|
180
|
-
rubygems_version: 3.0.
|
|
195
|
+
rubygems_version: 3.0.3
|
|
181
196
|
signing_key:
|
|
182
197
|
specification_version: 4
|
|
183
198
|
summary: Simple hash-based factory methods for objects.
|
|
184
|
-
test_files:
|
|
185
|
-
- spec/acts_as_hashable_spec.rb
|
|
186
|
-
- spec/examples.rb
|
|
187
|
-
- spec/factory_spec.rb
|
|
188
|
-
- spec/spec_helper.rb
|
|
199
|
+
test_files: []
|
data/Gemfile.lock
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
PATH
|
|
2
|
-
remote: .
|
|
3
|
-
specs:
|
|
4
|
-
acts_as_hashable (1.1.0)
|
|
5
|
-
|
|
6
|
-
GEM
|
|
7
|
-
remote: https://rubygems.org/
|
|
8
|
-
specs:
|
|
9
|
-
ansi (1.5.0)
|
|
10
|
-
ast (2.4.0)
|
|
11
|
-
byebug (11.0.1)
|
|
12
|
-
coderay (1.1.2)
|
|
13
|
-
diff-lcs (1.3)
|
|
14
|
-
docile (1.3.1)
|
|
15
|
-
ffi (1.10.0)
|
|
16
|
-
formatador (0.2.5)
|
|
17
|
-
guard (2.15.0)
|
|
18
|
-
formatador (>= 0.2.4)
|
|
19
|
-
listen (>= 2.7, < 4.0)
|
|
20
|
-
lumberjack (>= 1.0.12, < 2.0)
|
|
21
|
-
nenv (~> 0.1)
|
|
22
|
-
notiffany (~> 0.0)
|
|
23
|
-
pry (>= 0.9.12)
|
|
24
|
-
shellany (~> 0.0)
|
|
25
|
-
thor (>= 0.18.1)
|
|
26
|
-
guard-compat (1.2.1)
|
|
27
|
-
guard-rspec (4.7.3)
|
|
28
|
-
guard (~> 2.1)
|
|
29
|
-
guard-compat (~> 1.1)
|
|
30
|
-
rspec (>= 2.99.0, < 4.0)
|
|
31
|
-
hirb (0.7.3)
|
|
32
|
-
jaro_winkler (1.5.2)
|
|
33
|
-
json (2.1.0)
|
|
34
|
-
listen (3.1.5)
|
|
35
|
-
rb-fsevent (~> 0.9, >= 0.9.4)
|
|
36
|
-
rb-inotify (~> 0.9, >= 0.9.7)
|
|
37
|
-
ruby_dep (~> 1.2)
|
|
38
|
-
lumberjack (1.0.13)
|
|
39
|
-
method_source (0.9.2)
|
|
40
|
-
nenv (0.3.0)
|
|
41
|
-
notiffany (0.1.1)
|
|
42
|
-
nenv (~> 0.1)
|
|
43
|
-
shellany (~> 0.0)
|
|
44
|
-
parallel (1.13.0)
|
|
45
|
-
parser (2.6.0.0)
|
|
46
|
-
ast (~> 2.4.0)
|
|
47
|
-
powerpack (0.1.2)
|
|
48
|
-
pry (0.12.2)
|
|
49
|
-
coderay (~> 1.1.0)
|
|
50
|
-
method_source (~> 0.9.0)
|
|
51
|
-
pry-byebug (3.7.0)
|
|
52
|
-
byebug (~> 11.0)
|
|
53
|
-
pry (~> 0.10)
|
|
54
|
-
rainbow (3.0.0)
|
|
55
|
-
rake (10.5.0)
|
|
56
|
-
rb-fsevent (0.10.3)
|
|
57
|
-
rb-inotify (0.10.0)
|
|
58
|
-
ffi (~> 1.0)
|
|
59
|
-
rspec (3.8.0)
|
|
60
|
-
rspec-core (~> 3.8.0)
|
|
61
|
-
rspec-expectations (~> 3.8.0)
|
|
62
|
-
rspec-mocks (~> 3.8.0)
|
|
63
|
-
rspec-core (3.8.0)
|
|
64
|
-
rspec-support (~> 3.8.0)
|
|
65
|
-
rspec-expectations (3.8.1)
|
|
66
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
|
67
|
-
rspec-support (~> 3.8.0)
|
|
68
|
-
rspec-mocks (3.8.0)
|
|
69
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
|
70
|
-
rspec-support (~> 3.8.0)
|
|
71
|
-
rspec-support (3.8.0)
|
|
72
|
-
rubocop (0.63.1)
|
|
73
|
-
jaro_winkler (~> 1.5.1)
|
|
74
|
-
parallel (~> 1.10)
|
|
75
|
-
parser (>= 2.5, != 2.5.1.1)
|
|
76
|
-
powerpack (~> 0.1)
|
|
77
|
-
rainbow (>= 2.2.2, < 4.0)
|
|
78
|
-
ruby-progressbar (~> 1.7)
|
|
79
|
-
unicode-display_width (~> 1.4.0)
|
|
80
|
-
ruby-progressbar (1.10.0)
|
|
81
|
-
ruby_dep (1.5.0)
|
|
82
|
-
shellany (0.0.1)
|
|
83
|
-
simplecov (0.16.1)
|
|
84
|
-
docile (~> 1.1)
|
|
85
|
-
json (>= 1.8, < 3)
|
|
86
|
-
simplecov-html (~> 0.10.0)
|
|
87
|
-
simplecov-console (0.4.2)
|
|
88
|
-
ansi
|
|
89
|
-
hirb
|
|
90
|
-
simplecov
|
|
91
|
-
simplecov-html (0.10.2)
|
|
92
|
-
thor (0.20.3)
|
|
93
|
-
unicode-display_width (1.4.1)
|
|
94
|
-
|
|
95
|
-
PLATFORMS
|
|
96
|
-
ruby
|
|
97
|
-
|
|
98
|
-
DEPENDENCIES
|
|
99
|
-
acts_as_hashable!
|
|
100
|
-
guard-rspec (~> 4.7)
|
|
101
|
-
pry
|
|
102
|
-
pry-byebug (~> 3)
|
|
103
|
-
rake (~> 10.0)
|
|
104
|
-
rspec
|
|
105
|
-
rubocop (~> 0.63.1)
|
|
106
|
-
simplecov (~> 0.16.1)
|
|
107
|
-
simplecov-console (~> 0.4.2)
|
|
108
|
-
|
|
109
|
-
BUNDLED WITH
|
|
110
|
-
1.17.3
|
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#
|
|
4
|
-
# Copyright (c) 2018-present, Blue Marble Payroll, LLC
|
|
5
|
-
#
|
|
6
|
-
# This source code is licensed under the MIT license found in the
|
|
7
|
-
# LICENSE file in the root directory of this source tree.
|
|
8
|
-
#
|
|
9
|
-
|
|
10
|
-
require './spec/spec_helper'
|
|
11
|
-
|
|
12
|
-
describe ActsAsHashable do
|
|
13
|
-
context '#make' do
|
|
14
|
-
context 'when passing in unacceptable input' do
|
|
15
|
-
it 'should raise ArgumentError for number' do
|
|
16
|
-
expect { Pet.make(1) }.to raise_error(ArgumentError)
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
it 'should raise ArgumentError for string' do
|
|
20
|
-
expect { Pet.make('') }.to raise_error(ArgumentError)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
it 'should raise ArgumentError for true' do
|
|
24
|
-
expect { Pet.make(true) }.to raise_error(ArgumentError)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
it 'should raise ArgumentError for array' do
|
|
28
|
-
expect { Pet.make([]) }.to raise_error(ArgumentError)
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
context 'with a hash constructor interface' do
|
|
33
|
-
it 'should properly return object if object is already the same type' do
|
|
34
|
-
original_pet_obj = Pet.new
|
|
35
|
-
pet_obj = Pet.make(original_pet_obj)
|
|
36
|
-
|
|
37
|
-
expect(pet_obj.hash).to eq(original_pet_obj.hash)
|
|
38
|
-
expect(pet_obj.name).to be nil
|
|
39
|
-
expect(pet_obj.toy).to be nil
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
it 'should properly return hydrated object if nil is passed in and is not nullable' do
|
|
43
|
-
pet_obj = Pet.make(nil, nullable: false)
|
|
44
|
-
|
|
45
|
-
expect(pet_obj.name).to be nil
|
|
46
|
-
expect(pet_obj.toy).to be nil
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
it 'should properly return nil if nil is passed in and is nullable' do
|
|
50
|
-
pet_obj = Pet.make(nil, nullable: false)
|
|
51
|
-
|
|
52
|
-
expect(pet_obj.name).to be nil
|
|
53
|
-
expect(pet_obj.toy).to be nil
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
it 'should properly instantiate objects from a symbol-keyed hash' do
|
|
57
|
-
pet = {
|
|
58
|
-
name: 'Doug the dog',
|
|
59
|
-
toy: { squishy: true }
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
pet_obj = Pet.make(pet)
|
|
63
|
-
|
|
64
|
-
expect(pet_obj.name).to eq('Doug the dog')
|
|
65
|
-
expect(pet_obj.toy.squishy).to be true
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
context 'with keyword arguments interface' do
|
|
70
|
-
it 'should properly instantiate objects from symbol-keyed hash' do
|
|
71
|
-
head_of_household = {
|
|
72
|
-
person: {
|
|
73
|
-
name: 'Matt',
|
|
74
|
-
age: 109
|
|
75
|
-
},
|
|
76
|
-
partner: {
|
|
77
|
-
name: 'Katie',
|
|
78
|
-
age: 110
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
head_of_household_obj = HeadOfHousehold.make(head_of_household)
|
|
83
|
-
|
|
84
|
-
expect(head_of_household_obj.person.name).to eq('Matt')
|
|
85
|
-
expect(head_of_household_obj.person.age).to eq(109)
|
|
86
|
-
expect(head_of_household_obj.partner.name).to eq('Katie')
|
|
87
|
-
expect(head_of_household_obj.partner.age).to eq(110)
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
it 'should properly instantiate objects from string-keyed hash' do
|
|
91
|
-
head_of_household = {
|
|
92
|
-
'person' => {
|
|
93
|
-
'name' => 'Matt',
|
|
94
|
-
'age' => 109
|
|
95
|
-
},
|
|
96
|
-
'partner' => {
|
|
97
|
-
'name' => 'Katie',
|
|
98
|
-
'age' => 110
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
head_of_household_obj = HeadOfHousehold.make(head_of_household)
|
|
103
|
-
|
|
104
|
-
expect(head_of_household_obj.person.name).to eq('Matt')
|
|
105
|
-
expect(head_of_household_obj.person.age).to eq(109)
|
|
106
|
-
expect(head_of_household_obj.partner.name).to eq('Katie')
|
|
107
|
-
expect(head_of_household_obj.partner.age).to eq(110)
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
it 'should raise an ArgumentError for missing required keyword' do
|
|
111
|
-
head_of_household = {
|
|
112
|
-
person: {
|
|
113
|
-
# name: 'Matt',
|
|
114
|
-
age: 109
|
|
115
|
-
},
|
|
116
|
-
partner: {
|
|
117
|
-
name: 'Katie',
|
|
118
|
-
age: 110
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
expect { HeadOfHousehold.make(head_of_household) }.to raise_error(ArgumentError)
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
it 'should raise an ArgumentError for unknown required keyword' do
|
|
126
|
-
head_of_household = {
|
|
127
|
-
person: {
|
|
128
|
-
name: 'Matt',
|
|
129
|
-
age: 109,
|
|
130
|
-
height_in_inches: 700
|
|
131
|
-
},
|
|
132
|
-
partner: {
|
|
133
|
-
name: 'Katie',
|
|
134
|
-
age: 110
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
expect { HeadOfHousehold.make(head_of_household) }.to raise_error(ArgumentError)
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
context '#array' do
|
|
143
|
-
it 'should properly create objects and arrays of objects from symbol-keyed hash and arrays' do
|
|
144
|
-
family = {
|
|
145
|
-
head_of_household: {
|
|
146
|
-
person: {
|
|
147
|
-
name: 'Matt',
|
|
148
|
-
age: 109
|
|
149
|
-
},
|
|
150
|
-
partner: {
|
|
151
|
-
name: 'Katie',
|
|
152
|
-
age: 110
|
|
153
|
-
}
|
|
154
|
-
},
|
|
155
|
-
children: [
|
|
156
|
-
{ name: 'Martin', age: 29 },
|
|
157
|
-
{ name: 'Short', age: 99 }
|
|
158
|
-
]
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
family_obj = Family.make(family)
|
|
162
|
-
|
|
163
|
-
expect(family_obj.head_of_household.person.name).to eq('Matt')
|
|
164
|
-
expect(family_obj.head_of_household.person.age).to eq(109)
|
|
165
|
-
expect(family_obj.head_of_household.partner.name).to eq('Katie')
|
|
166
|
-
expect(family_obj.head_of_household.partner.age).to eq(110)
|
|
167
|
-
|
|
168
|
-
expect(family_obj.children.length).to eq(2)
|
|
169
|
-
expect(family_obj.children[0].name).to eq('Martin')
|
|
170
|
-
expect(family_obj.children[0].age).to eq(29)
|
|
171
|
-
expect(family_obj.children[1].name).to eq('Short')
|
|
172
|
-
expect(family_obj.children[1].age).to eq(99)
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
it 'should properly instantiate arrays of objects when only a single hash is passed in' do
|
|
176
|
-
family = {
|
|
177
|
-
head_of_household: {
|
|
178
|
-
person: {
|
|
179
|
-
name: 'Matt',
|
|
180
|
-
age: 109
|
|
181
|
-
},
|
|
182
|
-
partner: {
|
|
183
|
-
name: 'Katie',
|
|
184
|
-
age: 110
|
|
185
|
-
}
|
|
186
|
-
},
|
|
187
|
-
children: { name: 'Martin', age: 29 }
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
family_obj = Family.make(family)
|
|
191
|
-
|
|
192
|
-
expect(family_obj.head_of_household.person.name).to eq('Matt')
|
|
193
|
-
expect(family_obj.head_of_household.person.age).to eq(109)
|
|
194
|
-
expect(family_obj.head_of_household.partner.name).to eq('Katie')
|
|
195
|
-
expect(family_obj.head_of_household.partner.age).to eq(110)
|
|
196
|
-
|
|
197
|
-
expect(family_obj.children.length).to eq(1)
|
|
198
|
-
expect(family_obj.children[0].name).to eq('Martin')
|
|
199
|
-
expect(family_obj.children[0].age).to eq(29)
|
|
200
|
-
end
|
|
201
|
-
end
|
|
202
|
-
end
|
|
203
|
-
end
|
data/spec/examples.rb
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#
|
|
4
|
-
# Copyright (c) 2018-present, Blue Marble Payroll, LLC
|
|
5
|
-
#
|
|
6
|
-
# This source code is licensed under the MIT license found in the
|
|
7
|
-
# LICENSE file in the root directory of this source tree.
|
|
8
|
-
#
|
|
9
|
-
|
|
10
|
-
class Toy
|
|
11
|
-
acts_as_hashable
|
|
12
|
-
|
|
13
|
-
attr_reader :squishy
|
|
14
|
-
|
|
15
|
-
def initialize(opts = {})
|
|
16
|
-
@squishy = opts[:squishy] || false
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
class Pet
|
|
21
|
-
acts_as_hashable
|
|
22
|
-
|
|
23
|
-
attr_reader :name, :toy
|
|
24
|
-
|
|
25
|
-
def initialize(opts = {})
|
|
26
|
-
@name = opts[:name]
|
|
27
|
-
@toy = Toy.make(opts[:toy])
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
class Person
|
|
32
|
-
acts_as_hashable
|
|
33
|
-
|
|
34
|
-
attr_reader :name, :age
|
|
35
|
-
|
|
36
|
-
def initialize(name:, age:)
|
|
37
|
-
@name = name
|
|
38
|
-
@age = age
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
class HeadOfHousehold
|
|
43
|
-
acts_as_hashable
|
|
44
|
-
|
|
45
|
-
attr_reader :person, :partner
|
|
46
|
-
|
|
47
|
-
def initialize(person:, partner: nil)
|
|
48
|
-
@person = Person.make(person)
|
|
49
|
-
@partner = Person.make(partner)
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
class Family
|
|
54
|
-
acts_as_hashable
|
|
55
|
-
|
|
56
|
-
attr_reader :head_of_household, :children
|
|
57
|
-
|
|
58
|
-
def initialize(head_of_household:, children: [])
|
|
59
|
-
@head_of_household = HeadOfHousehold.make(head_of_household)
|
|
60
|
-
@children = Person.array(children)
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
class ExampleFactory
|
|
65
|
-
acts_as_hashable_factory
|
|
66
|
-
|
|
67
|
-
type_key :object_type
|
|
68
|
-
|
|
69
|
-
register 'Person', Person
|
|
70
|
-
|
|
71
|
-
register 'Pet', Pet
|
|
72
|
-
|
|
73
|
-
register 'Toy', Toy
|
|
74
|
-
|
|
75
|
-
# These are examples of registering a proc instead of a class constant. It
|
|
76
|
-
# will send the key through the argument if you need
|
|
77
|
-
register 'Familiy', ->(_key) { Family }
|
|
78
|
-
|
|
79
|
-
register 'HeadOfHousehold', ->(_key) { HeadOfHousehold }
|
|
80
|
-
end
|
data/spec/factory_spec.rb
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#
|
|
4
|
-
# Copyright (c) 2018-present, Blue Marble Payroll, LLC
|
|
5
|
-
#
|
|
6
|
-
# This source code is licensed under the MIT license found in the
|
|
7
|
-
# LICENSE file in the root directory of this source tree.
|
|
8
|
-
#
|
|
9
|
-
|
|
10
|
-
require './spec/spec_helper'
|
|
11
|
-
|
|
12
|
-
describe ActsAsHashable::Factory do
|
|
13
|
-
subject { ExampleFactory }
|
|
14
|
-
|
|
15
|
-
it 'should hydrate example objects' do
|
|
16
|
-
objects = [
|
|
17
|
-
{
|
|
18
|
-
object_type: 'Pet',
|
|
19
|
-
name: 'Doug the dog',
|
|
20
|
-
toy: { squishy: true }
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
object_type: 'HeadOfHousehold',
|
|
24
|
-
person: {
|
|
25
|
-
name: 'Matt',
|
|
26
|
-
age: 109
|
|
27
|
-
},
|
|
28
|
-
partner: {
|
|
29
|
-
name: 'Katie',
|
|
30
|
-
age: 110
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
]
|
|
34
|
-
|
|
35
|
-
hydrated_objects = subject.array(objects)
|
|
36
|
-
|
|
37
|
-
pet_obj = hydrated_objects.first
|
|
38
|
-
|
|
39
|
-
expect(pet_obj.name).to eq('Doug the dog')
|
|
40
|
-
expect(pet_obj.toy.squishy).to be true
|
|
41
|
-
|
|
42
|
-
head_of_household_obj = hydrated_objects.last
|
|
43
|
-
|
|
44
|
-
expect(head_of_household_obj.person.name).to eq('Matt')
|
|
45
|
-
expect(head_of_household_obj.person.age).to eq(109)
|
|
46
|
-
expect(head_of_household_obj.partner.name).to eq('Katie')
|
|
47
|
-
expect(head_of_household_obj.partner.age).to eq(110)
|
|
48
|
-
end
|
|
49
|
-
end
|
data/spec/spec_helper.rb
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#
|
|
4
|
-
# Copyright (c) 2018-present, Blue Marble Payroll, LLC
|
|
5
|
-
#
|
|
6
|
-
# This source code is licensed under the MIT license found in the
|
|
7
|
-
# LICENSE file in the root directory of this source tree.
|
|
8
|
-
#
|
|
9
|
-
|
|
10
|
-
require 'pry'
|
|
11
|
-
require 'pry-byebug'
|
|
12
|
-
|
|
13
|
-
unless ENV['DISABLE_SIMPLECOV'] == 'true'
|
|
14
|
-
require 'simplecov'
|
|
15
|
-
require 'simplecov-console'
|
|
16
|
-
|
|
17
|
-
SimpleCov.formatter = SimpleCov::Formatter::Console
|
|
18
|
-
SimpleCov.start do
|
|
19
|
-
add_filter %r{\A/spec/}
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
require './lib/acts_as_hashable'
|
|
24
|
-
require './spec/examples'
|