jsonapi-serializable 0.1.1.beta4 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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