iso-deserializer 0.8.0

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