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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f9fd301ccb4d740406de4faa658a77fece8ff4e492569882355227a35276bfd1
4
- data.tar.gz: d6e3e8c2bbd3a95c5d438da03188d84ee2941409b13c45e536df8a462b0a1195
3
+ metadata.gz: 512a071f89b95030d2e3669230a444aeb2c84bfa3eba507e922364f92d00d484
4
+ data.tar.gz: a6a796fbbcbae31bcd528bf1300e1ef74c61b3db1a104898320569a68c48e378
5
5
  SHA512:
6
- metadata.gz: e484913fe20a7b5d7fd4afde3778e92b5986d5fae952dfe3b7b32c51a38f1f9e12ca8533c18e6fab1a0d9fad2c191f361a7e38634f92fdeccbc8d3eb604b5e11
7
- data.tar.gz: bf7ba60f5e6435cf4094ed9199bf3115e58cdbaeb03b24e4ee0aa4778bb510b0b40f7d3f825de11d7852b10fe058d790fb8cb2d2990e2a432225c06c4691246c
6
+ metadata.gz: 3cdcb125699f45bf9165bf92008eb42eede26e983d4ed82335116c72640b8a1356a1202b366c294e75966312d9b02e1bd0703fbf9dfea74318c459b78a73efe6
7
+ data.tar.gz: b7b0e7304fec6cf66c4a034a0575244b06ee3c7f5381274df3fbbe5bcc33b12982d67f6ad9271f8ab196acf77bd37e42a90c6f068038e9890eafd6997d5b22bc
data/.gitignore CHANGED
@@ -3,3 +3,4 @@
3
3
  /tmp
4
4
  /coverage
5
5
  /pkg
6
+ Gemfile.lock
@@ -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.3
10
+ TargetRubyVersion: 2.5
9
11
 
10
12
  Style/RescueModifier:
11
13
  Enabled: false
@@ -1 +1 @@
1
- 2.6.0
1
+ 2.6.6
@@ -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.3.8
8
- - 2.4.5
9
- - 2.5.3
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
@@ -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 install acts_as_hashable
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
@@ -2,7 +2,9 @@
2
2
 
3
3
  require 'bundler/gem_tasks'
4
4
  require 'rspec/core/rake_task'
5
+ require 'rubocop/rake_task'
5
6
 
6
7
  RSpec::Core::RakeTask.new(:spec)
8
+ RuboCop::RakeTask.new
7
9
 
8
- task default: :spec
10
+ task default: %i[rubocop spec]
@@ -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.files = `git ls-files`.split("\n")
19
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
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.3.8'
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 'rake', '~> 10.0'
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')
File without changes
@@ -7,6 +7,7 @@
7
7
  # LICENSE file in the root directory of this source tree.
8
8
  #
9
9
 
10
+ require 'caution'
10
11
  require 'forwardable'
11
12
 
12
13
  require_relative 'acts_as_hashable/factory'
@@ -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
- # register 'some_class_name', SomeClassName
22
+ # register 'some_class_name', SomeClassName
23
+ # register 'some_class_name', '', SomeClassName
23
24
  # or:
24
- # register 'some_class_name', ->(_key) { SomeClassName }
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(key, value)
31
- registry[key] = value
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
- context.new(**(object || {}).symbolize_keys)
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
- if hydrator[:condition].call(self, object, nullable)
49
- return hydrator[:converter].call(self, object, nullable)
50
- end
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
- @registry = registry.symbolize_keys
21
- @type_key = type_key.to_s.to_sym
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 = registry[type]
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
- object_class.new(config_without_type)
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
@@ -8,5 +8,5 @@
8
8
  #
9
9
 
10
10
  module ActsAsHashable
11
- VERSION = '1.1.0'
11
+ VERSION = '1.3.0'
12
12
  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.1.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Ruggio
8
8
  autorequire:
9
- bindir: bin
9
+ bindir: exe
10
10
  cert_chain: []
11
- date: 2019-05-03 00:00:00.000000000 Z
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: '10.0'
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: '10.0'
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.3.8
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.1
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: []
@@ -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
@@ -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
@@ -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
@@ -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'