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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f682b562eebc07619c02953ffa74bc78cbe6bd20
|
4
|
+
data.tar.gz: 9a23b69d8605c95f7faec47e86d2e14752dcb26d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f48bbd38d5c5b65b6cf3592d583379a3e8b67414468fa13ca1c5369179a225f6ecb9267556feded7fa9dc8fad4f50259457f683f44ebbd4be0f9982be9ce75f2
|
7
|
+
data.tar.gz: 4b2fe8dbb795bf652085a3d6f5c82bb76fdf839c97c8bd5580e42ee0b7f26b60d23bfc8f59b23745959623a1c4de20b86199ce938b5cec15c32ac855f92275de
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Carles Jove i Buxeda
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
# CollectionJson::Serializer
|
2
|
+
|
3
|
+
[](https://travis-ci.org/carlesjove/collection_json_serializer)
|
4
|
+
|
5
|
+
| :warning: ** This is _not finished_ yet, so you better do not use it ** |
|
6
|
+
---------------------------------------------------------------------------
|
7
|
+
|
8
|
+
A Ruby gem to respond with Collection+JSON.
|
9
|
+
|
10
|
+
CollectionJson::Serializer formats JSON responses following the Collection+JSON media type by Mike Amudsen. It also handles input data templates.
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Add this line to your application's Gemfile:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
gem 'collection_json_serializer'
|
18
|
+
```
|
19
|
+
|
20
|
+
And then execute:
|
21
|
+
|
22
|
+
$ bundle
|
23
|
+
|
24
|
+
Or install it yourself as:
|
25
|
+
|
26
|
+
$ gem install collection_json_serializer
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
As this gem user, you will be mainly writing/generating and mantaining serializers for your models. A serializer goes like:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
class UserSerializer < CollectionJson::Serializer
|
34
|
+
href self: "http://example.com/users/1",
|
35
|
+
collection: "http://example.com/users"
|
36
|
+
|
37
|
+
attributes :id, name: { prompt: "Your full name" }, :email
|
38
|
+
|
39
|
+
template :name, email: { prompt: "My email" }
|
40
|
+
|
41
|
+
# Please note that links can only be passed as hashes
|
42
|
+
links dashboard: { href: "http://example.com/my-dashboard" }
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
Then, you pass your objects to the serializer:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
# Create your object as you wish
|
50
|
+
@user = User.new(name: "Carles Jove", email: "hola@carlus.cat")
|
51
|
+
|
52
|
+
# You can also pass an array of objects
|
53
|
+
# user_serializer = UserSerializer.new([@user1, @user2, etc])
|
54
|
+
|
55
|
+
# Pass it to the serializer
|
56
|
+
user_serializer = UserSerializer.new(@user)
|
57
|
+
|
58
|
+
# Pass the serializer to the builder, and pack it as a hash
|
59
|
+
builder = Builder.new(user_serializer)
|
60
|
+
builder.pack
|
61
|
+
# => { collection: { version: "1.0" } }
|
62
|
+
|
63
|
+
# Get it as JSON
|
64
|
+
builder.to_json
|
65
|
+
```
|
66
|
+
|
67
|
+
This will generate this Collection+JSON response:
|
68
|
+
|
69
|
+
```javascript
|
70
|
+
{ "collection":
|
71
|
+
{
|
72
|
+
"version" : "1.0",
|
73
|
+
"href" : "http://example.com/users",
|
74
|
+
"items" : [{
|
75
|
+
"href": "http://example.com/users/1",
|
76
|
+
"data": [
|
77
|
+
{ "name": "id", "value": "1" },
|
78
|
+
{ "name": "name", "value": "Carles Jove", "prompt": "Your full name" },
|
79
|
+
{ "name": "email", "value": "email@example.com" },
|
80
|
+
],
|
81
|
+
"links": [
|
82
|
+
{ "name": "dashboard", "href": "http://example.com/my-dashboard" }
|
83
|
+
]
|
84
|
+
}],
|
85
|
+
"template" : {
|
86
|
+
"data": [
|
87
|
+
{ "name": "name", "value": "" },
|
88
|
+
{ "name": "email", "value": "", "prompt": "My email" }
|
89
|
+
]
|
90
|
+
}
|
91
|
+
}
|
92
|
+
}
|
93
|
+
```
|
94
|
+
|
95
|
+
#### URL placeholders
|
96
|
+
|
97
|
+
URLs can be generated dinamically with placeholder. A placeholder is a URL segment wrapped in curly braces. A placeholder can be any method that can be called on the object that the serializer takes (i.e. `id`, `username`, etc.).
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
class UserSerializer < CollectionJson::Serializer
|
101
|
+
href self: "http://example.com/users/{id}"
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
105
|
+
All placeholders will be called, so you can use more than one if necessary, but you may use only one placeholer per segment.
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
class UserSerializer < CollectionJson::Serializer
|
109
|
+
# This is ok
|
110
|
+
href self: "http://example.com/users/{id}/{username}"
|
111
|
+
|
112
|
+
# This is not ok
|
113
|
+
href self: "http://example.com/users/{id}-{username}"
|
114
|
+
end
|
115
|
+
```
|
116
|
+
|
117
|
+
#### Open Attributes Policy
|
118
|
+
|
119
|
+
Collection+JSON serializer has an __open attributes policy__, which means that objects' attributes can be extended at will. That is good if you want to use many of the [extensions available](https://github.com/collection-json/extensions), and also if you need to add custom extensions to suit your particular needs. Be aware that, [as the specs say](https://github.com/collection-json/spec#7-extensibility), you must only extend attributes in a way that won't break clients that are not aware of them.
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
class UserSerializer < CollectionJson::Serializer
|
123
|
+
attributes :id, name: { css_class: "people" }
|
124
|
+
|
125
|
+
template name: { regex: "/\A[a-zA-Z0-9_]*\z/" }
|
126
|
+
|
127
|
+
links profile: { css_class: "button" }
|
128
|
+
end
|
129
|
+
```
|
130
|
+
|
131
|
+
## Contributing
|
132
|
+
|
133
|
+
1. Fork it ( https://github.com/[my-github-username]/collection_json_serializer/fork )
|
134
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
135
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
136
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
137
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'collection_json_serializer/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "collection_json_serializer"
|
8
|
+
spec.version = CollectionJson::Serializer::VERSION
|
9
|
+
spec.authors = ["Carles Jove i Buxeda"]
|
10
|
+
spec.email = ["hola@carlus.cat"]
|
11
|
+
spec.summary = %q{Serialize objects as Collection+JSON.}
|
12
|
+
spec.description = %q{CollectionJson::Serializer makes it easy to serialize objects into the Collection+JSON hypermedia type.}
|
13
|
+
spec.homepage = "https://github.com/carlesjove/collection_json_serializer"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "activesupport", "~> 4.1"
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
24
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
25
|
+
spec.add_development_dependency "minitest", "~> 5.4"
|
26
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module CollectionJson
|
2
|
+
class Serializer
|
3
|
+
class Builder
|
4
|
+
def initialize(serializer)
|
5
|
+
@serializer = serializer
|
6
|
+
@collection = { version: "1.0" }
|
7
|
+
end
|
8
|
+
|
9
|
+
def pack
|
10
|
+
if @serializer.errors.any?
|
11
|
+
error = "The #{@serializer.class} has errors: "
|
12
|
+
@serializer.errors.each_value { |v| error << v.join(", ") }
|
13
|
+
raise Exception, error
|
14
|
+
else
|
15
|
+
build
|
16
|
+
{ collection: @collection }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_json
|
21
|
+
pack.to_json
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def build
|
27
|
+
# There might be a more elegant way to do it, yes
|
28
|
+
add_href if @serializer.href.respond_to? :key
|
29
|
+
add_items if @serializer.attributes.present?
|
30
|
+
add_template if @serializer.template.present?
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_href
|
34
|
+
if @serializer.href.key? :collection
|
35
|
+
@collection.store :href, @serializer.href[:collection]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def add_items
|
40
|
+
@collection.store :items, Array.new
|
41
|
+
@serializer.resources.each_index do |i|
|
42
|
+
item = CollectionJson::Serializer::Objects::Item.
|
43
|
+
new(@serializer, item: i)
|
44
|
+
@collection[:items] << item.create
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_template
|
49
|
+
@collection.store :template, Hash.new
|
50
|
+
template = CollectionJson::Serializer::Objects::Template.
|
51
|
+
new(@serializer)
|
52
|
+
@collection[:template].store :data, template.create
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module CollectionJson
|
2
|
+
class Serializer
|
3
|
+
class Objects
|
4
|
+
class Item
|
5
|
+
include CollectionJson::Serializer::Support
|
6
|
+
|
7
|
+
def initialize(serializer, item: 0)
|
8
|
+
@serializer = serializer
|
9
|
+
@index = item >= 0 ? item : 0
|
10
|
+
@resource = @serializer.resources[@index]
|
11
|
+
@item = Hash.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def create
|
15
|
+
add_href if @serializer.href.present?
|
16
|
+
add_data if @serializer.attributes.present?
|
17
|
+
add_links if @serializer.links.present?
|
18
|
+
|
19
|
+
@item
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def add_href
|
25
|
+
if @serializer.href.present?
|
26
|
+
@item.store :href, set_href
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_data
|
31
|
+
@serializer.attributes.each do |attr|
|
32
|
+
params = attr.extract_params
|
33
|
+
value = extract_value_from(@resource, params[:name])
|
34
|
+
|
35
|
+
next unless value
|
36
|
+
|
37
|
+
c = { name: params[:name], value: value }
|
38
|
+
c.merge!(params[:properties]) if params[:properties]
|
39
|
+
|
40
|
+
start_object :data, Array.new
|
41
|
+
@item[:data] << c
|
42
|
+
end if @serializer.attributes.present?
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_links
|
46
|
+
@serializer.links.each do |attr|
|
47
|
+
params = attr.extract_params
|
48
|
+
|
49
|
+
next unless params.key? :properties
|
50
|
+
|
51
|
+
start_object :links, Array.new
|
52
|
+
@item[:links] << {
|
53
|
+
name: params[:name].to_s,
|
54
|
+
href: params[:properties][:href]
|
55
|
+
}.merge!(params[:properties])
|
56
|
+
end if @serializer.links.present?
|
57
|
+
end
|
58
|
+
|
59
|
+
def start_object(name, type)
|
60
|
+
@item.store(name.to_sym, type) unless @item.key? name.to_sym
|
61
|
+
end
|
62
|
+
|
63
|
+
def set_href
|
64
|
+
url = @serializer.href[:self] || @serializer.href
|
65
|
+
parse_url(url, @resource)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module CollectionJson
|
2
|
+
class Serializer
|
3
|
+
class Objects
|
4
|
+
class Template
|
5
|
+
def initialize(serializer)
|
6
|
+
@serializer = serializer
|
7
|
+
@data = Array.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def create
|
11
|
+
@serializer.template.each do |attr|
|
12
|
+
params = attr.extract_params
|
13
|
+
|
14
|
+
c = { name: params[:name], value: nil.to_s }
|
15
|
+
c.merge!(params[:properties]) if params[:properties]
|
16
|
+
|
17
|
+
@data << c
|
18
|
+
end if @serializer.template.present?
|
19
|
+
|
20
|
+
@data
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module CollectionJson
|
2
|
+
class Serializer
|
3
|
+
class << self
|
4
|
+
attr_accessor :href
|
5
|
+
attr_accessor :attributes
|
6
|
+
attr_accessor :template
|
7
|
+
attr_accessor :links
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.inherited(base)
|
11
|
+
base.href = []
|
12
|
+
base.attributes = []
|
13
|
+
base.template = []
|
14
|
+
base.links = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.href(*attrs)
|
18
|
+
@href.concat attrs
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.attributes(*attrs)
|
22
|
+
@attributes.concat attrs
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.template(*attrs)
|
26
|
+
@template.concat attrs
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.links(*attrs)
|
30
|
+
@links.concat attrs
|
31
|
+
end
|
32
|
+
|
33
|
+
attr_accessor :resources
|
34
|
+
|
35
|
+
def initialize(resource)
|
36
|
+
@resources = if resource.respond_to? :to_ary
|
37
|
+
resource
|
38
|
+
else
|
39
|
+
[resource]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def href
|
44
|
+
self.class.href.first
|
45
|
+
end
|
46
|
+
|
47
|
+
def attributes
|
48
|
+
self.class.attributes
|
49
|
+
end
|
50
|
+
|
51
|
+
def template
|
52
|
+
self.class.template
|
53
|
+
end
|
54
|
+
|
55
|
+
def links
|
56
|
+
self.class.links
|
57
|
+
end
|
58
|
+
|
59
|
+
def invalid?
|
60
|
+
Validator.new(self).invalid?
|
61
|
+
end
|
62
|
+
|
63
|
+
def valid?
|
64
|
+
Validator.new(self).valid?
|
65
|
+
end
|
66
|
+
|
67
|
+
def errors
|
68
|
+
Validator.new(self).errors
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|