collection_json_serializer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.travis.yml +7 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +137 -0
  7. data/Rakefile +10 -0
  8. data/collection_json_serializer.gemspec +26 -0
  9. data/lib/collection_json_serializer/builder.rb +56 -0
  10. data/lib/collection_json_serializer/core_ext/hash.rb +9 -0
  11. data/lib/collection_json_serializer/core_ext/symbol.rb +8 -0
  12. data/lib/collection_json_serializer/objects/item.rb +70 -0
  13. data/lib/collection_json_serializer/objects/template.rb +25 -0
  14. data/lib/collection_json_serializer/serializer.rb +71 -0
  15. data/lib/collection_json_serializer/support.rb +36 -0
  16. data/lib/collection_json_serializer/validator/url.rb +22 -0
  17. data/lib/collection_json_serializer/validator/value.rb +28 -0
  18. data/lib/collection_json_serializer/validator.rb +131 -0
  19. data/lib/collection_json_serializer/version.rb +5 -0
  20. data/lib/collection_json_serializer.rb +18 -0
  21. data/test/builder/builder_test.rb +157 -0
  22. data/test/fixtures/models.rb +3 -0
  23. data/test/fixtures/poro.rb +26 -0
  24. data/test/fixtures/serializers/custom_item_links_serializer.rb +7 -0
  25. data/test/fixtures/serializers/custom_item_serializer.rb +3 -0
  26. data/test/fixtures/serializers/custom_template_serializer.rb +5 -0
  27. data/test/fixtures/serializers/invalid_serializer.rb +4 -0
  28. data/test/fixtures/serializers/multiple_href_serializer.rb +3 -0
  29. data/test/fixtures/serializers/unknown_attribute_serializer.rb +3 -0
  30. data/test/fixtures/serializers/user_serializer.rb +7 -0
  31. data/test/fixtures/serializers/valid_serializer.rb +5 -0
  32. data/test/minitest_helper.rb +11 -0
  33. data/test/objects/item_test.rb +82 -0
  34. data/test/objects/template_test.rb +40 -0
  35. data/test/serializer/data_test.rb +29 -0
  36. data/test/serializer/href_test.rb +39 -0
  37. data/test/serializer/links_test.rb +23 -0
  38. data/test/serializer/template_test.rb +30 -0
  39. data/test/support/ext_test.rb +23 -0
  40. data/test/support/support_test.rb +29 -0
  41. data/test/validator/invalid_test.rb +138 -0
  42. data/test/validator/url_validator_test.rb +24 -0
  43. data/test/validator/validator_test.rb +64 -0
  44. data/test/validator/value_validator_test.rb +42 -0
  45. 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,5 @@
1
+ module CollectionJson
2
+ class Serializer
3
+ VERSION = "0.0.1"
4
+ end
5
+ 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,3 @@
1
+ # Models we'll be using in tests
2
+ User = Class.new(Model)
3
+ Account = Class.new(Model)
@@ -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
@@ -0,0 +1,7 @@
1
+ class CustomItemLinksSerializer < CollectionJson::Serializer
2
+ links dashboard: {
3
+ href: "/my-dashboard",
4
+ anything: "at all",
5
+ whatever: "really"
6
+ }
7
+ end