iso-deserializer 0.8.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.
@@ -0,0 +1,54 @@
1
+ module Deserializer
2
+ module Attributable
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ class << self
7
+ # deserializer interface functions
8
+
9
+ def attributes( *attrs )
10
+ self.__attrs = __attrs.dup
11
+ attrs.each do |attr|
12
+ attribute( attr, {} )
13
+ end
14
+ end
15
+
16
+ def attribute( name, opts = {} )
17
+ attribute = Attribute::Attribute.new( Attribute::ValueAttribute, name, opts )
18
+ self.__attrs = __attrs.merge attribute.key => attribute
19
+ end
20
+
21
+ def has_one( name, opts = {} )
22
+ unless opts[:deserializer]
23
+ raise DeserializerError, class: self, message: "has_one associations need a deserilaizer"
24
+ end
25
+
26
+ attribute = Attribute::Attribute.new( Attribute::HasOneAssociation, name, opts )
27
+ self.__attrs = __attrs.merge attribute.key => attribute
28
+ end
29
+
30
+ def has_many( name, opts = {} )
31
+ unless opts[:deserializer]
32
+ raise DeserializerError, class: self, message: "has_many associations need a deserilaizer"
33
+ end
34
+
35
+ attribute = Attribute::Attribute.new( Attribute::HasManyAssociation, name, opts )
36
+ self.__attrs = __attrs.merge attribute.key => attribute
37
+ end
38
+
39
+ def belongs_to( *args )
40
+ raise DeserializerError, class: self, message: "belongs_to is unsupported."
41
+ end
42
+
43
+ def nests( name, opts = {} )
44
+ unless opts[:deserializer]
45
+ raise DeserializerError, class: self, message: "nested associations need a deserilaizer"
46
+ end
47
+
48
+ attribute = Attribute::Attribute.new( Attribute::NestedAssociation, name, opts )
49
+ self.__attrs = __attrs.merge attribute.key => attribute
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,12 @@
1
+ module Deserializer
2
+ module Attribute
3
+ class Association < Base
4
+
5
+ private
6
+
7
+ def deserializer
8
+ opts[:deserializer]
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,20 @@
1
+ module Deserializer
2
+ module Attribute
3
+ class Attribute
4
+ def initialize( type, name, opts )
5
+ @type = type
6
+ @name = name
7
+ @opts = opts
8
+ end
9
+
10
+ def key
11
+ @opts.fetch :key, @name
12
+ end
13
+
14
+ def to_hash( params, object )
15
+ attribute = @type.new( @name, @opts, object )
16
+ attribute.to_hash( params )
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,49 @@
1
+ module Deserializer
2
+ module Attribute
3
+ class Base
4
+
5
+ attr_reader :name
6
+
7
+ def initialize( name, opts = {}, object )
8
+ self.name = name
9
+ self.opts = opts
10
+ self.object = object
11
+ end
12
+
13
+ # simple object
14
+ # { key => value }
15
+
16
+ # has_* object
17
+ # { key => { deserialized obejct }}
18
+
19
+ # has_one :whatever; where def wahtever{ object }
20
+ # { object }
21
+ def to_hash( params )
22
+ return {} unless params.has_key? key
23
+ tuple( params )
24
+ end
25
+
26
+ private
27
+
28
+ attr_accessor :opts, :value, :object
29
+ attr_writer :name
30
+
31
+ def key
32
+ @key ||= opts.fetch :key, name
33
+ end
34
+
35
+ def tuple( params = {} )
36
+ value = value( params )
37
+ if value == :ignore
38
+ {}
39
+ else
40
+ { name => value }
41
+ end
42
+ end
43
+
44
+ def value( params = {} )
45
+ return "not implemented"
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,14 @@
1
+ module Deserializer
2
+ module Attribute
3
+ class HasManyAssociation < Association
4
+
5
+ def value( params )
6
+ target = []
7
+ params[key].each do |association_datum|
8
+ target << deserializer.from_params( association_datum )
9
+ end
10
+ target
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,35 @@
1
+ module Deserializer
2
+ module Attribute
3
+ class HasOneAssociation < Association
4
+
5
+ def to_hash( params )
6
+ return {} unless params.has_key? key
7
+ value = deserializer.from_params( params[key] )
8
+
9
+ if object.respond_to? name
10
+
11
+ target = object.send( name )
12
+
13
+ # has_one :thing, deserializer: ThingDeserializer
14
+ #
15
+ # def thing
16
+ # object
17
+ # end
18
+ if target == object.object
19
+ return value
20
+
21
+ # has_one :thing, deserializer: GnihtDeserializer
22
+ #
23
+ # def thing
24
+ # :some_other_key
25
+ # end
26
+ else
27
+ return { target => value }
28
+ end
29
+ else
30
+ return { name => value }
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,10 @@
1
+ module Deserializer
2
+ module Attribute
3
+ class NestedAssociation < Association
4
+
5
+ def to_hash( params )
6
+ { name => deserializer.from_params( params ) }
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,24 @@
1
+ module Deserializer
2
+ module Attribute
3
+ class ValueAttribute < Base
4
+
5
+ def value( params )
6
+ value = params[key]
7
+
8
+ if opts[:ignore_empty] && value.blank?
9
+ return :ignore
10
+ end
11
+
12
+ if opts[:convert_with]
13
+ method = opts[:convert_with]
14
+ if object.respond_to? method
15
+ return object.send method, value
16
+ end
17
+ end
18
+ # other options go here
19
+
20
+ value
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,11 @@
1
+ module Deserializer
2
+ module Attribute
3
+ autoload :Base, "deserializer/attribute/base"
4
+ autoload :Association, "deserializer/attribute/association"
5
+ autoload :Attribute, "deserializer/attribute/attribute"
6
+ autoload :HasManyAssociation, "deserializer/attribute/has_many_association"
7
+ autoload :HasOneAssociation, "deserializer/attribute/has_one_association"
8
+ autoload :NestedAssociation, "deserializer/attribute/nested_association"
9
+ autoload :ValueAttribute, "deserializer/attribute/value_attribute"
10
+ end
11
+ end
@@ -0,0 +1,56 @@
1
+ module Deserializer
2
+ class Base
3
+
4
+ ## atribute, has_one, nested, etc associations
5
+ include Deserializer::Attributable
6
+
7
+ class_attribute :__attrs
8
+ self.__attrs = {}
9
+ class << self
10
+
11
+ # deserializer usage functions
12
+
13
+ def from_params( params = {} )
14
+ new( params ).deserialize
15
+ end
16
+
17
+ def permitted_params
18
+ __attrs.keys
19
+ end
20
+ end
21
+
22
+ attr_reader :object
23
+
24
+ def deserialize
25
+
26
+ object ||= {}
27
+
28
+ # deserialize
29
+ self.class.__attrs.each do |_, attr|
30
+ object.merge!( attr.to_hash( params, self ) ) do |key, old_value, new_value|
31
+ # in the case that 2 has_ones merge into the same key. Not sure i want to support this
32
+ if old_value.is_a?( Hash ) && new_value.is_a?( Hash )
33
+ old_value.merge new_value
34
+ else
35
+ new_value
36
+ end
37
+ end
38
+ end
39
+ object
40
+ end
41
+
42
+ protected
43
+
44
+ attr_accessor :params
45
+ attr_writer :object
46
+
47
+ def initialize( params = {})
48
+ unless params
49
+ raise DeserializerError, class: self.class, message: "params cannot be nil"
50
+ end
51
+
52
+ self.params = params
53
+ self.object = {}
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,12 @@
1
+ module Deserializer
2
+ class DeserializerError < StandardError
3
+ def initialize(opts = {})
4
+ @klass = opts.fetch :class, Deserializer::Base
5
+ @message = opts.fetch :message, ""
6
+ end
7
+
8
+ def message
9
+ "#{@klass}: #{@message}"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ module Deserializer
2
+ VERSION = "0.8.0"
3
+ end
@@ -0,0 +1,12 @@
1
+ require "deserializer/version"
2
+ require 'active_support'
3
+ require 'active_support/concern'
4
+ require 'active_support/core_ext/object/blank'
5
+ require 'active_support/core_ext/class/attribute'
6
+
7
+ module Deserializer
8
+ autoload :Attribute, 'deserializer/attribute'
9
+ autoload :Attributable, 'deserializer/attributable'
10
+ autoload :Base, 'deserializer/base'
11
+ autoload :DeserializerError, 'deserializer/deserializer_error'
12
+ end
@@ -0,0 +1,148 @@
1
+ class BasicDeserializer < Deserializer::Base
2
+ attributes :user_id,
3
+ :text
4
+ end
5
+
6
+ class AttributeDeserializer < Deserializer::Base
7
+ attribute :user_id, key: :user
8
+ attribute :text
9
+ end
10
+
11
+ class VanillaHasOneDeserializer < Deserializer::Base
12
+ attribute :internal, key: :external
13
+
14
+ has_one :params, deserializer: BasicDeserializer
15
+
16
+ end
17
+
18
+ class EmptiableAttributeDeserializer < Deserializer::Base
19
+ attribute :emptiable, ignore_empty: true
20
+ attribute :nonemptiable, ignore_empty: false
21
+
22
+ attribute :emptiable_with_key, ignore_empty: true, key: :empty
23
+ attribute :nonemptiable_with_key, ignore_empty: false, key: :non_empty
24
+ end
25
+
26
+ class HasOneWithTargetDeserializer < Deserializer::Base
27
+ attribute :internal, key: :external
28
+
29
+ has_one :thing, deserializer: ::BasicDeserializer
30
+
31
+ def thing
32
+ :user_info
33
+ end
34
+ end
35
+
36
+ class OtherThingDeserializer < Deserializer::Base
37
+ attributes :attr1,
38
+ :attr2
39
+ end
40
+
41
+ class TricksyDeserializer < Deserializer::Base
42
+ attribute :internal, key: :external
43
+
44
+ has_one :thing, deserializer: ::BasicDeserializer
45
+
46
+ has_one :other_thing, deserializer: ::OtherThingDeserializer
47
+
48
+ def thing
49
+ :user_info
50
+ end
51
+
52
+ def other_thing
53
+ :user_info
54
+ end
55
+ end
56
+
57
+ class ExtraTricksyDeserializer < Deserializer::Base
58
+ attribute :internal, key: :external
59
+
60
+ has_one :thing, deserializer: ::BasicDeserializer
61
+
62
+ has_one :other_thing, deserializer: ::OtherThingDeserializer
63
+
64
+ def other_thing
65
+ :thing
66
+ end
67
+ end
68
+
69
+ class HasOneWithObjectTargetDeserializer < Deserializer::Base
70
+ attribute :internal, key: :external
71
+
72
+ has_one :thing, deserializer: ::BasicDeserializer
73
+
74
+ has_one :other_thing, deserializer: ::OtherThingDeserializer
75
+
76
+ def thing
77
+ object
78
+ end
79
+
80
+ def other_thing
81
+ object
82
+ end
83
+ end
84
+
85
+ class ConversionDeserializer < Deserializer::Base
86
+ attribute :real_range, convert_with: :to_range
87
+ attribute :bad_range, convert_with: :to_range
88
+
89
+ def to_range(value)
90
+ range_array = Array(value)
91
+ (range_array[0]..range_array[-1])
92
+ end
93
+ end
94
+
95
+ class KeyedConversionDeserializer < Deserializer::Base
96
+ attribute :real_range, convert_with: :to_range, key: :real
97
+ attribute :bad_range, convert_with: :to_range, key: :bad
98
+
99
+ def to_range(value)
100
+ range_array = Array(value)
101
+ (range_array[0]..range_array[-1])
102
+ end
103
+ end
104
+
105
+ class NillableConversionDeserializer < Deserializer::Base
106
+ attribute :real_range, convert_with: :to_range, key: :real, ignore_empty: true
107
+ attribute :bad_range, convert_with: :to_range, key: :bad, ignore_empty: true
108
+
109
+ def to_range(value)
110
+ range_array = Array(value)
111
+ (range_array[0]..range_array[-1])
112
+ end
113
+ end
114
+
115
+ class NestedDeserializer < Deserializer::Base
116
+ attribute :name, key: :attr_1
117
+ attribute :attr_2
118
+ end
119
+
120
+ class NestableDeserializer < Deserializer::Base
121
+ attributes :id,
122
+ :attr_1
123
+
124
+ nests :nested_object, deserializer: ::NestedDeserializer
125
+ end
126
+
127
+ class HasManyDeserializer < Deserializer::Base
128
+ attribute :id
129
+ has_many :attributes, deserializer: AttributeDeserializer
130
+ end
131
+
132
+ class InheritedDeserializer < HasOneWithTargetDeserializer
133
+ attributes :this,
134
+ :that
135
+
136
+ attribute :created_by
137
+ end
138
+
139
+ class KeyedHasManyDeserializer < Deserializer::Base
140
+ attribute :id
141
+ has_many :special_attributes, deserializer: AttributeDeserializer, key: :attributes
142
+ end
143
+
144
+ class KeyedHasOneDeserializer < Deserializer::Base
145
+ attribute :internal, key: :external
146
+
147
+ has_one :user_info, deserializer: ::BasicDeserializer, key: :thing
148
+ end
@@ -0,0 +1,14 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+
3
+ require 'simplecov'
4
+ SimpleCov.start
5
+
6
+ require 'deserializer'
7
+
8
+ test_libs_path = File.expand_path("../lib", __FILE__)
9
+ Dir[File.join(test_libs_path, "/**/*.rb")].each do |file|
10
+ puts file
11
+ require file
12
+ end
13
+
14
+ require 'minitest/autorun'
@@ -0,0 +1,12 @@
1
+ require 'minitest_helper'
2
+
3
+ class DeserializerErrorTest < Minitest::Test
4
+
5
+ def test_error_contains_message
6
+ begin
7
+ BasicDeserializer.has_one :lolnope
8
+ rescue => e
9
+ assert_equal "BasicDeserializer: has_one associations need a deserilaizer", e.message
10
+ end
11
+ end
12
+ end