jsonapi-serializable 0.1.1.beta4 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5b7a55ca51fb13e04ac2c96153ea3558aef19d35
4
- data.tar.gz: 7a378903d6206bdc7cb203bd47a5ec415034ec87
3
+ metadata.gz: bb3b603af78361747fc5ff33c1a81c19c66a78af
4
+ data.tar.gz: 8113fbfcbc52ff590bf7b820dbee01fa43b37b8b
5
5
  SHA512:
6
- metadata.gz: a29424f72a6a0170319503da5f7aa3a8bb62633cdcd496d0e0ae89f5a7431fe6b9dd763e8ff6fe44aa2823fa897c191434f6fe211836cc3133dcc57654ebb065
7
- data.tar.gz: 18ead5e2f56d6cad50e432c4d09f5f7590df9d27e1b52348d0d3ca1f72e8efd22065d0c763cd3e1b8340ab692fb7c1fb8633674f572dc5dfa9eeb5475a19d66f
6
+ metadata.gz: 54f95566a246044b529b017fd7bf38da736ece3332ee79d221752221baf9840f3ae26d1482061dfce323775fed052fee98bcc879e65e583a6a3943f70dcfa4a7
7
+ data.tar.gz: f9557913f0fa0ee5fa6c35212fdb193ffc2bae554205e988039de7f80ecdf1a3e3c8e9c85d740f9922e85d06e69c1cb51559834e3166e2005142caca0e2d42b7
data/README.md CHANGED
@@ -1,22 +1,12 @@
1
1
  # jsonapi-serializable
