jsonapi-serializable 0.1.1.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +80 -0
- data/lib/jsonapi/serializable/error.rb +82 -0
- data/lib/jsonapi/serializable/error_dsl.rb +26 -0
- data/lib/jsonapi/serializable/link.rb +65 -0
- data/lib/jsonapi/serializable/relationship.rb +42 -0
- data/lib/jsonapi/serializable/relationship_dsl.rb +82 -0
- data/lib/jsonapi/serializable/resource.rb +92 -0
- data/lib/jsonapi/serializable/resource_dsl.rb +127 -0
- data/lib/jsonapi/serializable.rb +2 -0
- data/lib/jsonapi/serializable_error.rb +93 -0
- data/lib/jsonapi/serializable_link.rb +34 -0
- data/lib/jsonapi/serializable_relationship.rb +56 -0
- data/lib/jsonapi/serializable_resource.rb +114 -0
- metadata +70 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 366782323070f412105463eeffabe870128e49c6
|
4
|
+
data.tar.gz: d80674ddfaa130ea341f286c3f12c87ef5e75016
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1bb696480c7e20a3dbe1b4d1525301bb0203b7fbe444268a7fd96bb8d684cdc8bddf8e9ed74161b261375f88e1962f8d1f7363647a544f7b040a9aa73aafdd2f
|
7
|
+
data.tar.gz: 9d17c652fc934460c7d1ef1249d1c32336671ce3fd26ade2acb0d430c98b24f40b56086d719ac481e77a6b6744f1225071564c5101f74b72b2e65b47d3f42160
|
data/README.md
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# jsonapi-serializable
|
2
|
+
Ruby gem for building and rendering [JSON API](http://jsonapi.org) resources.
|
3
|
+
Built upon the [jsonapi-renderer](https://github.com/beauby/jsonapi) gem.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
```ruby
|
7
|
+
# In Gemfile
|
8
|
+
gem 'jsonapi-serializable'
|
9
|
+
```
|
10
|
+
then
|
11
|
+
```
|
12
|
+
$ bundle
|
13
|
+
```
|
14
|
+
or manually via
|
15
|
+
```
|
16
|
+
$ gem install jsonapi-serializable
|
17
|
+
```
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
First, require the gem:
|
22
|
+
```ruby
|
23
|
+
require 'jsonapi/serializable'
|
24
|
+
```
|
25
|
+
|
26
|
+
Then, define some resource classes:
|
27
|
+
```ruby
|
28
|
+
class PostResource < JSONAPI::Serializable::Resource
|
29
|
+
type 'posts'
|
30
|
+
|
31
|
+
id do
|
32
|
+
@post.id.to_s
|
33
|
+
end
|
34
|
+
|
35
|
+
attribute :title do
|
36
|
+
@post.title
|
37
|
+
end
|
38
|
+
|
39
|
+
attribute :date do
|
40
|
+
@post.date
|
41
|
+
end
|
42
|
+
|
43
|
+
relationship :author do
|
44
|
+
link(:self) do
|
45
|
+
href @url_helper.link_for_rel('posts', @post.id, 'author')
|
46
|
+
meta link_meta: 'some meta'
|
47
|
+
end
|
48
|
+
link(:related) { @url_helper.link_for_res('users', @post.author.id) }
|
49
|
+
data do
|
50
|
+
if @post.author.nil?
|
51
|
+
nil
|
52
|
+
else
|
53
|
+
UserResource.new(user: @post.author, url_helper: @url_helper)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
meta do
|
57
|
+
{ relationship_meta: 'some meta' }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
meta do
|
62
|
+
{ resource_meta: 'some meta' }
|
63
|
+
end
|
64
|
+
|
65
|
+
link(:self) do
|
66
|
+
@url_helper.link_for_res('posts', @post.id)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
```
|
70
|
+
Finally, build your resources from your models and render them:
|
71
|
+
```ruby
|
72
|
+
# post = some post model
|
73
|
+
# UrlHelper is some helper class
|
74
|
+
resource = PostResource.new(post: post, url_helper: UrlHelper)
|
75
|
+
document = JSONAPI.render(resource)
|
76
|
+
```
|
77
|
+
|
78
|
+
## License
|
79
|
+
|
80
|
+
jsonapi-serializable is released under the [MIT License](http://www.opensource.org/licenses/MIT).
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'jsonapi/serializable/link'
|
2
|
+
require 'jsonapi/serializable/error_dsl'
|
3
|
+
|
4
|
+
module JSONAPI
|
5
|
+
module Serializable
|
6
|
+
class ErrorSource
|
7
|
+
def self.as_jsonapi(params = {})
|
8
|
+
self.class.new(params).as_jsonapi
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(params = {})
|
12
|
+
params.each { |k, v| instance_variable_set("@#{k}", v) }
|
13
|
+
@_data = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def as_jsonapi
|
17
|
+
@_data
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def method_missing(name, arg)
|
23
|
+
@_data[name] = arg
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Error
|
28
|
+
include ErrorDSL
|
29
|
+
|
30
|
+
class << self
|
31
|
+
attr_accessor :id, :id_block, :status, :status_block, :code,
|
32
|
+
:code_block, :title, :title_block, :detail, :detail_block,
|
33
|
+
:meta, :meta_block, :source_block, :link_blocks
|
34
|
+
end
|
35
|
+
|
36
|
+
self.link_blocks = {}
|
37
|
+
|
38
|
+
def self.inherited(klass)
|
39
|
+
super
|
40
|
+
klass.link_blocks = self.class.link_blocks.dup
|
41
|
+
end
|
42
|
+
|
43
|
+
def initialize(params = {})
|
44
|
+
@_param_hash = params
|
45
|
+
params.each { |k, v| instance_variable_set("@#{k}", v) }
|
46
|
+
end
|
47
|
+
|
48
|
+
def as_jsonapi
|
49
|
+
hash = links.any? ? { links: links } : {}
|
50
|
+
[:id, :status, :code, :title, :detail, :meta, :source]
|
51
|
+
.each_with_object(hash) do |key, h|
|
52
|
+
value = send(key)
|
53
|
+
h[key] = value unless value.nil?
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def links
|
60
|
+
@_links ||= self.class.link_blocks.each_with_object({}) do |(k, v), h|
|
61
|
+
h[k] = Link.as_jsonapi(@_param_hash, v)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def source
|
66
|
+
@_source ||= ErrorSource.as_jsonapi(@_param_hash,
|
67
|
+
self.class.source_block)
|
68
|
+
end
|
69
|
+
|
70
|
+
[:id, :status, :code, :title, :detail, :meta].each do |key|
|
71
|
+
define_method(key) do
|
72
|
+
unless instance_variable_defined?("@#{key}")
|
73
|
+
value = self.class.send(key) ||
|
74
|
+
instance_eval(self.class.send("#{key}_block"))
|
75
|
+
instance_variable_set("@#{key}", value)
|
76
|
+
end
|
77
|
+
instance_variable_get("@#{key}")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module JSONAPI
|
2
|
+
module Serializable
|
3
|
+
module ErrorDSL
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
[:id, :status, :code, :title, :detail, :meta].each do |key|
|
10
|
+
define_method(key) do |*args, &block|
|
11
|
+
send("@#{key}=", args[0])
|
12
|
+
send("@#{key}_block=", block)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def link(name, &block)
|
17
|
+
link_blocks[name] = block
|
18
|
+
end
|
19
|
+
|
20
|
+
def source(&block)
|
21
|
+
self.source_block = block
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module JSONAPI
|
2
|
+
module Serializable
|
3
|
+
class Link
|
4
|
+
def self.as_jsonapi(param_hash = {}, &block)
|
5
|
+
new(param_hash, &block).as_jsonapi
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(param_hash = {}, &block)
|
9
|
+
param_hash.each { |k, v| instance_variable_set("@#{k}", v) }
|
10
|
+
static_value = instance_eval(&block)
|
11
|
+
if static_value.is_a?(Hash)
|
12
|
+
@_hash = static_value
|
13
|
+
elsif static_value.is_a?(String)
|
14
|
+
@_href = static_value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# @overload href(value)
|
19
|
+
# Declare the href for this link.
|
20
|
+
# @param [String] value The value of href.
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# href "http://api.example.com/users/#{@user.id}"
|
24
|
+
#
|
25
|
+
# @overload href(&block)
|
26
|
+
# Declare the href for this link.
|
27
|
+
# @yieldreturn [String] The value of href.
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
# href do
|
31
|
+
# "http://api.example.com/users/#{@user.id}"
|
32
|
+
# end
|
33
|
+
def href(value = nil, &block)
|
34
|
+
@_href = block.nil? ? value : instance_eval(&block)
|
35
|
+
end
|
36
|
+
|
37
|
+
# @overload meta(value)
|
38
|
+
# Declare the meta information for this link.
|
39
|
+
# @param [Hash] value The meta information hash.
|
40
|
+
#
|
41
|
+
# @example
|
42
|
+
# meta paginated: true
|
43
|
+
#
|
44
|
+
# @overload meta(&block)
|
45
|
+
# Declare the meta information for this link.
|
46
|
+
# @yieldreturn [String] The meta information hash.
|
47
|
+
# @example
|
48
|
+
# meta do
|
49
|
+
# { paginated: true }
|
50
|
+
# end
|
51
|
+
def meta(value = nil, &block)
|
52
|
+
@_meta = block.nil? ? value : instance_eval(&block)
|
53
|
+
end
|
54
|
+
|
55
|
+
def as_jsonapi
|
56
|
+
@_hash ||=
|
57
|
+
if @_meta.nil?
|
58
|
+
@_href
|
59
|
+
else
|
60
|
+
{ href: @_href, meta: @_meta }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'jsonapi/serializable/link'
|
2
|
+
require 'jsonapi/serializable/relationship_dsl'
|
3
|
+
|
4
|
+
module JSONAPI
|
5
|
+
module Serializable
|
6
|
+
class Relationship
|
7
|
+
include RelationshipDSL
|
8
|
+
|
9
|
+
def initialize(param_hash = {}, &block)
|
10
|
+
param_hash.each { |k, v| instance_variable_set("@#{k}", v) }
|
11
|
+
@_param_hash = param_hash
|
12
|
+
@_links = {}
|
13
|
+
instance_eval(&block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def as_jsonapi(included)
|
17
|
+
hash = {}
|
18
|
+
hash[:links] = @_links if @_links.any?
|
19
|
+
hash[:meta] = @_meta unless @_meta.nil?
|
20
|
+
hash[:data] = eval_linkage_data if included && (@_linkage_data_block ||
|
21
|
+
@_data_block)
|
22
|
+
|
23
|
+
hash
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def eval_linkage_data
|
29
|
+
@_linkage_data ||=
|
30
|
+
if @_linkage_data_block
|
31
|
+
@_linkage_data_block.call
|
32
|
+
elsif data.respond_to?(:each)
|
33
|
+
data.map { |res| { type: res.jsonapi_type, id: res.jsonapi_id } }
|
34
|
+
elsif data.nil?
|
35
|
+
nil
|
36
|
+
else
|
37
|
+
{ type: data.jsonapi_type, id: data.jsonapi_id }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module JSONAPI
|
2
|
+
module Serializable
|
3
|
+
module RelationshipDSL
|
4
|
+
# Declare/access the data for this relationship.
|
5
|
+
#
|
6
|
+
# @yieldreturn [JSONAPI::Serializable::Resource,
|
7
|
+
# Array<JSONAPI::Serializable::Resource>,
|
8
|
+
# nil] The data for this relationship.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# data :posts do
|
12
|
+
# @user.posts.map { |p| PostResource.new(post: p) }
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# data :author do
|
17
|
+
# @user.author && UserResource.new(user: @user.author)
|
18
|
+
# end
|
19
|
+
def data(&block)
|
20
|
+
if block.nil?
|
21
|
+
@_data ||= (@_data_block && @_data_block.call)
|
22
|
+
else
|
23
|
+
@_data_block = block
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Declare the linkage data for this relationship. Useful when linkage
|
28
|
+
# can be computed in a more efficient way than the data itself.
|
29
|
+
#
|
30
|
+
# @yieldreturn [Hash] The block to compute linkage data.
|
31
|
+
#
|
32
|
+
# @example
|
33
|
+
# linkage_data do
|
34
|
+
# { id: @post.author_id.to_s, type: 'users' }
|
35
|
+
# end
|
36
|
+
def linkage_data(&block)
|
37
|
+
@_linkage_data_block = block
|
38
|
+
end
|
39
|
+
|
40
|
+
# @overload meta(value)
|
41
|
+
# Declare the meta information for this relationship.
|
42
|
+
# @param [Hash] value The meta information hash.
|
43
|
+
#
|
44
|
+
# @example
|
45
|
+
# meta paginated: true
|
46
|
+
#
|
47
|
+
# @overload meta(&block)
|
48
|
+
# Declare the meta information for this relationship.
|
49
|
+
# @yieldreturn [Hash] The meta information hash.
|
50
|
+
#
|
51
|
+
# @example
|
52
|
+
# meta do
|
53
|
+
# { paginated: true }
|
54
|
+
# end
|
55
|
+
def meta(value = nil)
|
56
|
+
@_meta = value || yield
|
57
|
+
end
|
58
|
+
|
59
|
+
# Declare a link for this relationship. The properties of the link are set
|
60
|
+
# by providing a block in which the DSL methods of
|
61
|
+
# +JSONAPI::Serializable::Link+ are called.
|
62
|
+
# @see JSONAPI::Serialiable::Link
|
63
|
+
#
|
64
|
+
# @param [Symbol] name The key of the link.
|
65
|
+
# @yieldreturn [Hash, String, nil] The block to compute the value, if any.
|
66
|
+
#
|
67
|
+
# @example
|
68
|
+
# link(:self) do
|
69
|
+
# "http://api.example.com/users/#{@user.id}/relationships/posts"
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# @example
|
73
|
+
# link(:related) do
|
74
|
+
# href "http://api.example.com/users/#{@user.id}/posts"
|
75
|
+
# meta authorization_needed: true
|
76
|
+
# end
|
77
|
+
def link(name, &block)
|
78
|
+
@_links[name] = Link.as_jsonapi(@_param_hash, &block)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'jsonapi/serializable/link'
|
2
|
+
require 'jsonapi/serializable/relationship'
|
3
|
+
require 'jsonapi/serializable/resource_dsl'
|
4
|
+
|
5
|
+
module JSONAPI
|
6
|
+
module Serializable
|
7
|
+
class Resource
|
8
|
+
include ResourceDSL
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_accessor :type_val, :type_block, :id_block, :attribute_blocks,
|
12
|
+
:relationship_blocks, :link_blocks, :meta_val, :meta_block
|
13
|
+
end
|
14
|
+
|
15
|
+
self.attribute_blocks = {}
|
16
|
+
self.relationship_blocks = {}
|
17
|
+
self.link_blocks = {}
|
18
|
+
|
19
|
+
def self.inherited(klass)
|
20
|
+
super
|
21
|
+
klass.attribute_blocks = attribute_blocks.dup
|
22
|
+
klass.relationship_blocks = relationship_blocks.dup
|
23
|
+
klass.link_blocks = link_blocks.dup
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(param_hash = {})
|
27
|
+
param_hash.each { |k, v| instance_variable_set("@#{k}", v) }
|
28
|
+
@_id = instance_eval(&self.class.id_block)
|
29
|
+
@_type = self.class.type_val || instance_eval(&self.class.type_block)
|
30
|
+
@_meta = if self.class.meta_val
|
31
|
+
self.class.meta_val
|
32
|
+
elsif self.class.meta_block
|
33
|
+
instance_eval(&self.class.meta_block)
|
34
|
+
end
|
35
|
+
@_attributes = {}
|
36
|
+
@_relationships = self.class.relationship_blocks
|
37
|
+
.each_with_object({}) do |(k, v), h|
|
38
|
+
h[k] = Relationship.new(param_hash, &v)
|
39
|
+
end
|
40
|
+
@_links = self.class.link_blocks.each_with_object({}) do |(k, v), h|
|
41
|
+
h[k] = Link.as_jsonapi(param_hash, &v)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def as_jsonapi(params = {})
|
46
|
+
hash = {}
|
47
|
+
hash[:id] = @_id
|
48
|
+
hash[:type] = @_type
|
49
|
+
attr = attributes(params[:fields] || self.class.attribute_blocks.keys)
|
50
|
+
hash[:attributes] = attr if attr.any?
|
51
|
+
rels = relationships(params[:fields] || @_relationships.keys,
|
52
|
+
params[:include] || [])
|
53
|
+
hash[:relationships] = rels if rels.any?
|
54
|
+
hash[:links] = @_links if @_links.any?
|
55
|
+
hash[:meta] = @_meta unless @_meta.nil?
|
56
|
+
|
57
|
+
hash
|
58
|
+
end
|
59
|
+
|
60
|
+
def jsonapi_type
|
61
|
+
@_type
|
62
|
+
end
|
63
|
+
|
64
|
+
def jsonapi_id
|
65
|
+
@_id
|
66
|
+
end
|
67
|
+
|
68
|
+
def jsonapi_related(include)
|
69
|
+
@_relationships
|
70
|
+
.select { |k, _| include.include?(k) }
|
71
|
+
.each_with_object({}) { |(k, v), h| h[k] = Array(v.data) }
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def attributes(fields)
|
77
|
+
self.class.attribute_blocks
|
78
|
+
.select { |k, _| !@_attributes.key?(k) && fields.include?(k) }
|
79
|
+
.each { |k, v| @_attributes[k] = instance_eval(&v) }
|
80
|
+
@_attributes.select { |k, _| fields.include?(k) }
|
81
|
+
end
|
82
|
+
|
83
|
+
def relationships(fields, include)
|
84
|
+
@_relationships
|
85
|
+
.select { |k, _| fields.include?(k) }
|
86
|
+
.each_with_object({}) do |(k, v), h|
|
87
|
+
h[k] = v.as_jsonapi(include.include?(k))
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module JSONAPI
|
2
|
+
module Serializable
|
3
|
+
module ResourceDSL
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
# @overload type(value)
|
10
|
+
# Declare the JSON API type of this resource.
|
11
|
+
# @param [String] value The value of the type.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# type 'users'
|
15
|
+
#
|
16
|
+
# @overload type(value)
|
17
|
+
# Declare the JSON API type of this resource.
|
18
|
+
# @yieldreturn [String] The value of the type.
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
# type { @user.admin? ? "admin" : "users" }
|
22
|
+
def type(value = nil, &block)
|
23
|
+
self.type_val = value
|
24
|
+
self.type_block = block
|
25
|
+
end
|
26
|
+
|
27
|
+
# Declare the JSON API id of this resource.
|
28
|
+
#
|
29
|
+
# @yieldreturn [String] The id of the resource.
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
# id { @user.id.to_s }
|
33
|
+
def id(&block)
|
34
|
+
self.id_block = block
|
35
|
+
end
|
36
|
+
|
37
|
+
# @overload meta(value)
|
38
|
+
# Declare the meta information for this resource.
|
39
|
+
# @param [Hash] value The meta information hash.
|
40
|
+
#
|
41
|
+
# @example
|
42
|
+
# meta key: value
|
43
|
+
#
|
44
|
+
# @overload meta(&block)
|
45
|
+
# Declare the meta information for this resource.
|
46
|
+
# @yieldreturn [String] The meta information hash.
|
47
|
+
# @example
|
48
|
+
# meta do
|
49
|
+
# { key: value }
|
50
|
+
# end
|
51
|
+
def meta(value = nil, &block)
|
52
|
+
self.meta_val = value
|
53
|
+
self.meta_block = block
|
54
|
+
end
|
55
|
+
|
56
|
+
# Declare an attribute for this resource.
|
57
|
+
#
|
58
|
+
# @param [Symbol] name The key of the attribute.
|
59
|
+
# @yieldreturn [Hash, String, nil] The block to compute the value.
|
60
|
+
#
|
61
|
+
# @example
|
62
|
+
# attribute(:name) { @user.name }
|
63
|
+
def attribute(name, &block)
|
64
|
+
attribute_blocks[name] = block
|
65
|
+
end
|
66
|
+
|
67
|
+
# Declare a relationship for this resource. The properties of the
|
68
|
+
# relationship are set by providing a block in which the DSL methods
|
69
|
+
# of +JSONAPI::Serializable::Relationship+ are called.
|
70
|
+
# @see JSONAPI::Serializable::Relationship
|
71
|
+
#
|
72
|
+
# @param [Symbol] name The key of the relationship.
|
73
|
+
#
|
74
|
+
# @example
|
75
|
+
# relationship :posts do
|
76
|
+
# data { @user.posts.map { |p| PostResource.new(post: p) } }
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# @example
|
80
|
+
# relationship :author do
|
81
|
+
# data do
|
82
|
+
# @post.author && UserResource.new(user: @post.author)
|
83
|
+
# end
|
84
|
+
# linkage_data do
|
85
|
+
# { type: 'users', id: @post.author_id }
|
86
|
+
# end
|
87
|
+
# link(:self) do
|
88
|
+
# "http://api.example.com/posts/#{@post.id}/relationships/author"
|
89
|
+
# end
|
90
|
+
# link(:related) do
|
91
|
+
# "http://api.example.com/posts/#{@post.id}/author"
|
92
|
+
# end
|
93
|
+
# meta do
|
94
|
+
# { author_online: @post.author.online? }
|
95
|
+
# end
|
96
|
+
# end
|
97
|
+
def relationship(name, &block)
|
98
|
+
relationship_blocks[name] = block
|
99
|
+
end
|
100
|
+
|
101
|
+
# Declare a link for this resource. The properties of the link are set
|
102
|
+
# by providing a block in which the DSL methods of
|
103
|
+
# +JSONAPI::Serializable::Link+ are called, or the value of the link
|
104
|
+
# is returned directly.
|
105
|
+
# @see JSONAPI::Serialiable::Link
|
106
|
+
#
|
107
|
+
# @param [Symbol] name The key of the link.
|
108
|
+
# @yieldreturn [Hash, String, nil] The block to compute the value, if
|
109
|
+
# any.
|
110
|
+
#
|
111
|
+
# @example
|
112
|
+
# link(:self) do
|
113
|
+
# "http://api.example.com/users/#{@user.id}"
|
114
|
+
# end
|
115
|
+
#
|
116
|
+
# @example
|
117
|
+
# link(:self) do
|
118
|
+
# href "http://api.example.com/users/#{@user.id}"
|
119
|
+
# meta is_self: true
|
120
|
+
# end
|
121
|
+
def link(name, &block)
|
122
|
+
link_blocks[name] = block
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'jsonapi/serializable_link'
|
2
|
+
|
3
|
+
module JSONAPI
|
4
|
+
class SerializableErrorSource
|
5
|
+
def self.as_jsonapi(params = {})
|
6
|
+
self.class.new(params).as_jsonapi
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(params = {})
|
10
|
+
params.each { |k, v| instance_variable_set("@#{k}", v) }
|
11
|
+
@_data = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def as_jsonapi
|
15
|
+
@_data
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def method_missing(name, arg)
|
21
|
+
@_data[name] = arg
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class SerializableError
|
26
|
+
class << self
|
27
|
+
attr_accessor :id, :id_block, :status, :status_block, :code, :code_block,
|
28
|
+
:title, :title_block, :detail, :detail_block, :meta,
|
29
|
+
:meta_block, :source_block, :link_blocks
|
30
|
+
|
31
|
+
[:id, :status, :code, :title, :detail, :meta].each do |key|
|
32
|
+
define_method(key) do |*args, &block|
|
33
|
+
send("@#{key}=", args[0])
|
34
|
+
send("@#{key}_block=", block)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def link(name, &block)
|
39
|
+
link_blocks[name] = block
|
40
|
+
end
|
41
|
+
|
42
|
+
def source(&block)
|
43
|
+
self.source_block = block
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
self.link_blocks = {}
|
48
|
+
|
49
|
+
def self.inherited(klass)
|
50
|
+
super
|
51
|
+
klass.link_blocks = self.class.link_blocks.dup
|
52
|
+
end
|
53
|
+
|
54
|
+
def initialize(params = {})
|
55
|
+
@_param_hash = params
|
56
|
+
params.each { |k, v| instance_variable_set("@#{k}", v) }
|
57
|
+
end
|
58
|
+
|
59
|
+
def as_jsonapi
|
60
|
+
hash = links.any? ? { links: links } : {}
|
61
|
+
[:id, :status, :code, :title, :detail, :meta, :source]
|
62
|
+
.each_with_object(hash) do |key, h|
|
63
|
+
value = send(key)
|
64
|
+
h[key] = value unless value.nil?
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def links
|
71
|
+
@_links ||= self.class.link_blocks.each_with_object({}) do |(k, v), h|
|
72
|
+
h[k] = JSONAPI::SerializableLink.as_jsonapi(@_param_hash, v)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def source
|
77
|
+
@_source ||=
|
78
|
+
JSONAPI::SerializableErrorSource.as_jsonapi(@_param_hash,
|
79
|
+
self.class.source_block)
|
80
|
+
end
|
81
|
+
|
82
|
+
[:id, :status, :code, :title, :detail, :meta].each do |key|
|
83
|
+
define_method(key) do
|
84
|
+
unless instance_variable_defined?("@#{key}")
|
85
|
+
instance_variable_set("@#{key}",
|
86
|
+
self.class.send(key) ||
|
87
|
+
instance_eval(self.class.send("#{key}_block")))
|
88
|
+
end
|
89
|
+
instance_variable_get("@#{key}")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module JSONAPI
|
2
|
+
class SerializableLink
|
3
|
+
def self.as_jsonapi(param_hash = {}, &block)
|
4
|
+
new(param_hash, &block).as_jsonapi
|
5
|
+
end
|
6
|
+
|
7
|
+
def initialize(param_hash = {}, &block)
|
8
|
+
param_hash.each do |k, v|
|
9
|
+
instance_variable_set("@#{k}", v)
|
10
|
+
end
|
11
|
+
str_value = instance_eval(&block)
|
12
|
+
@_href ||= str_value
|
13
|
+
end
|
14
|
+
|
15
|
+
def as_jsonapi
|
16
|
+
@_hash ||=
|
17
|
+
if @_meta.nil?
|
18
|
+
@_href
|
19
|
+
else
|
20
|
+
{ href: @_href, meta: @_meta }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def href(value = nil, &block)
|
27
|
+
@_href = block.nil? ? value : instance_eval(&block)
|
28
|
+
end
|
29
|
+
|
30
|
+
def meta(value = nil, &block)
|
31
|
+
@_meta = block.nil? ? value : instance_eval(&block)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'jsonapi/serializable_link'
|
2
|
+
|
3
|
+
module JSONAPI
|
4
|
+
class SerializableRelationship
|
5
|
+
def initialize(param_hash = {}, &block)
|
6
|
+
@_param_hash = param_hash
|
7
|
+
@_param_hash.each { |k, v| instance_variable_set("@#{k}", v) }
|
8
|
+
@_links = {}
|
9
|
+
instance_eval(&block)
|
10
|
+
end
|
11
|
+
|
12
|
+
def data(&block)
|
13
|
+
if block.nil?
|
14
|
+
@_data ||= @_data_block.call
|
15
|
+
else
|
16
|
+
@_data_block = block
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def as_jsonapi(included)
|
21
|
+
hash = {}
|
22
|
+
hash[:links] = @_links if @_links.any?
|
23
|
+
hash[:meta] = @_meta unless @_meta.nil?
|
24
|
+
hash[:data] = eval_linkage_data if included
|
25
|
+
|
26
|
+
hash
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def eval_linkage_data
|
32
|
+
@_linkage_data ||=
|
33
|
+
if @_linkage_data_block
|
34
|
+
@_linkage_data_block.call
|
35
|
+
elsif data.respond_to?(:each)
|
36
|
+
data.map { |res| { type: res.jsonapi_type, id: res.jsonapi_id } }
|
37
|
+
elsif data.nil?
|
38
|
+
nil
|
39
|
+
else
|
40
|
+
{ type: data.jsonapi_type, id: data.jsonapi_id }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def linkage_data(&block)
|
45
|
+
@_linkage_data_block = block
|
46
|
+
end
|
47
|
+
|
48
|
+
def meta(value = nil)
|
49
|
+
@_meta = value || yield
|
50
|
+
end
|
51
|
+
|
52
|
+
def link(name, &block)
|
53
|
+
@_links[name] = JSONAPI::SerializableLink.as_jsonapi(@_param_hash, &block)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'jsonapi/serializable_link'
|
2
|
+
require 'jsonapi/serializable_relationship'
|
3
|
+
|
4
|
+
module JSONAPI
|
5
|
+
class SerializableResource
|
6
|
+
class << self
|
7
|
+
attr_accessor :type_val, :type_block, :id_block, :attribute_blocks,
|
8
|
+
:relationship_blocks, :link_blocks, :meta_val, :meta_block
|
9
|
+
|
10
|
+
def type(value = nil, &block)
|
11
|
+
self.type_val = value
|
12
|
+
self.type_block = block
|
13
|
+
end
|
14
|
+
|
15
|
+
def id(&block)
|
16
|
+
self.id_block = block
|
17
|
+
end
|
18
|
+
|
19
|
+
def meta(value = nil, &block)
|
20
|
+
self.meta_val = value
|
21
|
+
self.meta_block = block
|
22
|
+
end
|
23
|
+
|
24
|
+
def attribute(name, &block)
|
25
|
+
attribute_blocks[name] = block
|
26
|
+
end
|
27
|
+
|
28
|
+
def relationship(name, &block)
|
29
|
+
relationship_blocks[name] = block
|
30
|
+
end
|
31
|
+
|
32
|
+
def link(name, &block)
|
33
|
+
link_blocks[name] = block
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
self.attribute_blocks = {}
|
38
|
+
self.relationship_blocks = {}
|
39
|
+
self.link_blocks = {}
|
40
|
+
|
41
|
+
def self.inherited(klass)
|
42
|
+
super
|
43
|
+
klass.attribute_blocks = attribute_blocks.dup
|
44
|
+
klass.relationship_blocks = relationship_blocks.dup
|
45
|
+
klass.link_blocks = link_blocks.dup
|
46
|
+
end
|
47
|
+
|
48
|
+
def initialize(param_hash = {})
|
49
|
+
param_hash.each { |k, v| instance_variable_set("@#{k}", v) }
|
50
|
+
@_id = instance_eval(&self.class.id_block)
|
51
|
+
@_type = self.class.type_val || instance_eval(&self.class.type_block)
|
52
|
+
@_meta = if self.class.meta_val
|
53
|
+
self.class.meta_val
|
54
|
+
elsif self.class.meta_block
|
55
|
+
instance_eval(&self.class.meta_block)
|
56
|
+
end
|
57
|
+
@_attributes = {}
|
58
|
+
@_relationships = self.class.relationship_blocks
|
59
|
+
.each_with_object({}) do |(k, v), h|
|
60
|
+
h[k] = JSONAPI::SerializableRelationship.new(param_hash, &v)
|
61
|
+
end
|
62
|
+
@_links = self.class.link_blocks
|
63
|
+
.each_with_object({}) do |(k, v), h|
|
64
|
+
h[k] = JSONAPI::SerializableLink.as_jsonapi(param_hash, &v)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def as_jsonapi(params = {})
|
69
|
+
hash = {}
|
70
|
+
hash[:id] = @_id
|
71
|
+
hash[:type] = @_type
|
72
|
+
attr = attributes(params[:fields] || @_attributes.keys)
|
73
|
+
hash[:attributes] = attr if attr.any?
|
74
|
+
rels = relationships(params[:field] || @_relationships.keys,
|
75
|
+
params[:include] || [])
|
76
|
+
hash[:relationships] = rels if rels.any?
|
77
|
+
hash[:links] = @_links if @_links.any?
|
78
|
+
hash[:meta] = @_meta unless @_meta.nil?
|
79
|
+
|
80
|
+
hash
|
81
|
+
end
|
82
|
+
|
83
|
+
def jsonapi_type
|
84
|
+
@_type
|
85
|
+
end
|
86
|
+
|
87
|
+
def jsonapi_id
|
88
|
+
@_id
|
89
|
+
end
|
90
|
+
|
91
|
+
def jsonapi_related(include)
|
92
|
+
@_relationships
|
93
|
+
.select { |k, _| include.include?(k) }
|
94
|
+
.each_with_object({}) { |(k, v), h| h[k] = Array(v.data) }
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def attributes(fields)
|
100
|
+
self.class.attribute_blocks
|
101
|
+
.select { |k, _| !@_attributes.key?(k) && fields.include?(k) }
|
102
|
+
.each { |k, v| @_attributes[k] = instance_eval(&v) }
|
103
|
+
@_attributes.select { |k, _| fields.include?(k) }
|
104
|
+
end
|
105
|
+
|
106
|
+
def relationships(fields, include)
|
107
|
+
@_relationships
|
108
|
+
.select { |k, _| fields.include?(k) }
|
109
|
+
.each_with_object({}) do |(k, v), h|
|
110
|
+
h[k] = v.as_jsonapi(include.include?(k))
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jsonapi-serializable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1.beta1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Lucas Hosseini
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-10-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: jsonapi-renderer
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.1'
|
27
|
+
description: DSL for building resource classes to be rendered by jsonapi-renderer.
|
28
|
+
email: lucas.hosseini@gmail.com
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- README.md
|
34
|
+
- lib/jsonapi/serializable.rb
|
35
|
+
- lib/jsonapi/serializable/error.rb
|
36
|
+
- lib/jsonapi/serializable/error_dsl.rb
|
37
|
+
- lib/jsonapi/serializable/link.rb
|
38
|
+
- lib/jsonapi/serializable/relationship.rb
|
39
|
+
- lib/jsonapi/serializable/relationship_dsl.rb
|
40
|
+
- lib/jsonapi/serializable/resource.rb
|
41
|
+
- lib/jsonapi/serializable/resource_dsl.rb
|
42
|
+
- lib/jsonapi/serializable_error.rb
|
43
|
+
- lib/jsonapi/serializable_link.rb
|
44
|
+
- lib/jsonapi/serializable_relationship.rb
|
45
|
+
- lib/jsonapi/serializable_resource.rb
|
46
|
+
homepage: https://github.com/beauby/jsonapi-serializable
|
47
|
+
licenses:
|
48
|
+
- MIT
|
49
|
+
metadata: {}
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options: []
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">"
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 1.3.1
|
64
|
+
requirements: []
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 2.5.1
|
67
|
+
signing_key:
|
68
|
+
specification_version: 4
|
69
|
+
summary: Build and render JSON API resources.
|
70
|
+
test_files: []
|