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.
- 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
|