objectmancy 1.0.0 → 2.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 +4 -4
- data/lib/objectmancy/attribute_options.rb +11 -1
- data/lib/objectmancy/common_class_methods.rb +69 -0
- data/lib/objectmancy/hashable.rb +83 -0
- data/lib/objectmancy/objectable.rb +41 -84
- data/lib/objectmancy/types.rb +5 -1
- data/lib/objectmancy/version.rb +1 -1
- data/lib/objectmancy.rb +5 -0
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 62c3afc8c03ca9e9c0de91c4935d1476aa51eb18
|
4
|
+
data.tar.gz: 2a1834ac88ef320a58f8c15e6e36618a9bb2bcac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 008e5e952f44195e7ea092f7419805887b7e6803f2b97454c7b80bba24b3a0665fcf2724079123c1d5a3dc4fcfed2889068593eaeaccfd0af548fdb893e94e41
|
7
|
+
data.tar.gz: 60c2a0195efd52ad84ca2c507235e6378a1c8588e60ea0f59529db319de588f1304563a98a49fe666eec783bf5411c6428fabf68226826362d4c50750475b997
|
@@ -7,12 +7,22 @@ module Objectmancy
|
|
7
7
|
# @!attribute [r] type
|
8
8
|
# @!attribute [r] objectable
|
9
9
|
# @!attribute [r] multiple
|
10
|
-
|
10
|
+
# @!attribute [r] hashable
|
11
|
+
attr_reader :type, :objectable, :multiple, :hashable
|
11
12
|
|
12
13
|
def initialize(**options)
|
13
14
|
@type = options[:type]
|
14
15
|
@objectable = options[:objectable]
|
15
16
|
@multiple = options[:multiple]
|
17
|
+
@hashable = options[:hashable]
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns a new copy of of the given object without a value for the multiple
|
21
|
+
# attribute.
|
22
|
+
#
|
23
|
+
# @return [AttributeOptions]
|
24
|
+
def force_singular
|
25
|
+
AttributeOptions.new(type: type, hashable: hashable)
|
16
26
|
end
|
17
27
|
end
|
18
28
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'objectmancy/errors'
|
2
|
+
require 'objectmancy/attribute_options'
|
3
|
+
require 'objectmancy/hashable'
|
4
|
+
|
5
|
+
module Objectmancy
|
6
|
+
# Class methods mixed into anything including one of the mixins for
|
7
|
+
# Objectmancy
|
8
|
+
module CommonClassMethods
|
9
|
+
# @private
|
10
|
+
# Basic setup of the class-level state.
|
11
|
+
def self.extended(base)
|
12
|
+
base.instance_variable_set(:@registered_attributes, {})
|
13
|
+
base.class.send(:attr_reader, :registered_attributes)
|
14
|
+
base.send(:private_class_method, :attribute, :multiples)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Defines an attribute usable by Objectmancy to create your object.
|
18
|
+
# Only attributes defined with this method will be converted to attributes
|
19
|
+
# on the final object.
|
20
|
+
#
|
21
|
+
# @param name [#to_sym] Attribute name
|
22
|
+
# @param opts [Hash] Options to be applied
|
23
|
+
# @option opts [Symbol, Class] :type The type of object to create. Can be
|
24
|
+
# either a Symbol of one of: :datetime; else, a Class
|
25
|
+
# @option opts [Symbol, String] :objectable Method to call on :type. Will
|
26
|
+
# default to calling :new. Ignored if :type is one of the known types.
|
27
|
+
# Requires :type option.
|
28
|
+
# @raise [AttributeAlreadyDefinedError] Attempt to define two attributes
|
29
|
+
# of the same name
|
30
|
+
# @raise [ArgumentError] Pass in :objectable without :type
|
31
|
+
def attribute(name, **opts)
|
32
|
+
symbolized_name = name.to_sym
|
33
|
+
|
34
|
+
if registered_attributes.key? symbolized_name
|
35
|
+
raise AttributeAlreadyDefinedError, name
|
36
|
+
end
|
37
|
+
|
38
|
+
if opts[:objectable] && opts[:type].nil?
|
39
|
+
raise ArgumentError, ':objectable option reuqires :type option'
|
40
|
+
end
|
41
|
+
|
42
|
+
registered_attributes[symbolized_name] = AttributeOptions.new(opts)
|
43
|
+
|
44
|
+
attr_accessor symbolized_name
|
45
|
+
end
|
46
|
+
|
47
|
+
# Allows the definition of Arrays of items to be turns into objects. Bear
|
48
|
+
# in mind that Arrays of basic objects (Strings, numbers, anything else
|
49
|
+
# that doesn't need special initialization) are handled by .attribute.
|
50
|
+
#
|
51
|
+
# @param name [#to_sym] Attribute name
|
52
|
+
# @param opts [Hash] Options to be applied
|
53
|
+
# @option opts [Symbol, Class] :type The type of object to create. Can be
|
54
|
+
# either a Symbol of one of: :datetime; else, a Class
|
55
|
+
# @option opts [Symbol, String] :objectable Method to call on :type. Will
|
56
|
+
# default to calling :new. Ignored if :type is one of the known types.
|
57
|
+
# @raise [AttributeAlreadyDefinedError] Attempt to define two attributes
|
58
|
+
# of the same name
|
59
|
+
# @raise [ArgumentError] Calling .multiples without :type option for
|
60
|
+
# non-Hashable objects.
|
61
|
+
def multiples(name, **opts)
|
62
|
+
if !ancestors.include?(Objectmancy::Hashable) && opts[:type].nil?
|
63
|
+
raise ArgumentError, 'Multiples require the :type option'
|
64
|
+
end
|
65
|
+
|
66
|
+
attribute(name, opts.merge(multiple: true))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'objectmancy/common_class_methods'
|
2
|
+
require 'objectmancy/types'
|
3
|
+
require 'pry'
|
4
|
+
|
5
|
+
module Objectmancy
|
6
|
+
# Mixin for allowing your object to be converted into a Hash.
|
7
|
+
module Hashable
|
8
|
+
# ClassMethods specific to Hashable functionality
|
9
|
+
# module ClassMethods
|
10
|
+
# # Allows the definition of Arrays of items to be turns into objects. Bear
|
11
|
+
# # in mind that Arrays of basic objects (Strings, numbers, anything else
|
12
|
+
# # that doesn't need special initialization) are handled by .attribute.
|
13
|
+
# #
|
14
|
+
# # @param name [#to_sym] Attribute name
|
15
|
+
# # @param opts [Hash] Options to be applied
|
16
|
+
# # @option opts [Symbol, Class] :type The type of object to create. Can be
|
17
|
+
# # either a Symbol of one of: :datetime; else, a Class
|
18
|
+
# # @option opts [Symbol, String] :objectable Method to call on :type. Will
|
19
|
+
# # default to calling :new. Ignored if :type is one of the known types.
|
20
|
+
# # @raise [AttributeAlreadyDefinedError] Attempt to define two attributes
|
21
|
+
# # of the same name
|
22
|
+
# def multiples(name, **opts)
|
23
|
+
# super(name, { hashable: true }.merge(opts))
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
|
27
|
+
# @private
|
28
|
+
# This is a little shady, but I'm not sure of a better way to do it.
|
29
|
+
# I gladly welcome suggestions.
|
30
|
+
def self.included(base)
|
31
|
+
base.extend(CommonClassMethods)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Turns the object into a Hash according to the rules defined with
|
35
|
+
#
|
36
|
+
# @return [Hash] Hash representing the object
|
37
|
+
def hashify
|
38
|
+
_present_hashable_values.each_with_object({}) do |(attr, options), memo|
|
39
|
+
memo[attr] = _hashify_value(send(attr), options)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# Provides current hashable values which have a non-empty value
|
46
|
+
#
|
47
|
+
# @return [Hash] Hash of values
|
48
|
+
def _present_hashable_values
|
49
|
+
self.class.registered_attributes.reject do |attr, _|
|
50
|
+
attr_to_check = send(attr)
|
51
|
+
|
52
|
+
attr_to_check.nil? ||
|
53
|
+
(attr_to_check.respond_to?(:empty?) && attr_to_check.empty?)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Generates the value for the produced hash
|
58
|
+
#
|
59
|
+
# @param value [Object] Object to be used as a value for the hash
|
60
|
+
# @param attribute_options [AttributeOptions] Options for the current
|
61
|
+
# attribute
|
62
|
+
# @return [Object] Value for the final hash
|
63
|
+
def _hashify_value(value, attribute_options)
|
64
|
+
if value.respond_to? :hashify
|
65
|
+
value.hashify
|
66
|
+
elsif attribute_options.multiple
|
67
|
+
value.map { |v| _hashify_value(v, attribute_options.force_singular) }
|
68
|
+
elsif attribute_options.hashable || attribute_options.type
|
69
|
+
_convert_hashable_value(value, attribute_options)
|
70
|
+
else
|
71
|
+
value
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def _convert_hashable_value(value, attribute_options)
|
76
|
+
hashable_method =
|
77
|
+
attribute_options.hashable ||
|
78
|
+
Types::SPECIAL_TYPES[attribute_options.type][:hashable]
|
79
|
+
|
80
|
+
value.send(hashable_method)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -1,13 +1,9 @@
|
|
1
|
-
require 'objectmancy/
|
1
|
+
require 'objectmancy/common_class_methods'
|
2
2
|
require 'objectmancy/types'
|
3
|
-
require 'objectmancy/attribute_options'
|
4
|
-
|
5
|
-
require 'pry'
|
6
3
|
|
7
4
|
module Objectmancy
|
8
5
|
# Mixin for allowing your objects to take a Hash and turn them into an object.
|
9
|
-
#
|
10
|
-
# is.
|
6
|
+
#
|
11
7
|
# @example Including Objectable
|
12
8
|
# class Kitten
|
13
9
|
# include Objectmancy::Objectable
|
@@ -17,75 +13,10 @@ module Objectmancy
|
|
17
13
|
#
|
18
14
|
# tabby = Kitten.new(name: 'Eddy')
|
19
15
|
# tabby.name # => "Eddy"
|
20
|
-
#
|
21
|
-
#
|
22
16
|
module Objectable
|
23
17
|
# @private
|
24
18
|
def self.included(base)
|
25
|
-
base.extend(
|
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
|
19
|
+
base.extend(CommonClassMethods)
|
89
20
|
end
|
90
21
|
|
91
22
|
# Creates your object. You should use the after_initialize
|
@@ -94,20 +25,28 @@ module Objectmancy
|
|
94
25
|
def initialize(attrs = {})
|
95
26
|
before_initialize
|
96
27
|
|
97
|
-
|
98
|
-
options = self.class.registered_attributes[attr.to_sym]
|
28
|
+
_attributes_update!(attrs)
|
99
29
|
|
100
|
-
|
101
|
-
|
102
|
-
_convert_multiples(value, options.type, options.objectable)
|
103
|
-
else
|
104
|
-
_single_value(value, options)
|
105
|
-
end
|
30
|
+
after_initialize
|
31
|
+
end
|
106
32
|
|
107
|
-
|
108
|
-
|
33
|
+
# Updates the attributes of the object
|
34
|
+
#
|
35
|
+
# @param attrs [Hash] Attributes to update
|
36
|
+
def mass_update(attrs = {})
|
37
|
+
_attributes_update!(attrs)
|
38
|
+
end
|
109
39
|
|
110
|
-
|
40
|
+
# Comparator for two objects
|
41
|
+
#
|
42
|
+
# @param other [Object]to be compared to
|
43
|
+
# @return [TrueClass, FalseClass] Boolean indicating if the two objects
|
44
|
+
# are equal.
|
45
|
+
def ==(other)
|
46
|
+
self.class == other.class &&
|
47
|
+
self.class.registered_attributes.keys.all? do |attr|
|
48
|
+
send(attr) == other.send(attr)
|
49
|
+
end
|
111
50
|
end
|
112
51
|
|
113
52
|
private
|
@@ -120,12 +59,30 @@ module Objectmancy
|
|
120
59
|
|
121
60
|
# Determines which attributes are assignable
|
122
61
|
#
|
123
|
-
# @param attrs [Hash] Provided
|
62
|
+
# @param attrs [Hash] Provided hash of attributes
|
124
63
|
# @return [Hash] Allowed attributes
|
125
64
|
def _assignable_attributes(attrs)
|
126
65
|
attrs.select { |k, _| self.class.registered_attributes.include? k.to_sym }
|
127
66
|
end
|
128
67
|
|
68
|
+
# Updates the values for defiend attributes
|
69
|
+
#
|
70
|
+
# @param attrs [Hash] Provided hash of attributes
|
71
|
+
def _attributes_update!(attrs)
|
72
|
+
_assignable_attributes(attrs).each do |attr, value|
|
73
|
+
options = self.class.registered_attributes[attr.to_sym]
|
74
|
+
|
75
|
+
value =
|
76
|
+
if options.multiple
|
77
|
+
_convert_multiples(value, options.type, options.objectable)
|
78
|
+
else
|
79
|
+
_single_value(value, options)
|
80
|
+
end
|
81
|
+
|
82
|
+
send("#{attr}=", value)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
129
86
|
# Assigns a multiples attribute
|
130
87
|
#
|
131
88
|
# @param multiples [Array] Array of values to be converted
|
data/lib/objectmancy/types.rb
CHANGED
@@ -4,7 +4,11 @@ module Objectmancy
|
|
4
4
|
# Namespace for storing type logic around manipulating different data types.
|
5
5
|
module Types
|
6
6
|
# DateTime special type specification
|
7
|
-
DATETIME = {
|
7
|
+
DATETIME = {
|
8
|
+
klass: ISO8601::DateTime,
|
9
|
+
objectable: :new,
|
10
|
+
hashable: :to_s
|
11
|
+
}.freeze
|
8
12
|
|
9
13
|
# Known types with special behavior defined.
|
10
14
|
SPECIAL_TYPES = {
|
data/lib/objectmancy/version.rb
CHANGED
data/lib/objectmancy.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
require 'objectmancy/objectable'
|
2
|
+
require 'objectmancy/hashable'
|
2
3
|
|
3
4
|
# Namespace for all Objectmancy functionality
|
4
5
|
# Also serves as a mixin to include Objectable
|
5
6
|
module Objectmancy
|
7
|
+
def self.included(base)
|
8
|
+
base.include(Objectable)
|
9
|
+
base.include(Hashable)
|
10
|
+
end
|
6
11
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: objectmancy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jon Anderson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-10-
|
11
|
+
date: 2016-10-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: iso8601
|
@@ -75,7 +75,9 @@ extra_rdoc_files: []
|
|
75
75
|
files:
|
76
76
|
- lib/objectmancy.rb
|
77
77
|
- lib/objectmancy/attribute_options.rb
|
78
|
+
- lib/objectmancy/common_class_methods.rb
|
78
79
|
- lib/objectmancy/errors.rb
|
80
|
+
- lib/objectmancy/hashable.rb
|
79
81
|
- lib/objectmancy/objectable.rb
|
80
82
|
- lib/objectmancy/types.rb
|
81
83
|
- lib/objectmancy/version.rb
|
@@ -89,9 +91,9 @@ require_paths:
|
|
89
91
|
- lib
|
90
92
|
required_ruby_version: !ruby/object:Gem::Requirement
|
91
93
|
requirements:
|
92
|
-
- - "
|
94
|
+
- - "~>"
|
93
95
|
- !ruby/object:Gem::Version
|
94
|
-
version: '
|
96
|
+
version: '2.1'
|
95
97
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
98
|
requirements:
|
97
99
|
- - ">="
|