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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d58d07ca21141f331f6008c56d07660bc1e9187d
4
- data.tar.gz: a5856401d36e4899530e11964b5f748fce6cc93a
3
+ metadata.gz: 62c3afc8c03ca9e9c0de91c4935d1476aa51eb18
4
+ data.tar.gz: 2a1834ac88ef320a58f8c15e6e36618a9bb2bcac
5
5
  SHA512:
6
- metadata.gz: db17c08f46d637f97c1f4730ef9480a99864ebfe2c92432558fc297a4007ad760a4da6041083c2dbdcae16bbee705c574c5b80210b63a13e3737cfd2c9c279ab
7
- data.tar.gz: 7c9691474e80119c1c0a3cc94e7fdbb28c2e12e2194b40e10c3cc3ee7ffb4afda0e6abb51e4ddbc976884c14e1c124eae2f26f2c993cb35a811e2402f658700f
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
- attr_reader :type, :objectable, :multiple
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/errors'
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
- # By default, the values will be whatever the value of the Hash at that key
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(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
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
- _assignable_attributes(attrs).each do |attr, value|
98
- options = self.class.registered_attributes[attr.to_sym]
28
+ _attributes_update!(attrs)
99
29
 
100
- value =
101
- if options.multiple
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
- send("#{attr}=", value)
108
- end
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
- after_initialize
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 base hash
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
@@ -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 = { klass: ISO8601::DateTime, objectable: :new }.freeze
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 = {
@@ -1,4 +1,4 @@
1
1
  module Objectmancy
2
2
  # Version of the gem
3
- VERSION = '1.0.0'.freeze
3
+ VERSION = '2.0.0'.freeze
4
4
  end
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: 1.0.0
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-08 00:00:00.000000000 Z
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: '0'
96
+ version: '2.1'
95
97
  required_rubygems_version: !ruby/object:Gem::Requirement
96
98
  requirements:
97
99
  - - ">="