objectmancy 1.0.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 +7 -0
- data/lib/objectmancy/attribute_options.rb +18 -0
- data/lib/objectmancy/errors.rb +21 -0
- data/lib/objectmancy/objectable.rb +177 -0
- data/lib/objectmancy/types.rb +14 -0
- data/lib/objectmancy/version.rb +4 -0
- data/lib/objectmancy.rb +6 -0
- metadata +106 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d58d07ca21141f331f6008c56d07660bc1e9187d
|
4
|
+
data.tar.gz: a5856401d36e4899530e11964b5f748fce6cc93a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: db17c08f46d637f97c1f4730ef9480a99864ebfe2c92432558fc297a4007ad760a4da6041083c2dbdcae16bbee705c574c5b80210b63a13e3737cfd2c9c279ab
|
7
|
+
data.tar.gz: 7c9691474e80119c1c0a3cc94e7fdbb28c2e12e2194b40e10c3cc3ee7ffb4afda0e6abb51e4ddbc976884c14e1c124eae2f26f2c993cb35a811e2402f658700f
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Objectmancy
|
2
|
+
# @private
|
3
|
+
# Class to contain the options for attribute definitions. Makes accessing them
|
4
|
+
# more Object-Oriented. This could be written with Objectable, but that felt
|
5
|
+
# a little too on-the-nose.
|
6
|
+
class AttributeOptions
|
7
|
+
# @!attribute [r] type
|
8
|
+
# @!attribute [r] objectable
|
9
|
+
# @!attribute [r] multiple
|
10
|
+
attr_reader :type, :objectable, :multiple
|
11
|
+
|
12
|
+
def initialize(**options)
|
13
|
+
@type = options[:type]
|
14
|
+
@objectable = options[:objectable]
|
15
|
+
@multiple = options[:multiple]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Objectmancy
|
2
|
+
# Error for defining attributes with the same name
|
3
|
+
class AttributeAlreadyDefinedError < StandardError
|
4
|
+
attr_reader :attribute
|
5
|
+
|
6
|
+
# Creates a new AttributeAlreadyDefinedError
|
7
|
+
#
|
8
|
+
# @param attribute [#to_s] name of the attribute in error
|
9
|
+
def initialize(attribute)
|
10
|
+
@attribute = attribute
|
11
|
+
end
|
12
|
+
|
13
|
+
# Message for logging
|
14
|
+
#
|
15
|
+
# @return [String] message containing the attribute name
|
16
|
+
# explaining the error.
|
17
|
+
def message
|
18
|
+
"#{attribute} has already been defined."
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
require 'objectmancy/errors'
|
2
|
+
require 'objectmancy/types'
|
3
|
+
require 'objectmancy/attribute_options'
|
4
|
+
|
5
|
+
require 'pry'
|
6
|
+
|
7
|
+
module Objectmancy
|
8
|
+
# Mixin for allowing your objects to take a Hash and turn them into an object.
|
9
|
+
# By default, the values will be whatever the value of the Hash at that key
|
10
|
+
# is.
|
11
|
+
# @example Including Objectable
|
12
|
+
# class Kitten
|
13
|
+
# include Objectmancy::Objectable
|
14
|
+
#
|
15
|
+
# attribute :name
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# tabby = Kitten.new(name: 'Eddy')
|
19
|
+
# tabby.name # => "Eddy"
|
20
|
+
#
|
21
|
+
#
|
22
|
+
module Objectable
|
23
|
+
# @private
|
24
|
+
def self.included(base)
|
25
|
+
base.extend(ClassMethods)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Class methods mixed into anything including Objectable. These are where
|
29
|
+
# the real power of Objectmancy comes from.
|
30
|
+
module ClassMethods
|
31
|
+
# @private
|
32
|
+
# Basic setup of the class-level state.
|
33
|
+
def self.extended(base)
|
34
|
+
base.instance_variable_set(:@registered_attributes, {})
|
35
|
+
base.class.send(:attr_reader, :registered_attributes)
|
36
|
+
base.send(:private_class_method, :attribute, :multiples)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Defines an attribute usable by Objectmancy to create your object.
|
40
|
+
# Only attributes defined with this method will be converted to attributes
|
41
|
+
# on the final object.
|
42
|
+
#
|
43
|
+
# @param name [#to_sym] Attribute name
|
44
|
+
# @param opts [Hash] Options to be applied
|
45
|
+
# @option opts [Symbol, Class] :type The type of object to create. Can be
|
46
|
+
# either a Symbol of one of: :datetime; else, a Class
|
47
|
+
# @option opts [Symbol, String] :objectable Method to call on :type. Will
|
48
|
+
# default to calling :new. Ignored if :type is one of the known types.
|
49
|
+
# Requires :type option.
|
50
|
+
# @raise [AttributeAlreadyDefinedError] Attempt to define two attributes
|
51
|
+
# of the same name
|
52
|
+
# @raise [ArgumentError] Pass in :objectable without :type
|
53
|
+
def attribute(name, **opts)
|
54
|
+
symbolized_name = name.to_sym
|
55
|
+
|
56
|
+
if registered_attributes.key? symbolized_name
|
57
|
+
raise AttributeAlreadyDefinedError, name
|
58
|
+
end
|
59
|
+
|
60
|
+
if opts[:objectable] && opts[:type].nil?
|
61
|
+
raise ArgumentError, ':objectable option reuqires :type option'
|
62
|
+
end
|
63
|
+
|
64
|
+
registered_attributes[symbolized_name] = AttributeOptions.new(opts)
|
65
|
+
|
66
|
+
attr_accessor symbolized_name
|
67
|
+
end
|
68
|
+
|
69
|
+
# Allows the definition of Arrays of items to be turns into objects. Bear
|
70
|
+
# in mind that Arrays of basic objects (Strings, numbers, anything else
|
71
|
+
# that doesn't need special initialization) are handled by .attribute.
|
72
|
+
#
|
73
|
+
# @param name [#to_sym] Attribute name
|
74
|
+
# @param opts [Hash] Options to be applied
|
75
|
+
# @option opts [Symbol, Class] :type The type of object to create. Can be
|
76
|
+
# either a Symbol of one of: :datetime; else, a Class
|
77
|
+
# @option opts [Symbol, String] :objectable Method to call on :type. Will
|
78
|
+
# default to calling :new. Ignored if :type is one of the known types.
|
79
|
+
# @raise [AttributeAlreadyDefinedError] Attempt to define two attributes
|
80
|
+
# of the same name
|
81
|
+
# @raise [ArgumentError] Calling .multiples without :type option
|
82
|
+
def multiples(name, **opts)
|
83
|
+
if opts[:type].nil?
|
84
|
+
raise ArgumentError, 'Multiples require the :type option'
|
85
|
+
end
|
86
|
+
|
87
|
+
attribute(name, opts.merge(multiple: true))
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Creates your object. You should use the after_initialize
|
92
|
+
#
|
93
|
+
# @param attrs [Hash] Hash of attributes to create the object with
|
94
|
+
def initialize(attrs = {})
|
95
|
+
before_initialize
|
96
|
+
|
97
|
+
_assignable_attributes(attrs).each do |attr, value|
|
98
|
+
options = self.class.registered_attributes[attr.to_sym]
|
99
|
+
|
100
|
+
value =
|
101
|
+
if options.multiple
|
102
|
+
_convert_multiples(value, options.type, options.objectable)
|
103
|
+
else
|
104
|
+
_single_value(value, options)
|
105
|
+
end
|
106
|
+
|
107
|
+
send("#{attr}=", value)
|
108
|
+
end
|
109
|
+
|
110
|
+
after_initialize
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
# Empty before_initialize callback
|
116
|
+
def before_initialize; end
|
117
|
+
|
118
|
+
# Empty after_initialize callback
|
119
|
+
def after_initialize; end
|
120
|
+
|
121
|
+
# Determines which attributes are assignable
|
122
|
+
#
|
123
|
+
# @param attrs [Hash] Provided base hash
|
124
|
+
# @return [Hash] Allowed attributes
|
125
|
+
def _assignable_attributes(attrs)
|
126
|
+
attrs.select { |k, _| self.class.registered_attributes.include? k.to_sym }
|
127
|
+
end
|
128
|
+
|
129
|
+
# Assigns a multiples attribute
|
130
|
+
#
|
131
|
+
# @param multiples [Array] Array of values to be converted
|
132
|
+
# @param type [Symbol, Class] Type of object to create
|
133
|
+
# @param creator [#to_s, nil] Method used to create the object
|
134
|
+
# @return [Array] Array of newly created objects
|
135
|
+
def _convert_multiples(multiples, type, creator)
|
136
|
+
creation_klass, creation_method = _creation_method(type, creator)
|
137
|
+
|
138
|
+
multiples.map { |m| creation_klass.send(creation_method, m) }
|
139
|
+
end
|
140
|
+
|
141
|
+
# Evaluates what needs to be done for a singular value.
|
142
|
+
#
|
143
|
+
# @param value [Object] Value to be assigned/evaluated.
|
144
|
+
# @param options [AttributeOptions] Options pertaining to the attribute
|
145
|
+
# @return [Object] Value for the new object
|
146
|
+
def _single_value(value, options)
|
147
|
+
return value unless options.type
|
148
|
+
|
149
|
+
_convert_value(value, options.type, options.objectable)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Handles object creation of arbitrary types
|
153
|
+
#
|
154
|
+
# @param old_value [Object] Value to be converted
|
155
|
+
# @param type [Symbol, Class] Type of object to create
|
156
|
+
# @param creator [#to_s, nil] Method used to create the object
|
157
|
+
# @return [Object] The newly created object
|
158
|
+
def _convert_value(old_value, type, creator)
|
159
|
+
creation_klass, creation_method = _creation_method(type, creator)
|
160
|
+
|
161
|
+
creation_klass.send(creation_method, old_value)
|
162
|
+
end
|
163
|
+
|
164
|
+
# Determines the method of creation for a custom object
|
165
|
+
#
|
166
|
+
# @param type [Symbol, Class] Type of object to create
|
167
|
+
# @param creator [#to_s, nil] Method used to create the object
|
168
|
+
# @return [Array] Class to create, method to call.
|
169
|
+
def _creation_method(type, creator)
|
170
|
+
if (known = Types::SPECIAL_TYPES[type])
|
171
|
+
[known[:klass], known[:objectable]]
|
172
|
+
else
|
173
|
+
[type, (creator || :new)]
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'iso8601'
|
2
|
+
|
3
|
+
module Objectmancy
|
4
|
+
# Namespace for storing type logic around manipulating different data types.
|
5
|
+
module Types
|
6
|
+
# DateTime special type specification
|
7
|
+
DATETIME = { klass: ISO8601::DateTime, objectable: :new }.freeze
|
8
|
+
|
9
|
+
# Known types with special behavior defined.
|
10
|
+
SPECIAL_TYPES = {
|
11
|
+
datetime: DATETIME
|
12
|
+
}.freeze
|
13
|
+
end
|
14
|
+
end
|
data/lib/objectmancy.rb
ADDED
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: objectmancy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jon Anderson
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-10-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: iso8601
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.9'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.9'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rubocop
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.43'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.43'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '5.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '5.0'
|
69
|
+
description: Set of extensions used to convert a hash to an object.
|
70
|
+
email:
|
71
|
+
- jon1992@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- lib/objectmancy.rb
|
77
|
+
- lib/objectmancy/attribute_options.rb
|
78
|
+
- lib/objectmancy/errors.rb
|
79
|
+
- lib/objectmancy/objectable.rb
|
80
|
+
- lib/objectmancy/types.rb
|
81
|
+
- lib/objectmancy/version.rb
|
82
|
+
homepage: https://github.com/jon2992/objectmancy
|
83
|
+
licenses:
|
84
|
+
- MIT
|
85
|
+
metadata: {}
|
86
|
+
post_install_message:
|
87
|
+
rdoc_options: []
|
88
|
+
require_paths:
|
89
|
+
- lib
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
requirements: []
|
101
|
+
rubyforge_project:
|
102
|
+
rubygems_version: 2.5.1
|
103
|
+
signing_key:
|
104
|
+
specification_version: 4
|
105
|
+
summary: Set of extensions used to convert a hash to an object.
|
106
|
+
test_files: []
|