acts_as_hashable 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 96c41090ceb3c7fa641c3d120194f1f553aec8e3
4
+ data.tar.gz: ae8c03743c9ecbef87c3944dbcfefa9f056819e0
5
+ SHA512:
6
+ metadata.gz: 12f6625c97ebc77a3a9c07659836f33a72cc24908733af3c0a46624e0e5ac6094aca062a06e1be296d8da94f315bc7dfb119e86a5efeb145770b85b0c93de521
7
+ data.tar.gz: 06cbfecd4d49b5bf94f450d539e7b4aacd3cd81f700ce9fb9ac7680c9cd6c27611181b0a9893dfea907c20d6f84456acf92ec3934c56f5f33e41f95438e6b840
data/.editorconfig ADDED
@@ -0,0 +1,8 @@
1
+ # See http://editorconfig.org/
2
+
3
+ [*]
4
+ trim_trailing_whitespace = true
5
+ indent_style = space
6
+ indent_size = 2
7
+ insert_final_newline = true
8
+ end_of_line = lf
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ .DS_Store
2
+ *.gem
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.3.7
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.1
4
+ - 2.3.7
5
+ cache: bundler
6
+ script:
7
+ - bundle exec rspec
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,32 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ acts_as_hashable (1.0.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.3)
10
+ rspec (3.8.0)
11
+ rspec-core (~> 3.8.0)
12
+ rspec-expectations (~> 3.8.0)
13
+ rspec-mocks (~> 3.8.0)
14
+ rspec-core (3.8.0)
15
+ rspec-support (~> 3.8.0)
16
+ rspec-expectations (3.8.1)
17
+ diff-lcs (>= 1.2.0, < 2.0)
18
+ rspec-support (~> 3.8.0)
19
+ rspec-mocks (3.8.0)
20
+ diff-lcs (>= 1.2.0, < 2.0)
21
+ rspec-support (~> 3.8.0)
22
+ rspec-support (3.8.0)
23
+
24
+ PLATFORMS
25
+ ruby
26
+
27
+ DEPENDENCIES
28
+ acts_as_hashable!
29
+ rspec
30
+
31
+ BUNDLED WITH
32
+ 1.16.3
data/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2018 Blue Marble Payroll, LLC
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,150 @@
1
+ # Acts as Hashable
2
+
3
+ [![Build Status](https://travis-ci.org/bluemarblepayroll/acts_as_hashable.svg?branch=master)](https://travis-ci.org/bluemarblepayroll/acts_as_hashable)
4
+
5
+ This is a small library that helps increase the pliability of object constructor signatures.
6
+ Instead of instantiating via the constructor, this library can install helper factory
7
+ class-level methods that you can use with hashes:
8
+
9
+ * make(): create a single instance of a class
10
+ * array(): create an array of instances of a class
11
+
12
+ One caveat to this library is that it is not meant to be a complete modeling solution but rather
13
+ just a tool that helps model a domain or complete a modeling framework.
14
+ The main missing element to this library is that you still have to manage and implement constructors.
15
+ The two class-level factory methods are just meant to create a syntactic hash-like interface.
16
+
17
+ ## Installation
18
+
19
+ To install through Rubygems:
20
+
21
+ ````
22
+ gem install install acts_as_hashable
23
+ ````
24
+
25
+ You can also add this to your Gemfile:
26
+
27
+ ````
28
+ bundle add acts_as_hashable
29
+ ````
30
+
31
+ ## Examples
32
+
33
+ Consider the following example:
34
+
35
+ ````
36
+ class Person
37
+ acts_as_hashable
38
+
39
+ attr_reader :name, :age
40
+
41
+ def initialize(name:, age:)
42
+ @name = name
43
+ @age = age
44
+ end
45
+ end
46
+
47
+ class HeadOfHousehold
48
+ acts_as_hashable
49
+
50
+ attr_reader :person, :partner
51
+
52
+ def initialize(person:, partner: nil)
53
+ @person = Person.make(person)
54
+ @partner = Person.make(partner)
55
+ end
56
+ end
57
+
58
+ class Family
59
+ acts_as_hashable
60
+
61
+ attr_reader :head_of_household, :children
62
+
63
+ def initialize(head_of_household:, children: [])
64
+ @head_of_household = HeadOfHousehold.make(head_of_household)
65
+ @children = Person.array(children)
66
+ end
67
+ end
68
+ ````
69
+
70
+ Simply placing:
71
+
72
+ ````
73
+ acts_as_hashable
74
+ ````
75
+
76
+ on a class means it will have the two main factory methods available for you to use. Then,
77
+ you can leverage these for instantiation:
78
+
79
+ ````
80
+ family = {
81
+ head_of_household: {
82
+ person: {
83
+ name: 'Matt',
84
+ age: 109,
85
+ },
86
+ partner: {
87
+ name: 'Katie',
88
+ age: 110,
89
+ }
90
+ },
91
+ children: [
92
+ { name: 'Martin', age: 29 },
93
+ { name: 'Short', age: 99 },
94
+ ]
95
+ }
96
+
97
+ family_obj = Family.make(family)
98
+ ````
99
+
100
+ The family_obj will then be a fully hydrated Family instance. The family instance
101
+ attributes will also be fully hydrated since its constructor also uses acts_as_hashable.
102
+
103
+ Note that this works nicely with either keyword arguments or a hash-based argument constructor like so:
104
+
105
+ ````
106
+ class Toy
107
+ acts_as_hashable
108
+
109
+ attr_reader :squishy
110
+
111
+ def initialize(opts = {})
112
+ @squishy = opts[:squishy] || false
113
+ end
114
+ end
115
+
116
+ class Pet
117
+ acts_as_hashable
118
+
119
+ attr_reader :name, :toy
120
+
121
+ def initialize(opts = {})
122
+ @name = opts[:name]
123
+ @toy = Toy.make(opts[:toy])
124
+ end
125
+ end
126
+ ````
127
+
128
+ ## Contributing
129
+
130
+ ### Development Environment Configuration
131
+
132
+ Basic steps to take to get this repository compiling:
133
+
134
+ 1. Install [Ruby](https://www.ruby-lang.org/en/documentation/installation/) (check acts_as_hashable.gemspec for versions supported)
135
+ 2. Install bundler (gem install bundler)
136
+ 3. Clone the repository (git clone git@github.com:bluemarblepayroll/acts_as_hashable.git)
137
+ 4. Navigate to the root folder (cd acts_as_hashable)
138
+ 5. Install dependencies (bundle)
139
+
140
+ ### Running Tests
141
+
142
+ To execute the test suite run:
143
+
144
+ ````
145
+ rspec spec --format documentation
146
+ ````
147
+
148
+ ## License
149
+
150
+ This project is MIT Licensed.
@@ -0,0 +1,25 @@
1
+ require "./lib/acts_as_hashable/version"
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'acts_as_hashable'
5
+ s.version = ActsAsHashable::VERSION
6
+ s.summary = "Simple hash-based factory methods for objects."
7
+
8
+ s.description = <<-EOS
9
+ This is a small library that helps increase the pliability of object constructor signatures.
10
+ Instead of instantiating via the constructor, this library can install helper factory
11
+ class-level methods that you can use with hashes.
12
+ EOS
13
+
14
+ s.authors = [ 'Matthew Ruggio' ]
15
+ s.email = [ 'mruggio@bluemarblepayroll.com' ]
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.homepage = 'https://github.com/bluemarblepayroll/acts_as_hashable'
20
+ s.license = 'MIT'
21
+
22
+ s.required_ruby_version = '>= 2.3.1'
23
+
24
+ s.add_development_dependency('rspec')
25
+ end
@@ -0,0 +1,21 @@
1
+ #
2
+ # Copyright (c) 2018-present, Blue Marble Payroll, LLC
3
+ #
4
+ # This source code is licensed under the MIT license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+ #
7
+
8
+ require_relative 'utilities'
9
+ require_relative 'hashable'
10
+
11
+ module ActsAsHashable
12
+ module DslHook
13
+ def acts_as_hashable
14
+ extend ::ActsAsHashable::Hashable
15
+ end
16
+ end
17
+ end
18
+
19
+ Object.class_eval do
20
+ extend ::ActsAsHashable::DslHook
21
+ end
@@ -0,0 +1,36 @@
1
+ #
2
+ # Copyright (c) 2018-present, Blue Marble Payroll, LLC
3
+ #
4
+ # This source code is licensed under the MIT license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+ #
7
+
8
+ module ActsAsHashable
9
+ module Hashable
10
+
11
+ def array(object, nullable: true)
12
+ if object.is_a?(Hash)
13
+ objects = [ object ]
14
+ else
15
+ objects = Array(object)
16
+ end
17
+
18
+ objects.select { |o| !!o }.map { |o| make(o, nullable: nullable) }
19
+ end
20
+
21
+ def make(object, nullable: true)
22
+ if object.is_a?(Hash)
23
+ self.new(**::ActsAsHashable::Utilities.symbolize_keys(object))
24
+ elsif object.is_a?(self)
25
+ object
26
+ elsif object.nil? && nullable
27
+ nil
28
+ elsif object.nil? && !nullable
29
+ self.new
30
+ else
31
+ raise "Cannot create hashable object with class name: #{object.class.name}"
32
+ end
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,32 @@
1
+ #
2
+ # Copyright (c) 2018-present, Blue Marble Payroll, LLC
3
+ #
4
+ # This source code is licensed under the MIT license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+ #
7
+
8
+ module ActsAsHashable
9
+ class Utilities
10
+ class << self
11
+
12
+ # https://apidock.com/rails/Hash/symbolize_keys
13
+ def symbolize_keys(hash)
14
+ transform_keys(hash) { |key| key.to_sym rescue key }
15
+ end
16
+
17
+ # https://apidock.com/rails/v4.2.7/Hash/transform_keys
18
+ def transform_keys(hash)
19
+ return enum_for(:transform_keys) unless block_given?
20
+
21
+ result = {}
22
+
23
+ hash.keys.each do |key|
24
+ result[yield(key)] = hash[key]
25
+ end
26
+
27
+ result
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,10 @@
1
+ #
2
+ # Copyright (c) 2018-present, Blue Marble Payroll, LLC
3
+ #
4
+ # This source code is licensed under the MIT license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+ #
7
+
8
+ module ActsAsHashable
9
+ VERSION = "1.0.0"
10
+ end
@@ -0,0 +1,222 @@
1
+ #
2
+ # Copyright (c) 2018-present, Blue Marble Payroll, LLC
3
+ #
4
+ # This source code is licensed under the MIT license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+ #
7
+
8
+ require './lib/acts_as_hashable/acts_as_hashable'
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
+ describe ActsAsHashable do
65
+ context '#make' do
66
+
67
+ context 'with a hash constructor interface' do
68
+
69
+ it 'should properly instantiate objects from a symbol-keyed hash' do
70
+
71
+ pet = {
72
+ name: 'Doug the dog',
73
+ toy: { squishy: true }
74
+ }
75
+
76
+ pet_obj = Pet.make(pet)
77
+
78
+ expect(pet_obj.name).to eq('Doug the dog')
79
+ expect(pet_obj.toy.squishy).to be true
80
+ end
81
+
82
+ end
83
+
84
+ context 'with keyword arguments interface' do
85
+
86
+ it 'should properly instantiate objects from symbol-keyed hash' do
87
+ head_of_household = {
88
+ person: {
89
+ name: 'Matt',
90
+ age: 109,
91
+ },
92
+ partner: {
93
+ name: 'Katie',
94
+ age: 110,
95
+ }
96
+ }
97
+
98
+ head_of_household_obj = HeadOfHousehold.make(head_of_household)
99
+
100
+ expect(head_of_household_obj.person.name).to eq('Matt')
101
+ expect(head_of_household_obj.person.age).to eq(109)
102
+ expect(head_of_household_obj.partner.name).to eq('Katie')
103
+ expect(head_of_household_obj.partner.age).to eq(110)
104
+ end
105
+
106
+ it 'should properly instantiate objects from symbol-keyed hash' do
107
+ head_of_household = {
108
+ 'person' => {
109
+ 'name' => 'Matt',
110
+ 'age' => 109,
111
+ },
112
+ 'partner' => {
113
+ 'name' => 'Katie',
114
+ 'age' => 110,
115
+ }
116
+ }
117
+
118
+ head_of_household_obj = HeadOfHousehold.make(head_of_household)
119
+
120
+ expect(head_of_household_obj.person.name).to eq('Matt')
121
+ expect(head_of_household_obj.person.age).to eq(109)
122
+ expect(head_of_household_obj.partner.name).to eq('Katie')
123
+ expect(head_of_household_obj.partner.age).to eq(110)
124
+ end
125
+
126
+ it 'should raise an ArgumentError for missing required keyword' do
127
+ head_of_household = {
128
+ person: {
129
+ # name: 'Matt',
130
+ age: 109,
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
+
141
+ it 'should raise an ArgumentError for unknown required keyword' do
142
+ head_of_household = {
143
+ person: {
144
+ name: 'Matt',
145
+ age: 109,
146
+ height_in_inches: 700,
147
+ },
148
+ partner: {
149
+ name: 'Katie',
150
+ age: 110,
151
+ }
152
+ }
153
+
154
+ expect { HeadOfHousehold.make(head_of_household) }.to raise_error(ArgumentError)
155
+ end
156
+
157
+ end
158
+
159
+ context '#array' do
160
+
161
+ it 'should properly instantiate objects and arrays of objects from symbol-keyed hash and arrays' do
162
+ family = {
163
+ head_of_household: {
164
+ person: {
165
+ name: 'Matt',
166
+ age: 109,
167
+ },
168
+ partner: {
169
+ name: 'Katie',
170
+ age: 110,
171
+ }
172
+ },
173
+ children: [
174
+ { name: 'Martin', age: 29 },
175
+ { name: 'Short', age: 99 },
176
+ ]
177
+ }
178
+
179
+ family_obj = Family.make(family)
180
+
181
+ expect(family_obj.head_of_household.person.name).to eq('Matt')
182
+ expect(family_obj.head_of_household.person.age).to eq(109)
183
+ expect(family_obj.head_of_household.partner.name).to eq('Katie')
184
+ expect(family_obj.head_of_household.partner.age).to eq(110)
185
+
186
+ expect(family_obj.children.length).to eq(2)
187
+ expect(family_obj.children[0].name).to eq('Martin')
188
+ expect(family_obj.children[0].age).to eq(29)
189
+ expect(family_obj.children[1].name).to eq('Short')
190
+ expect(family_obj.children[1].age).to eq(99)
191
+ end
192
+
193
+ it 'should properly instantiate arrays of objects when only a single hash is passed in' do
194
+ family = {
195
+ head_of_household: {
196
+ person: {
197
+ name: 'Matt',
198
+ age: 109,
199
+ },
200
+ partner: {
201
+ name: 'Katie',
202
+ age: 110,
203
+ }
204
+ },
205
+ children: { name: 'Martin', age: 29 }
206
+ }
207
+
208
+ family_obj = Family.make(family)
209
+
210
+ expect(family_obj.head_of_household.person.name).to eq('Matt')
211
+ expect(family_obj.head_of_household.person.age).to eq(109)
212
+ expect(family_obj.head_of_household.partner.name).to eq('Katie')
213
+ expect(family_obj.head_of_household.partner.age).to eq(110)
214
+
215
+ expect(family_obj.children.length).to eq(1)
216
+ expect(family_obj.children[0].name).to eq('Martin')
217
+ expect(family_obj.children[0].age).to eq(29)
218
+ end
219
+
220
+ end
221
+ end
222
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: acts_as_hashable
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Matthew Ruggio
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-09-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
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
+ description: |2
28
+ This is a small library that helps increase the pliability of object constructor signatures.
29
+ Instead of instantiating via the constructor, this library can install helper factory
30
+ class-level methods that you can use with hashes.
31
+ email:
32
+ - mruggio@bluemarblepayroll.com
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - ".editorconfig"
38
+ - ".gitignore"
39
+ - ".ruby-version"
40
+ - ".travis.yml"
41
+ - Gemfile
42
+ - Gemfile.lock
43
+ - LICENSE
44
+ - README.md
45
+ - acts_as_hashable.gemspec
46
+ - lib/acts_as_hashable/acts_as_hashable.rb
47
+ - lib/acts_as_hashable/hashable.rb
48
+ - lib/acts_as_hashable/utilities.rb
49
+ - lib/acts_as_hashable/version.rb
50
+ - spec/acts_as_hashable_spec.rb
51
+ homepage: https://github.com/bluemarblepayroll/acts_as_hashable
52
+ licenses:
53
+ - MIT
54
+ metadata: {}
55
+ post_install_message:
56
+ rdoc_options: []
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 2.3.1
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ requirements: []
70
+ rubyforge_project:
71
+ rubygems_version: 2.5.2.3
72
+ signing_key:
73
+ specification_version: 4
74
+ summary: Simple hash-based factory methods for objects.
75
+ test_files:
76
+ - spec/acts_as_hashable_spec.rb