jsonapi-serializable 0.1.1.beta1
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/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: []
|