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