collection_json_serializer 0.0.1
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 +14 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +137 -0
- data/Rakefile +10 -0
- data/collection_json_serializer.gemspec +26 -0
- data/lib/collection_json_serializer/builder.rb +56 -0
- data/lib/collection_json_serializer/core_ext/hash.rb +9 -0
- data/lib/collection_json_serializer/core_ext/symbol.rb +8 -0
- data/lib/collection_json_serializer/objects/item.rb +70 -0
- data/lib/collection_json_serializer/objects/template.rb +25 -0
- data/lib/collection_json_serializer/serializer.rb +71 -0
- data/lib/collection_json_serializer/support.rb +36 -0
- data/lib/collection_json_serializer/validator/url.rb +22 -0
- data/lib/collection_json_serializer/validator/value.rb +28 -0
- data/lib/collection_json_serializer/validator.rb +131 -0
- data/lib/collection_json_serializer/version.rb +5 -0
- data/lib/collection_json_serializer.rb +18 -0
- data/test/builder/builder_test.rb +157 -0
- data/test/fixtures/models.rb +3 -0
- data/test/fixtures/poro.rb +26 -0
- data/test/fixtures/serializers/custom_item_links_serializer.rb +7 -0
- data/test/fixtures/serializers/custom_item_serializer.rb +3 -0
- data/test/fixtures/serializers/custom_template_serializer.rb +5 -0
- data/test/fixtures/serializers/invalid_serializer.rb +4 -0
- data/test/fixtures/serializers/multiple_href_serializer.rb +3 -0
- data/test/fixtures/serializers/unknown_attribute_serializer.rb +3 -0
- data/test/fixtures/serializers/user_serializer.rb +7 -0
- data/test/fixtures/serializers/valid_serializer.rb +5 -0
- data/test/minitest_helper.rb +11 -0
- data/test/objects/item_test.rb +82 -0
- data/test/objects/template_test.rb +40 -0
- data/test/serializer/data_test.rb +29 -0
- data/test/serializer/href_test.rb +39 -0
- data/test/serializer/links_test.rb +23 -0
- data/test/serializer/template_test.rb +30 -0
- data/test/support/ext_test.rb +23 -0
- data/test/support/support_test.rb +29 -0
- data/test/validator/invalid_test.rb +138 -0
- data/test/validator/url_validator_test.rb +24 -0
- data/test/validator/validator_test.rb +64 -0
- data/test/validator/value_validator_test.rb +42 -0
- metadata +169 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
module CollectionJson
|
2
|
+
class Serializer
|
3
|
+
module Support
|
4
|
+
def extract_value_from(resource, method)
|
5
|
+
begin
|
6
|
+
value = resource.send(method)
|
7
|
+
rescue NoMethodError
|
8
|
+
# ignore unknown attributes
|
9
|
+
end
|
10
|
+
|
11
|
+
value if value
|
12
|
+
end
|
13
|
+
|
14
|
+
def parse_url(url, object)
|
15
|
+
segments = url.split("/")
|
16
|
+
|
17
|
+
segments.each_with_index do |segment, index|
|
18
|
+
if has_placeholder?(segment)
|
19
|
+
action = segment.gsub(/[{}]/, "")
|
20
|
+
segments[index] = object.send(action)
|
21
|
+
end
|
22
|
+
end if segments_with_placeholder?(segments)
|
23
|
+
|
24
|
+
segments.join("/")
|
25
|
+
end
|
26
|
+
|
27
|
+
def segments_with_placeholder?(segments)
|
28
|
+
segments.any? { |s| has_placeholder?(s) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def has_placeholder?(string)
|
32
|
+
string.chars.first.eql?("{") && string.chars.last.eql?("}")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module CollectionJson
|
2
|
+
class Serializer
|
3
|
+
class Validator
|
4
|
+
class Url
|
5
|
+
# Stolen from https://github.com/eparreno/ruby_regex/blob/master/lib/ruby_regex.rb
|
6
|
+
VALID = /(\A\z)|(\A(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?\z)/ix
|
7
|
+
|
8
|
+
def initialize(value)
|
9
|
+
@uri = value
|
10
|
+
end
|
11
|
+
|
12
|
+
def valid?
|
13
|
+
true unless VALID.match(@uri).nil?
|
14
|
+
end
|
15
|
+
|
16
|
+
def invalid?
|
17
|
+
true unless valid?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module CollectionJson
|
2
|
+
class Serializer
|
3
|
+
class Validator
|
4
|
+
class Value
|
5
|
+
VALID = %w(
|
6
|
+
String
|
7
|
+
Fixnum
|
8
|
+
Float
|
9
|
+
TrueClass
|
10
|
+
FalseClass
|
11
|
+
NilClass
|
12
|
+
)
|
13
|
+
|
14
|
+
def initialize(value)
|
15
|
+
@value = value
|
16
|
+
end
|
17
|
+
|
18
|
+
def valid?
|
19
|
+
VALID.include? @value.class.to_s
|
20
|
+
end
|
21
|
+
|
22
|
+
def invalid?
|
23
|
+
!valid?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module CollectionJson
|
2
|
+
class Serializer
|
3
|
+
class Validator
|
4
|
+
include CollectionJson::Serializer::Support
|
5
|
+
|
6
|
+
attr_accessor :errors
|
7
|
+
|
8
|
+
def initialize(serializer)
|
9
|
+
@serializer = serializer
|
10
|
+
@errors = {}
|
11
|
+
validate
|
12
|
+
end
|
13
|
+
|
14
|
+
def valid?
|
15
|
+
!invalid?
|
16
|
+
end
|
17
|
+
|
18
|
+
def invalid?
|
19
|
+
@errors.any?
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def validate
|
25
|
+
[
|
26
|
+
:attributes,
|
27
|
+
:href,
|
28
|
+
:links,
|
29
|
+
:template
|
30
|
+
].each { |m| send("validate_#{m}") }
|
31
|
+
end
|
32
|
+
|
33
|
+
def validate_attributes
|
34
|
+
@serializer.attributes.each do |attr|
|
35
|
+
params = attr.extract_params
|
36
|
+
|
37
|
+
@serializer.resources.each do |resource|
|
38
|
+
val = extract_value_from(resource, params[:name])
|
39
|
+
if value_is_invalid? val
|
40
|
+
error_for :value, root: :attributes, path: [params[:name]]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
params[:properties].each do |key, value|
|
45
|
+
if value_is_invalid? value
|
46
|
+
error_for :value, root: :attributes, path: [params[:name], key]
|
47
|
+
end
|
48
|
+
end if params[:properties]
|
49
|
+
|
50
|
+
end if @serializer.attributes.any?
|
51
|
+
end
|
52
|
+
|
53
|
+
def validate_href
|
54
|
+
href = @serializer.href
|
55
|
+
case href
|
56
|
+
when String
|
57
|
+
error_for :url, root: :href if url_is_invalid? href
|
58
|
+
when Hash
|
59
|
+
href.each do |key, value|
|
60
|
+
error_for :url, root: :href, path: [key] if url_is_invalid? value
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def validate_links
|
66
|
+
@serializer.links.first.each do |k, link|
|
67
|
+
unless link.key? :href
|
68
|
+
error_for :missing_attribute, root: :links, path: [k, :href]
|
69
|
+
|
70
|
+
next
|
71
|
+
end
|
72
|
+
|
73
|
+
link.each do |key, value|
|
74
|
+
case key
|
75
|
+
when :href
|
76
|
+
if url_is_invalid? link[:href]
|
77
|
+
error_for :url, root: :links, path: [k, key]
|
78
|
+
end
|
79
|
+
else
|
80
|
+
if value_is_invalid? value
|
81
|
+
error_for :value, root: :links, path: [k, key]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end if @serializer.links.present?
|
86
|
+
end
|
87
|
+
|
88
|
+
def validate_template
|
89
|
+
@serializer.template.each do |attr|
|
90
|
+
params = attr.extract_params
|
91
|
+
|
92
|
+
params[:properties].each do |key, value|
|
93
|
+
if value_is_invalid? value
|
94
|
+
error_for :value, root: :template, path: [params[:name], key]
|
95
|
+
end
|
96
|
+
end if params[:properties]
|
97
|
+
|
98
|
+
end if @serializer.template.any?
|
99
|
+
end
|
100
|
+
|
101
|
+
def value_is_invalid?(value)
|
102
|
+
v = CollectionJson::Serializer::Validator::Value.new(value)
|
103
|
+
v.invalid?
|
104
|
+
end
|
105
|
+
|
106
|
+
def url_is_invalid?(value)
|
107
|
+
v = CollectionJson::Serializer::Validator::Url.new(value)
|
108
|
+
v.invalid?
|
109
|
+
end
|
110
|
+
|
111
|
+
def error_for(kind, root:, path: [])
|
112
|
+
case kind.to_sym
|
113
|
+
when :url
|
114
|
+
ending = " is an invalid URL"
|
115
|
+
when :value
|
116
|
+
ending = " is an invalid value"
|
117
|
+
when :missing_attribute
|
118
|
+
ending = " is missing"
|
119
|
+
else
|
120
|
+
ending = " is an invalid value"
|
121
|
+
end
|
122
|
+
|
123
|
+
@errors[root] = [] unless @errors.key? root
|
124
|
+
e = "#{@serializer.class} #{root}"
|
125
|
+
e << ":" + path.join(":") if path.any?
|
126
|
+
e << ending
|
127
|
+
@errors[root] << e
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "collection_json_serializer/version"
|
2
|
+
|
3
|
+
require "collection_json_serializer/core_ext/hash"
|
4
|
+
require "collection_json_serializer/core_ext/symbol"
|
5
|
+
|
6
|
+
require "collection_json_serializer/support"
|
7
|
+
|
8
|
+
require "collection_json_serializer/serializer"
|
9
|
+
|
10
|
+
require "collection_json_serializer/builder"
|
11
|
+
|
12
|
+
require "collection_json_serializer/validator"
|
13
|
+
require "collection_json_serializer/validator/url"
|
14
|
+
require "collection_json_serializer/validator/value"
|
15
|
+
|
16
|
+
require "collection_json_serializer/objects/item"
|
17
|
+
|
18
|
+
require "collection_json_serializer/objects/template"
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require "minitest_helper"
|
2
|
+
|
3
|
+
module CollectionJson
|
4
|
+
class Serializer
|
5
|
+
class Builder
|
6
|
+
class TestBuilder < Minitest::Test
|
7
|
+
def setup
|
8
|
+
@user1 = User.new(name: "Carles Jove", email: "hola@carlus.cat")
|
9
|
+
@user2 = User.new(name: "Aina Jove", email: "hola@example.com")
|
10
|
+
@account = Account.new(id: 1, name: "My Account", created_at: Time.now)
|
11
|
+
@user1.account = @account
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_response_format_with_one_resource
|
15
|
+
user_serializer = UserSerializer.new(@user1)
|
16
|
+
builder = Builder.new(user_serializer)
|
17
|
+
expected = {
|
18
|
+
collection: {
|
19
|
+
version: "1.0",
|
20
|
+
href: "http://example.com/users",
|
21
|
+
items: [
|
22
|
+
{
|
23
|
+
href: "http://example.com/users/#{@user1.id}",
|
24
|
+
data: [
|
25
|
+
{
|
26
|
+
name: "name",
|
27
|
+
value: "Carles Jove"
|
28
|
+
},
|
29
|
+
{
|
30
|
+
name: "email",
|
31
|
+
value: "hola@carlus.cat"
|
32
|
+
}
|
33
|
+
],
|
34
|
+
links: [
|
35
|
+
{
|
36
|
+
name: "dashboard",
|
37
|
+
href: "http://example.com/my-dashboard"
|
38
|
+
}
|
39
|
+
]
|
40
|
+
}
|
41
|
+
],
|
42
|
+
template: {
|
43
|
+
data: [
|
44
|
+
{
|
45
|
+
name: "name",
|
46
|
+
value: ""
|
47
|
+
},
|
48
|
+
{
|
49
|
+
name: "email",
|
50
|
+
value: "",
|
51
|
+
prompt: "My email"
|
52
|
+
}
|
53
|
+
]
|
54
|
+
}
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
assert_equal expected.to_json, builder.to_json
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_response_format_with_multiple_resources
|
62
|
+
user_serializer = UserSerializer.new([@user1, @user2])
|
63
|
+
builder = Builder.new(user_serializer)
|
64
|
+
expected = {
|
65
|
+
collection: {
|
66
|
+
version: "1.0",
|
67
|
+
href: "http://example.com/users",
|
68
|
+
items: [
|
69
|
+
{
|
70
|
+
href: "http://example.com/users/#{@user1.id}",
|
71
|
+
data: [
|
72
|
+
{
|
73
|
+
name: "name",
|
74
|
+
value: "Carles Jove"
|
75
|
+
},
|
76
|
+
{
|
77
|
+
name: "email",
|
78
|
+
value: "hola@carlus.cat"
|
79
|
+
}
|
80
|
+
],
|
81
|
+
links: [
|
82
|
+
{
|
83
|
+
name: "dashboard",
|
84
|
+
href: "http://example.com/my-dashboard"
|
85
|
+
}
|
86
|
+
]
|
87
|
+
},
|
88
|
+
{
|
89
|
+
href: "http://example.com/users/#{@user2.id}",
|
90
|
+
data: [
|
91
|
+
{
|
92
|
+
name: "name",
|
93
|
+
value: "Aina Jove"
|
94
|
+
},
|
95
|
+
{
|
96
|
+
name: "email",
|
97
|
+
value: "hola@example.com"
|
98
|
+
}
|
99
|
+
],
|
100
|
+
links: [
|
101
|
+
{
|
102
|
+
name: "dashboard",
|
103
|
+
href: "http://example.com/my-dashboard"
|
104
|
+
}
|
105
|
+
]
|
106
|
+
}
|
107
|
+
],
|
108
|
+
template: {
|
109
|
+
data: [
|
110
|
+
{
|
111
|
+
name: "name",
|
112
|
+
value: ""
|
113
|
+
},
|
114
|
+
{
|
115
|
+
name: "email",
|
116
|
+
value: "",
|
117
|
+
prompt: "My email"
|
118
|
+
}
|
119
|
+
]
|
120
|
+
}
|
121
|
+
}
|
122
|
+
}
|
123
|
+
|
124
|
+
assert_equal expected.to_json, builder.to_json
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_that_any_attributes_can_be_passed_to_template
|
128
|
+
custom_serializer = CustomTemplateSerializer.new(@user1)
|
129
|
+
builder = Builder.new(custom_serializer)
|
130
|
+
|
131
|
+
expected = {
|
132
|
+
collection: {
|
133
|
+
version: "1.0",
|
134
|
+
template: {
|
135
|
+
data: [
|
136
|
+
{ name: "name", value: "" },
|
137
|
+
{ name: "email", value: "", prompt: "My email", anything: "at all", whatever: "really" }
|
138
|
+
]
|
139
|
+
}
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
assert_equal expected.to_json, builder.to_json
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_that_an_invalid_serializer_raises_an_error
|
147
|
+
invalid_serializer = InvalidSerializer.new(@user1)
|
148
|
+
builder = Builder.new(invalid_serializer)
|
149
|
+
|
150
|
+
assert_raises Exception do
|
151
|
+
builder.pack
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# Base class for pseudo Models
|
2
|
+
#
|
3
|
+
# Create a model:
|
4
|
+
# User = Class.new(Model)
|
5
|
+
#
|
6
|
+
# and then populate it in your tests:
|
7
|
+
# @user = User.new(name: "Carles Jove", email: "hola@carlus.cat")
|
8
|
+
class Model
|
9
|
+
def initialize(hash = {})
|
10
|
+
@attributes = hash
|
11
|
+
end
|
12
|
+
|
13
|
+
def id
|
14
|
+
@attributes[:id] || @attributes["id"] || object_id
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_missing(method, *args)
|
18
|
+
if method.to_s =~ /^(.*)=$/
|
19
|
+
@attributes[$1.to_sym] = args[0]
|
20
|
+
elsif @attributes.key?(method)
|
21
|
+
@attributes[method]
|
22
|
+
else
|
23
|
+
super
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|