objectmancy 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
  - - ">="