iso-deserializer 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.travis.yml +32 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +55 -0
- data/MIT-LICENSE +20 -0
- data/README.md +641 -0
- data/Rakefile +11 -0
- data/deserializer.gemspec +25 -0
- data/gemfiles/4.0.gemfile +8 -0
- data/gemfiles/5.0.gemfile +8 -0
- data/gemfiles/6.0.gemfile +8 -0
- data/lib/deserializer/attributable.rb +54 -0
- data/lib/deserializer/attribute/association.rb +12 -0
- data/lib/deserializer/attribute/attribute.rb +20 -0
- data/lib/deserializer/attribute/base.rb +49 -0
- data/lib/deserializer/attribute/has_many_association.rb +14 -0
- data/lib/deserializer/attribute/has_one_association.rb +35 -0
- data/lib/deserializer/attribute/nested_association.rb +10 -0
- data/lib/deserializer/attribute/value_attribute.rb +24 -0
- data/lib/deserializer/attribute.rb +11 -0
- data/lib/deserializer/base.rb +56 -0
- data/lib/deserializer/deserializer_error.rb +12 -0
- data/lib/deserializer/version.rb +3 -0
- data/lib/deserializer.rb +12 -0
- data/test/lib/deserializers.rb +148 -0
- data/test/minitest_helper.rb +14 -0
- data/test/unit/deserializer_error_test.rb +12 -0
- data/test/unit/deserializer_test.rb +200 -0
- metadata +118 -0
@@ -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,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,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
|
data/lib/deserializer.rb
ADDED
@@ -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
|