2
- Ruby gem for building [JSON API](http://jsonapi.org) resources to be rendered by
3
- the [jsonapi-renderer](https://github.com/jsonapi-rb/renderer) gem.
2
+ Ruby gem for building and rendering [JSON API](http://jsonapi.org) documents.
4
3
 
5
4
  ## Status
6
5
 
7
6
  [![Gem Version](https://badge.fury.io/rb/jsonapi-serializable.svg)](https://badge.fury.io/rb/jsonapi-serializable)
8
- [![Build Status](https://secure.travis-ci.org/jsonapi-rb/serializable.svg?branch=master)](http://travis-ci.org/jsonapi-rb/serializable?branch=master)
9
-
10
- ## Table of Contents
11
-
12
- - [Installation](#installation)
13
- - [Usage](#usage)
14
- - [Documentation](#documentation)
15
- - [`JSONAPI::Serializable::Resource` DSL](#jsonapiserializableresource-dsl)
16
- - [`JSONAPI::Serializable::Relationship` DSL](#jsonapiserializablerelationship-dsl)
17
- - [`JSONAPI::Serializable::Link` DSL](#jsonapiserializablelink-dsl)
18
- - [`JSONAPI::Serializable::Error` DSL](#jsonapiserializableerror-dsl)
19
- - [License](#license)
7
+ [![Build Status](https://secure.travis-ci.org/jsonapi-rb/jsonapi-serializable.svg?branch=master)](http://travis-ci.org/jsonapi-rb/jsonapi-serializable?branch=master)
8
+ [![codecov](https://codecov.io/gh/jsonapi-rb/jsonapi-serializable/branch/master/graph/badge.svg)](https://codecov.io/gh/jsonapi-rb/jsonapi-serializable)
9
+ [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/jsonapi-rb/Lobby)
20
10
 
21
11
  ## Installation
22
12
  ```ruby
@@ -32,288 +22,9 @@ or manually via
32
22
  $ gem install jsonapi-serializable
33
23
  ```
34
24
 
35
- ## Usage
36
-
37
- First, require the gem:
38
- ```ruby
39
- require 'jsonapi/serializable'
40
- ```
41
-
42
- Then, define some resource classes:
43
-
44
- ```ruby
45
- class PostResource < JSONAPI::Serializable::Resource
46
- type 'posts'
47
-
48
- attribute :title
49
-
50
- attribute :date do
51
- @object.created_at
52
- end
53
-
54
- has_one :author, 'V2::SerializableUser' do
55
- link(:self) do
56
- href @url_helpers.link_for_rel('posts', @object.id, 'author')
57
- meta link_meta: 'some meta'
58
- end
59
- link(:related) { @url_helpers.link_for_res('users', @object.author.id) }
60
- meta do
61
- { relationship_meta: 'some meta' }
62
- end
63
- end
64
-
65
- has_many :comments
66
-
67
- meta do
68
- { resource_meta: 'some meta' }
69
- end
70
-
71
- link(:self) do
72
- @url_helpers.link_for_res('posts', @object.id)
73
- end
74
- end
75
- ```
76
-
77
- Then, render your resources:
78
- ```ruby
79
- # `post` is some `Post` object
80
- # `UrlHelpers` is some helper class
81
- document = JSONAPI::Serializable::Renderer.render(
82
- post,
83
- expose: { url_helpers: UrlHelpers.new }
84
- )
85
- ```
86
-
87
- ## Documentation
88
-
89
- ### `JSONAPI::Serializable::AbstractResource` DSL
90
-
91
- + `#initialize(hash)`
92
-
93
- All the values of the hash are made available during serialization as instance
94
- variables within all DSLs.
95
-
96
- Example:
97
- ```ruby
98
- SerializablePost.new(post: post, url_helpers: url_helpers)
99
- # => You can then use @post and @url_helpers from within the DSL.
100
- ```
101
-
102
- + `::type(value = nil, &block)`
103
-
104
- Define the type of the resource, either statically, or dynamically as the
105
- return value of the block.
106
-
107
- + `::id(&block)`
108
-
109
- Define the id of the resource.
110
-
111
- Example:
112
- ```ruby
113
- id { @post.id }
114
- ```
115
-
116
- + `::attribute(key, &block)`
117
-
118
- Define an attribute of the resource.
119
-
120
- Example:
121
- ```ruby
122
- attribute(:title) { @post.title }
123
- ```
124
-
125
- + `::relationship(key, &block)`
126
-
127
- Define a relationship of the resource. The block can contain any instruction of
128
- the [`JSONAPI::Serializable::Relationship` DSL](#jsonapiserializablerelationship-dsl).
129
-
130
- Example:
131
- ```ruby
132
- relationship :comments do
133
- resources do
134
- @post.comments.map do |c|
135
- SerializableComment.new(comment: c, url_helpers: @url_helpers)
136
- end
137
- end
138
- link :self do
139
- @url_helpers.link_for_post_comments(post_id: @post.id)
140
- end
141
- meta do
142
- { count: @post.comments.count }
143
- end
144
- end
145
- ```
146
-
147
- + `::link(key, &block)`
148
-
149
- Define a resource-level link. The block can either return a string or contain
150
- any instruction of the [`JSONAPI::Serializable::Link` DSL](#jsonapiserializablelink-dsl).
151
-
152
- Example:
153
- ```ruby
154
- link :self do
155
- "http://api.example.com/posts/#{@post.id}"
156
- end
157
- ```
158
-
159
- + `::meta(value = nil, &block)`
160
-
161
- Define a resource-level meta member. The value can either be provided
162
- statically, or dynamically as the return value of a block.
163
-
164
- Examples:
165
- ```ruby
166
- meta(experimental: true)
167
- ```
168
- ```ruby
169
- meta do
170
- { remaining_time: @post.remaining_time }
171
- end
172
- ```
173
-
174
- ### `JSONAPI::Serializable::Resource` DSL
175
-
176
- This class is a subclass of `JSONAPI::Serializable::AbstractResource` with a more
177
- convenient DSL tailored for resources that are direct representation of some
178
- business models.
179
-
180
- + `#initialize(hash)`
181
-
182
- See `JSONAPI::Serializable::AbstractResource` DSL.
183
-
184
- The model is expected to be provided in the hash with the key `:model`.
185
-
186
- + `::type(value = nil, &block)`
187
-
188
- See `JSONAPI::Serializable::AbstractResource` DSL.
189
-
190
- + `::id(&block)`
191
-
192
- See `JSONAPI::Serializable::AbstractResource` DSL.
193
-
194
- Defaults to:
195
- ```ruby
196
- id { @model.id }
197
- ```
198
-
199
- + `::attribute(key, &block)`
200
-
201
- See `JSONAPI::Serializable::AbstractResource` DSL.
202
-
203
- Defaults to the following when no block is provided:
204
- ```ruby
205
- attribute key do
206
- @model.public_send(key)
207
- end
208
- ```
209
-
210
- + `::attributes(*keys)`
211
-
212
- Define multiple attributes.
213
-
214
- + `::has_one(key, resource_klass = nil, &block)`
215
-
216
- Define a `has_one` relationship on the resource.
217
-
218
- The serializable class for the related resource can be explicitly stated as the
219
- second parameter, but when omitted it will be infered from the related
220
- resource's class name.
221
-
222
- When no block is provided, the value of the relationship defaults to
223
- `resource_klass.new(model: @model.public_send(key))`.
224
-
225
- + `::has_many(key, resource_klass = nil, &block)`
226
-
227
- Define a `has_many` relationship on the resource.
228
-
229
- The serializable class for the related resources can be explicitly stated as the
230
- second parameter, but when omitted it will be infered from the related
231
- resources' class names.
232
-
233
- When no block is provided, the value of the relationship defaults to:
234
- ```ruby
235
- @model.public_send(key).map do |r|
236
- resource_klass.new(model: r)
237
- end
238
- ```
239
-
240
- + `::relationship(key, &block)`
241
-
242
- See `JSONAPI::Serializable::AbstractResource` DSL.
243
-
244
- + `::link(key, &block)`
245
-
246
- See `JSONAPI::Serializable::AbstractResource` DSL.
247
-
248
- + `::meta(value = nil, &block)`
249
-
250
- See `JSONAPI::Serializable::AbstractResource` DSL.
251
-
252
- ### `JSONAPI::Serializable::Relationship` DSL
253
-
254
- + `::resources(resource_class = nil, &block)`
255
-
256
- NOTE: This section is outdated. It is still valid, but the resources method is
257
- now much more flexible.
258
-
259
- Defines the related serializable resources for the relationship.
260
-
261
- Example:
262
- ```ruby
263
- resources do
264
- if @post.author.nil?
265
- nil
266
- else
267
- SerializableUser.new(user: @post.author)
268
- end
269
- end
270
- ```
271
-
272
- + `::data(&block)`
273
-
274
- Explicitly define linkage data (optional).
275
-
276
- + `::link(key, &block)`
277
-
278
- Define a relationship-level link.
279
-
280
- See `JSONAPI::Serializable::AbstractResource` DSL.
281
-
282
- + `::meta(value = nil, &block)`
283
-
284
- Define some relationship-level meta member.
285
-
286
- See `JSONAPI::Serializable::AbstractResource` DSL.
287
-
288
- ### `JSONAPI::Serializable::Link` DSL
289
-
290
- + `::href(value = nil, &block)`
291
-
292
- Define the href member for the link, either directly, or dynamically as the
293
- return value of a block.
294
-
295
- + `::meta(value = nil, &block)`
296
-
297
- Define the meta member for the link, either directly, or dynamically as the
298
- return value of a block.
299
-
300
- ### `JSONAPI::Serializable::Error` DSL
301
-
302
- + `::id(value = nil, &block)`
303
-
304
- + `::status(value = nil, &block)`
305
-
306
- + `::code(value = nil, &block)`
307
-
308
- + `::title(value = nil, &block)`
309
-
310
- + `::detail(value = nil, &block)`
311
-
312
- + `::meta(value = nil, &block)`
313
-
314
- + `::link(key, &block)`
25
+ ## Usage and documentation
315
26
 
316
- + `::source(&block)`
27
+ See [jsonapi-rb.org/guides](http://jsonapi-rb.org/guides).
317
28
 
318
29
  ## License
319
30
 
@@ -4,13 +4,14 @@ require 'jsonapi/serializable/error_dsl'
4
4
  module JSONAPI
5
5
  module Serializable
6
6
  class ErrorSource
7
- def self.as_jsonapi(params = {})
8
- self.class.new(params).as_jsonapi
7
+ def self.as_jsonapi(params = {}, &block)
8
+ new(params, &block).as_jsonapi
9
9
  end
10
10
 
11
- def initialize(params = {})
11
+ def initialize(params = {}, &block)
12
12
  params.each { |k, v| instance_variable_set("@#{k}", v) }
13
13
  @_data = {}
14
+ instance_eval(&block)
14
15
  end
15
16
 
16
17
  def as_jsonapi
@@ -19,6 +20,7 @@ module JSONAPI
19
20
 
20
21
  private
21
22
 
23
+ # @api private
22
24
  def method_missing(name, arg)
23
25
  @_data[name] = arg
24
26
  end
@@ -28,16 +30,17 @@ module JSONAPI
28
30
  include ErrorDSL
29
31
 
30
32
  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
33
+ attr_accessor :id_val, :id_block, :status_val, :status_block, :code_val,
34
+ :code_block, :title_val, :title_block, :detail_val,
35
+ :detail_block, :meta_val, :meta_block, :source_block,
36
+ :link_blocks
34
37
  end
35
38
 
36
39
  self.link_blocks = {}
37
40
 
38
41
  def self.inherited(klass)
39
42
  super
40
- klass.link_blocks = self.class.link_blocks.dup
43
+ klass.link_blocks = link_blocks.dup
41
44
  end
42
45
 
43
46
  def initialize(exposures = {})
@@ -58,23 +61,30 @@ module JSONAPI
58
61
 
59
62
  def links
60
63
  @_links ||= self.class.link_blocks.each_with_object({}) do |(k, v), h|
61
- h[k] = Link.as_jsonapi(@_exposures, v)
64
+ h[k] = Link.as_jsonapi(@_exposures, &v)
62
65
  end
63
66
  end
64
67
 
65
68
  def source
66
- @_source ||= ErrorSource.as_jsonapi(@_exposures,
67
- self.class.source_block)
69
+ return @_source if @_source
70
+ return if self.class.source_block.nil?
71
+ @_source = ErrorSource.as_jsonapi(@_exposures,
72
+ &self.class.source_block)
68
73
  end
69
74
 
70
75
  [:id, :status, :code, :title, :detail, :meta].each do |key|
71
76
  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)
77
+ unless instance_variable_defined?("@_#{key}")
78
+ block = self.class.send("#{key}_block")
79
+ value =
80
+ if block
81
+ instance_eval(&block)
82
+ else
83
+ self.class.send("#{key}_val")
84
+ end
85
+ instance_variable_set("@_#{key}", value)
76
86
  end
77
- instance_variable_get("@#{key}")
87
+ instance_variable_get("@_#{key}")
78
88
  end
79
89
  end
80
90
  end
@@ -6,11 +6,34 @@ module JSONAPI
6
6
  end
7
7
 
8
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
9
+ def id(value = nil, &block)
10
+ @id_val = value
11
+ @id_block = block
12
+ end
13
+
14
+ def status(value = nil, &block)
15
+ @status_val = value
16
+ @status_block = block
17
+ end
18
+
19
+ def code(value = nil, &block)
20
+ @code_val = value
21
+ @code_block = block
22
+ end
23
+
24
+ def title(value = nil, &block)
25
+ @title_val = value
26
+ @title_block = block
27
+ end
28
+
29
+ def detail(value = nil, &block)
30
+ @detail_val = value
31
+ @detail_block = block
32
+ end
33
+
34
+ def meta(value = nil, &block)
35
+ @meta_val = value
36
+ @meta_block = block
14
37
  end
15
38
 
16
39
  def link(name, &block)
@@ -0,0 +1,7 @@
1
+ module JSONAPI
2
+ module Serializable
3
+ class FieldSet
4
+
5
+ end
6
+ end
7
+ end
@@ -8,11 +8,7 @@ module JSONAPI
8
8
  def initialize(exposures = {}, &block)
9
9
  exposures.each { |k, v| instance_variable_set("@#{k}", v) }
10
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
11
+ @_href = static_value if static_value.is_a?(String)
16
12
  end
17
13
 
18
14
  # @overload href(value)
@@ -0,0 +1,121 @@
1
+ require 'jsonapi/serializable/resource_builder'
2
+
3
+ module JSONAPI
4
+ module Serializable
5
+ class Relationship
6
+ module DSL
7
+ # Declare the related resources for this relationship.
8
+ # @param [String,Constant,Hash{Symbol=>String,Constant}] resource_class
9
+ # @yieldreturn The related resources for this relationship.
10
+ # If it is nil, an object implementing the Serializable::Resource
11
+ # interface, an empty array, or an array of objects implementing the
12
+ # Serializable::Resource interface, then it is used as is.
13
+ # Otherwise an appropriate Serializable::Resource subclass is inferred
14
+ # from the object(s)' namespace/class, the resource_class parameter if
15
+ # provided, and the @_resource_inferrer.
16
+ #
17
+ # @example
18
+ # data do
19
+ # @user.posts.map { |p| PostResource.new(post: p) }
20
+ # end
21
+ #
22
+ # @example
23
+ # data do
24
+ # @post.author && UserResource.new(user: @user.author)
25
+ # end
26
+ #
27
+ # @example
28
+ # data do
29
+ # @user.posts
30
+ # end
31
+ # end
32
+ #
33
+ # @example
34
+ # data SerializablePost do
35
+ # @user.posts
36
+ # end
37
+ #
38
+ # @example
39
+ # data "SerializableUser" do
40
+ # @post.author
41
+ # end
42
+ def data(resource_class = nil)
43
+ # NOTE(beauby): Lazify computation since it is only needed when
44
+ # the corresponding relationship is included.
45
+ @_resources_block = proc do
46
+ _resources_for(yield, resource_class)
47
+ end
48
+ end
49
+
50
+ # @overload linkage(options = {}, &block)
51
+ # Explicitly declare linkage data.
52
+ # @yieldreturn The resource linkage.
53
+ #
54
+ # @example
55
+ # linkage do
56
+ # @object.posts.map { |p| { id: p.id.to_s, type: 'posts' } }
57
+ # end
58
+ #
59
+ # @overload linkage(options = {})
60
+ # Forces standard linkage even if relationship not included.
61
+ #
62
+ # @example
63
+ # linkage always: true
64
+ def linkage(always: false, &block)
65
+ @_include_linkage = always
66
+ @_linkage_block = block
67
+ end
68
+
69
+ # @overload meta(value)
70
+ # Declare the meta information for this relationship.
71
+ # @param [Hash] value The meta information hash.
72
+ #
73
+ # @example
74
+ # meta paginated: true
75
+ #
76
+ # @overload meta(&block)
77
+ # Declare the meta information for this relationship.
78
+ # @yieldreturn [Hash] The meta information hash.
79
+ #
80
+ # @example
81
+ # meta do
82
+ # { paginated: true }
83
+ # end
84
+ def meta(value = nil)
85
+ @_meta = value || yield
86
+ end
87
+
88
+ # Declare a link for this relationship. The properties of the link are set
89
+ # by providing a block in which the DSL methods of
90
+ # +JSONAPI::Serializable::Link+ are called.
91
+ # @see JSONAPI::Serialiable::Link
92
+ #
93
+ # @param [Symbol] name The key of the link.
94
+ # @yieldreturn [Hash, String, nil] The block to compute the value, if any.
95
+ #
96
+ # @example
97
+ # link(:self) do
98
+ # "http://api.example.com/users/#{@user.id}/relationships/posts"
99
+ # end
100
+ #
101
+ # @example
102
+ # link(:related) do
103
+ # href "http://api.example.com/users/#{@user.id}/posts"
104
+ # meta authorization_needed: true
105
+ # end
106
+ def link(name, &block)
107
+ @_links[name] = Link.as_jsonapi(@_exposures, &block)
108
+ end
109
+
110
+ private
111
+
112
+ # @api private
113
+ def _resources_for(objects, resource_class)
114
+ resource_class ||= @_resource_inferrer
115
+
116
+ ResourceBuilder.build(objects, @_exposures, resource_class)
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -1,10 +1,10 @@
1
1
  require 'jsonapi/serializable/link'
2
- require 'jsonapi/serializable/relationship_dsl'
2
+ require 'jsonapi/serializable/relationship/dsl'
3
3
 
4
4
  module JSONAPI
5
5
  module Serializable
6
6
  class Relationship
7
- include RelationshipDSL
7
+ include DSL
8
8
 
9
9
  def initialize(exposures = {}, &block)
10
10
  exposures.each { |k, v| instance_variable_set("@#{k}", v) }
@@ -15,25 +15,26 @@ module JSONAPI
15
15
 
16
16
  def as_jsonapi(included)
17
17
  {}.tap do |hash|
18
- hash[:links] = @_links if @_links.any?
19
- hash[:meta] = @_meta unless @_meta.nil?
20
- include_linkage = included || (!@_links.any? && @_meta.nil?)
21
- hash[:data] = linkage_data if include_linkage
18
+ hash[:links] = @_links if @_links.any?
19
+ hash[:data] = linkage_data if included || @_include_linkage
20
+ hash[:meta] = @_meta unless @_meta.nil?
21
+ hash[:meta] = { included: false } if hash.empty?
22
22
  end
23
23
  end
24
24
 
25
+ # @api private
25
26
  def related_resources
26
- return @_related_resources if @_related_resources
27
-
28
- resources = @_resources_block.call
29
- @_arity = resources.respond_to?(:each) ? :many : :one
30
- @_related_resources = Array(resources)
31
-
32
- @_related_resources
27
+ @_related_resources ||= Array(resources)
33
28
  end
34
29
 
35
30
  private
36
31
 
32
+ # @api private
33
+ def resources
34
+ @_resources ||= @_resources_block.call
35
+ end
36
+
37
+ # @api private
37
38
  def linkage_data
38
39
  return @_linkage_block.call if @_linkage_block
39
40
 
@@ -41,7 +42,7 @@ module JSONAPI
41
42
  { type: res.jsonapi_type, id: res.jsonapi_id }
42
43
  end
43
44
 
44
- @_arity == :many ? linkage_data : linkage_data.first
45
+ resources.respond_to?(:each) ? linkage_data : linkage_data.first
45
46
  end
46
47
  end
47
48
  end