jsonapi-serializable 0.1.1.beta3 → 0.1.1.beta4

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: 1a382b6d5802f167908d0d6ee82605a7a52d43a0
4
- data.tar.gz: 9c9211218919e4a05e74b010cbc9a5ad51c6533e
3
+ metadata.gz: 5b7a55ca51fb13e04ac2c96153ea3558aef19d35
4
+ data.tar.gz: 7a378903d6206bdc7cb203bd47a5ec415034ec87
5
5
  SHA512:
6
- metadata.gz: bf14811ff7b926f43b45c9cf49032d3fc0c0fe507d4c446d2108fd948de2c5c782cba5b3e90bfe5e6893ee0ef18dcddaf6c91de9e7052445fae25f1b747f65e0
7
- data.tar.gz: 14e585466ae7124d51c0d7562e52fbff0982f91025ed71ad9f6a01e1100379b241e1ccea9248d2ebe84a273bdf50d020671b41a80c672f819226c4cfdc0c9a69
6
+ metadata.gz: a29424f72a6a0170319503da5f7aa3a8bb62633cdcd496d0e0ae89f5a7431fe6b9dd763e8ff6fe44aa2823fa897c191434f6fe211836cc3133dcc57654ebb065
7
+ data.tar.gz: 18ead5e2f56d6cad50e432c4d09f5f7590df9d27e1b52348d0d3ca1f72e8efd22065d0c763cd3e1b8340ab692fb7c1fb8633674f572dc5dfa9eeb5475a19d66f
data/README.md CHANGED
@@ -11,11 +11,8 @@ the [jsonapi-renderer](https://github.com/jsonapi-rb/renderer) gem.
11
11
 
12
12
  - [Installation](#installation)
13
13
  - [Usage](#usage)
14
- - [Example for Model-based Resources](#example-for-model-based-resources)
15
- - [Example for General Resources](#example-for-general-resources)
16
14
  - [Documentation](#documentation)
17
15
  - [`JSONAPI::Serializable::Resource` DSL](#jsonapiserializableresource-dsl)
18
- - [`JSONAPI::Serializable::Model` DSL](#jsonapiserializablemodel-dsl)
19
16
  - [`JSONAPI::Serializable::Relationship` DSL](#jsonapiserializablerelationship-dsl)
20
17
  - [`JSONAPI::Serializable::Link` DSL](#jsonapiserializablelink-dsl)
21
18
  - [`JSONAPI::Serializable::Error` DSL](#jsonapiserializableerror-dsl)
@@ -44,26 +41,22 @@ require 'jsonapi/serializable'
44
41
 
45
42
  Then, define some resource classes:
46
43
 
47
- ### Example for Model-based Resources
48
-
49
- For resources that are simple representations of models, the DSL is simplified:
50
-
51
44
  ```ruby
52
- class PostResource < JSONAPI::Serializable::Model
45
+ class PostResource < JSONAPI::Serializable::Resource
53
46
  type 'posts'
54
47
 
55
48
  attribute :title
56
49
 
57
50
  attribute :date do
58
- @model.created_at
51
+ @object.created_at
59
52
  end
60
53
 
61
- has_one :author, UserResource do
54
+ has_one :author, 'V2::SerializableUser' do
62
55
  link(:self) do
63
- href @url_helper.link_for_rel('posts', @model.id, 'author')
56
+ href @url_helpers.link_for_rel('posts', @object.id, 'author')
64
57
  meta link_meta: 'some meta'
65
58
  end
66
- link(:related) { @url_helper.link_for_res('users', @model.author.id) }
59
+ link(:related) { @url_helpers.link_for_res('users', @object.author.id) }
67
60
  meta do
68
61
  { relationship_meta: 'some meta' }
69
62
  end
@@ -76,78 +69,24 @@ class PostResource < JSONAPI::Serializable::Model
76
69
  end
77
70
 
78
71
  link(:self) do
79
- @url_helper.link_for_res('posts', @model.id)
72
+ @url_helpers.link_for_res('posts', @object.id)
80
73
  end
81
74
  end
82
75
  ```
83
76
 
84
- Then, build your resources from your models and render them:
85
- ```ruby
86
- # post = some post model
87
- # UrlHelper is some helper class
88
- resource = PostResource.new(model: post, url_helper: UrlHelper)
89
- document = JSONAPI.render(data: resource)
90
- ```
91
-
92
- ### Example for General Resources
93
-
94
- In case your resource is not a simple representation of one of your models,
95
- the more general `JSONAPI::Serializable::Resource` class can be used.
96
-
97
- ```ruby
98
- class PostResource < JSONAPI::Serializable::Resource
99
- type 'posts'
100
-
101
- id do
102
- @post.id.to_s
103
- end
104
-
105
- attribute :title do
106
- @post.title
107
- end
108
-
109
- attribute :date do
110
- @post.date
111
- end
112
-
113
- relationship :author do
114
- link(:self) do
115
- href @url_helper.link_for_rel('posts', @post.id, 'author')
116
- meta link_meta: 'some meta'
117
- end
118
- link(:related) { @url_helper.link_for_res('users', @post.author.id) }
119
- data do
120
- if @post.author.nil?
121
- nil
122
- else
123
- UserResource.new(user: @post.author, url_helper: @url_helper)
124
- end
125
- end
126
- meta do
127
- { relationship_meta: 'some meta' }
128
- end
129
- end
130
-
131
- meta do
132
- { resource_meta: 'some meta' }
133
- end
134
-
135
- link(:self) do
136
- @url_helper.link_for_res('posts', @post.id)
137
- end
138
- end
139
- ```
140
- Finally, build your resources from your models and render them:
77
+ Then, render your resources:
141
78
  ```ruby
142
- # post = some post model
143
- # UrlHelper is some helper class
144
- resource = PostResource.new(post: post, url_helper: UrlHelper)
145
- document = JSONAPI.render(data: resource)
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
+ )
146
85
  ```
147
86
 
148
87
  ## Documentation
149
88
 
150
- ### `JSONAPI::Serializable::Resource` DSL
89
+ ### `JSONAPI::Serializable::AbstractResource` DSL
151
90
 
152
91
  + `#initialize(hash)`
153
92
 
@@ -156,8 +95,8 @@ variables within all DSLs.
156
95
 
157
96
  Example:
158
97
  ```ruby
159
- SerializablePost.new(post: post, url_helper: url_helper)
160
- # => You can then use @post and @url_helper from within the DSL.
98
+ SerializablePost.new(post: post, url_helpers: url_helpers)
99
+ # => You can then use @post and @url_helpers from within the DSL.
161
100
  ```
162
101
 
163
102
  + `::type(value = nil, &block)`
@@ -191,13 +130,13 @@ the [`JSONAPI::Serializable::Relationship` DSL](#jsonapiserializablerelationship
191
130
  Example:
192
131
  ```ruby
193
132
  relationship :comments do
194
- data do
133
+ resources do
195
134
  @post.comments.map do |c|
196
- SerializableComment.new(comment: c, url_helper: @url_helper)
135
+ SerializableComment.new(comment: c, url_helpers: @url_helpers)
197
136
  end
198
137
  end
199
138
  link :self do
200
- @url_helper.link_for_post_comments(post_id: @post.id)
139
+ @url_helpers.link_for_post_comments(post_id: @post.id)
201
140
  end
202
141
  meta do
203
142
  { count: @post.comments.count }
@@ -232,25 +171,25 @@ meta do
232
171
  end
233
172
  ```
234
173
 
235
- ### `JSONAPI::Serializable::Model` DSL
174
+ ### `JSONAPI::Serializable::Resource` DSL
236
175
 
237
- This class is a subclass of `JSONAPI::Serializable::Resource` with a more
176
+ This class is a subclass of `JSONAPI::Serializable::AbstractResource` with a more
238
177
  convenient DSL tailored for resources that are direct representation of some
239
178
  business models.
240
179
 
241
180
  + `#initialize(hash)`
242
181
 
243
- See `JSONAPI::Serializable::Resource` DSL.
182
+ See `JSONAPI::Serializable::AbstractResource` DSL.
244
183
 
245
184
  The model is expected to be provided in the hash with the key `:model`.
246
185
 
247
186
  + `::type(value = nil, &block)`
248
187
 
249
- See `JSONAPI::Serializable::Resource` DSL.
188
+ See `JSONAPI::Serializable::AbstractResource` DSL.
250
189
 
251
190
  + `::id(&block)`
252
191
 
253
- See `JSONAPI::Serializable::Resource` DSL.
192
+ See `JSONAPI::Serializable::AbstractResource` DSL.
254
193
 
255
194
  Defaults to:
256
195
  ```ruby
@@ -259,7 +198,7 @@ id { @model.id }
259
198
 
260
199
  + `::attribute(key, &block)`
261
200
 
262
- See `JSONAPI::Serializable::Resource` DSL.
201
+ See `JSONAPI::Serializable::AbstractResource` DSL.
263
202
 
264
203
  Defaults to the following when no block is provided:
265
204
  ```ruby
@@ -268,6 +207,10 @@ attribute key do
268
207
  end
269
208
  ```
270
209
 
210
+ + `::attributes(*keys)`
211
+
212
+ Define multiple attributes.
213
+
271
214
  + `::has_one(key, resource_klass = nil, &block)`
272
215
 
273
216
  Define a `has_one` relationship on the resource.
@@ -296,28 +239,28 @@ end
296
239
 
297
240
  + `::relationship(key, &block)`
298
241
 
299
- See `JSONAPI::Serializable::Resource` DSL.
242
+ See `JSONAPI::Serializable::AbstractResource` DSL.
300
243
 
301
244
  + `::link(key, &block)`
302
245
 
303
- See `JSONAPI::Serializable::Resource` DSL.
246
+ See `JSONAPI::Serializable::AbstractResource` DSL.
304
247
 
305
248
  + `::meta(value = nil, &block)`
306
249
 
307
- See `JSONAPI::Serializable::Resource` DSL.
250
+ See `JSONAPI::Serializable::AbstractResource` DSL.
308
251
 
309
252
  ### `JSONAPI::Serializable::Relationship` DSL
310
253
 
311
- + `::data(resource_class = nil, &block)`
254
+ + `::resources(resource_class = nil, &block)`
312
255
 
313
- NOTE: This section is outdated. It is still valid, but the data method is now
314
- much more flexible.
256
+ NOTE: This section is outdated. It is still valid, but the resources method is
257
+ now much more flexible.
315
258
 
316
259
  Defines the related serializable resources for the relationship.
317
260
 
318
261
  Example:
319
262
  ```ruby
320
- data do
263
+ resources do
321
264
  if @post.author.nil?
322
265
  nil
323
266
  else
@@ -326,17 +269,21 @@ data do
326
269
  end
327
270
  ```
328
271
 
272
+ + `::data(&block)`
273
+
274
+ Explicitly define linkage data (optional).
275
+
329
276
  + `::link(key, &block)`
330
277
 
331
278
  Define a relationship-level link.
332
279
 
333
- See `JSONAPI::Serializable::Resource` DSL.
280
+ See `JSONAPI::Serializable::AbstractResource` DSL.
334
281
 
335
282
  + `::meta(value = nil, &block)`
336
283
 
337
284
  Define some relationship-level meta member.
338
285
 
339
- See `JSONAPI::Serializable::Resource` DSL.
286
+ See `JSONAPI::Serializable::AbstractResource` DSL.
340
287
 
341
288
  ### `JSONAPI::Serializable::Link` DSL
342
289
 
@@ -0,0 +1,122 @@
1
+ require 'jsonapi/serializable/link'
2
+ require 'jsonapi/serializable/relationship'
3
+ require 'jsonapi/serializable/abstract_resource_dsl'
4
+
5
+ module JSONAPI
6
+ module Serializable
7
+ class AbstractResource
8
+ include AbstractResourceDSL
9
+
10
+ class << self
11
+ attr_accessor :id_block,
12
+ :type_val, :type_block,
13
+ :meta_val, :meta_block,
14
+ :attribute_blocks,
15
+ :relationship_blocks,
16
+ :link_blocks
17
+ end
18
+
19
+ self.attribute_blocks = {}
20
+ self.relationship_blocks = {}
21
+ self.link_blocks = {}
22
+
23
+ def self.inherited(klass)
24
+ super
25
+ klass.type_val = type_val
26
+ klass.type_block = type_block
27
+ klass.id_block = id_block
28
+ klass.meta_val = meta_val
29
+ klass.meta_block = meta_block
30
+ klass.attribute_blocks = attribute_blocks.dup
31
+ klass.relationship_blocks = relationship_blocks.dup
32
+ klass.link_blocks = link_blocks.dup
33
+ end
34
+
35
+ def initialize(exposures = {})
36
+ exposures.each { |k, v| instance_variable_set("@#{k}", v) }
37
+ @_exposures = exposures
38
+ @_type = _type
39
+ @_id = _id
40
+ @_attributes = {}
41
+ @_relationships = _relationships
42
+ @_meta = _meta
43
+ @_links = _links
44
+ end
45
+
46
+ def as_jsonapi(fields: nil, include: [])
47
+ {}.tap do |hash|
48
+ hash[:id] = @_id
49
+ hash[:type] = @_type
50
+ hash[:links] = @_links if @_links.any?
51
+ hash[:meta] = @_meta unless @_meta.nil?
52
+
53
+ attrs = requested_attributes(fields)
54
+ hash[:attributes] = attrs if attrs.any?
55
+
56
+ rels = requested_relationships(fields, include)
57
+ hash[:relationships] = rels if rels.any?
58
+ end
59
+ end
60
+
61
+ def jsonapi_type
62
+ @_type
63
+ end
64
+
65
+ def jsonapi_id
66
+ @_id
67
+ end
68
+
69
+ def jsonapi_related(include)
70
+ @_relationships
71
+ .select { |k, _| include.include?(k) }
72
+ .each_with_object({}) { |(k, v), h| h[k] = v.related_resources }
73
+ end
74
+
75
+ private
76
+
77
+ def _type
78
+ self.class.type_val || instance_eval(&self.class.type_block)
79
+ end
80
+
81
+ def _id
82
+ instance_eval(&self.class.id_block)
83
+ end
84
+
85
+ def _relationships
86
+ self.class.relationship_blocks
87
+ .each_with_object({}) do |(k, v), h|
88
+ h[k] = Relationship.new(@_exposures, &v)
89
+ end
90
+ end
91
+
92
+ def _meta
93
+ if self.class.meta_block
94
+ instance_eval(&self.class.meta_block)
95
+ else
96
+ self.class.meta_val
97
+ end
98
+ end
99
+
100
+ def _links
101
+ self.class.link_blocks
102
+ .each_with_object({}) do |(k, v), h|
103
+ h[k] = Link.as_jsonapi(@_exposures, &v)
104
+ end
105
+ end
106
+
107
+ def requested_attributes(fields)
108
+ self.class.attribute_blocks
109
+ .select { |k, _| fields.nil? || fields.include?(k) }
110
+ .each_with_object({}) { |(k, v), h| h[k] = instance_eval(&v) }
111
+ end
112
+
113
+ def requested_relationships(fields, include)
114
+ @_relationships
115
+ .select { |k, _| fields.nil? || fields.include?(k) }
116
+ .each_with_object({}) do |(k, v), h|
117
+ h[k] = v.as_jsonapi(include.include?(k))
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,130 @@
1
+ module JSONAPI
2
+ module Serializable
3
+ module AbstractResourceDSL
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
+ alias has_many relationship
101
+ alias has_one relationship
102
+ alias belongs_to relationship
103
+
104
+ # Declare a link for this resource. The properties of the link are set
105
+ # by providing a block in which the DSL methods of
106
+ # +JSONAPI::Serializable::Link+ are called, or the value of the link
107
+ # is returned directly.
108
+ # @see JSONAPI::Serialiable::Link
109
+ #
110
+ # @param [Symbol] name The key of the link.
111
+ # @yieldreturn [Hash, String, nil] The block to compute the value, if
112
+ # any.
113
+ #
114
+ # @example
115
+ # link(:self) do
116
+ # "http://api.example.com/users/#{@user.id}"
117
+ # end
118
+ #
119
+ # @example
120
+ # link(:self) do
121
+ # href "http://api.example.com/users/#{@user.id}"
122
+ # meta is_self: true
123
+ # end
124
+ def link(name, &block)
125
+ link_blocks[name] = block
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
@@ -40,9 +40,9 @@ module JSONAPI
40
40
  klass.link_blocks = self.class.link_blocks.dup
41
41
  end
42
42
 
43
- def initialize(params = {})
44
- @_param_hash = params
45
- params.each { |k, v| instance_variable_set("@#{k}", v) }
43
+ def initialize(exposures = {})
44
+ @_exposures = exposures
45
+ exposures.each { |k, v| instance_variable_set("@#{k}", v) }
46
46
  end
47
47
 
48
48
  def as_jsonapi
@@ -58,12 +58,12 @@ module JSONAPI
58
58
 
59
59
  def links
60
60
  @_links ||= self.class.link_blocks.each_with_object({}) do |(k, v), h|
61
- h[k] = Link.as_jsonapi(@_param_hash, v)
61
+ h[k] = Link.as_jsonapi(@_exposures, v)
62
62
  end
63
63
  end
64
64
 
65
65
  def source
66
- @_source ||= ErrorSource.as_jsonapi(@_param_hash,
66
+ @_source ||= ErrorSource.as_jsonapi(@_exposures,
67
67
  self.class.source_block)
68
68
  end
69
69
 
@@ -1,12 +1,12 @@
1
1
  module JSONAPI
2
2
  module Serializable
3
3
  class Link
4
- def self.as_jsonapi(param_hash = {}, &block)
5
- new(param_hash, &block).as_jsonapi
4
+ def self.as_jsonapi(exposures = {}, &block)
5
+ new(exposures, &block).as_jsonapi
6
6
  end
7
7
 
8
- def initialize(param_hash = {}, &block)
9
- param_hash.each { |k, v| instance_variable_set("@#{k}", v) }
8
+ def initialize(exposures = {}, &block)
9
+ exposures.each { |k, v| instance_variable_set("@#{k}", v) }
10
10
  static_value = instance_eval(&block)
11
11
  if static_value.is_a?(Hash)
12
12
  @_hash = static_value
@@ -6,31 +6,42 @@ module JSONAPI
6
6
  class Relationship
7
7
  include RelationshipDSL
8
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 = {}
9
+ def initialize(exposures = {}, &block)
10
+ exposures.each { |k, v| instance_variable_set("@#{k}", v) }
11
+ @_exposures = exposures
12
+ @_links = {}
13
13
  instance_eval(&block)
14
14
  end
15
15
 
16
16
  def as_jsonapi(included)
17
- hash = {}
18
- hash[:links] = @_links if @_links.any?
19
- hash[:meta] = @_meta unless @_meta.nil?
20
- return hash unless included || (!@_links.any? && @_meta.nil?)
21
- hash[:data] = linkage_data
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
22
+ end
23
+ end
24
+
25
+ def related_resources
26
+ return @_related_resources if @_related_resources
22
27
 
23
- hash
28
+ resources = @_resources_block.call
29
+ @_arity = resources.respond_to?(:each) ? :many : :one
30
+ @_related_resources = Array(resources)
31
+
32
+ @_related_resources
24
33
  end
25
34
 
26
35
  private
27
36
 
28
37
  def linkage_data
29
- linkage_data = Array(data).map do |res|
38
+ return @_linkage_block.call if @_linkage_block
39
+
40
+ linkage_data = related_resources.map do |res|
30
41
  { type: res.jsonapi_type, id: res.jsonapi_id }
31
42
  end
32
43
 
33
- data.respond_to?(:each) ? linkage_data : linkage_data.first
44
+ @_arity == :many ? linkage_data : linkage_data.first
34
45
  end
35
46
  end
36
47
  end
@@ -3,55 +3,58 @@ require 'jsonapi/serializable/resource_builder'
3
3
  module JSONAPI
4
4
  module Serializable
5
5
  module RelationshipDSL
6
- # Declare the data for this relationship.
6
+ # Declare the related resources for this relationship.
7
7
  # @param [String,Constant,Hash{Symbol=>String,Constant}] resource_class
8
- # @yieldreturn The data for this relationship.
8
+ # @yieldreturn The related resources for this relationship.
9
9
  # If it is nil, an object implementing the Serializable::Resource
10
10
  # interface, an empty array, or an array of objects implementing the
11
11
  # Serializable::Resource interface, then it is used as is.
12
- # Otherwise an appropriate Serializable::Model subclass is inferred
12
+ # Otherwise an appropriate Serializable::Resource subclass is inferred
13
13
  # from the object(s)' namespace/class, the resource_class parameter if
14
14
  # provided, and the @_resource_inferer.
15
15
  #
16
16
  # @example
17
- # data do
17
+ # resources do
18
18
  # @user.posts.map { |p| PostResource.new(post: p) }
19
19
  # end
20
20
  #
21
21
  # @example
22
- # data do
22
+ # resources do
23
23
  # @post.author && UserResource.new(user: @user.author)
24
24
  # end
25
25
  #
26
26
  # @example
27
- # data do
27
+ # resources do
28
28
  # @user.posts
29
29
  # end
30
30
  # end
31
31
  #
32
32
  # @example
33
- # data SerializablePost do
33
+ # resources SerializablePost do
34
34
  # @user.posts
35
35
  # end
36
36
  #
37
37
  # @example
38
- # data "SerializableUser" do
38
+ # resources "SerializableUser" do
39
39
  # @post.author
40
40
  # end
41
- def data(resource_class = nil)
42
- if block_given?
43
- # NOTE(beauby): Lazify computation since it is only needed when
44
- # the corresponding relationship is included.
45
- @_data_block = proc do
46
- _resources_for(yield, resource_class)
47
- end
48
- else
49
- # NOTE(beauby): In the case of a computation heavy relationship with
50
- # nil value, this block might be executed multiple times.
51
- @_data ||= @_data_block.call
41
+ def resources(resource_class = nil)
42
+ # NOTE(beauby): Lazify computation since it is only needed when
43
+ # the corresponding relationship is included.
44
+ @_resources_block = proc do
45
+ _resources_for(yield, resource_class)
52
46
  end
53
47
  end
54
48
 
49
+ # Explicitly declare linkage data.
50
+ # @yieldreturn The resource linkage.
51
+ def data(&block)
52
+ # NOTE(beauby): Lazify computation since it is only executed when
53
+ # the corresponding relationship is included (or no links and
54
+ # no meta was specified).
55
+ @_linkage_block = block
56
+ end
57
+
55
58
  # @overload meta(value)
56
59
  # Declare the meta information for this relationship.
57
60
  # @param [Hash] value The meta information hash.
@@ -90,16 +93,16 @@ module JSONAPI
90
93
  # meta authorization_needed: true
91
94
  # end
92
95
  def link(name, &block)
93
- @_links[name] = Link.as_jsonapi(@_param_hash, &block)
96
+ @_links[name] = Link.as_jsonapi(@_exposures, &block)
94
97
  end
95
98
 
96
99
  private
97
100
 
98
101
  # @api private
99
- def _resources_for(models, resource_class)
102
+ def _resources_for(objects, resource_class)
100
103
  resource_class ||= @_resource_inferer
101
104
 
102
- ResourceBuilder.build(models, @_param_hash, resource_class)
105
+ ResourceBuilder.build(objects, @_exposures, resource_class)
103
106
  end
104
107
  end
105
108
  end
@@ -1,14 +1,15 @@
1
1
  require 'jsonapi/renderer'
2
+ require 'jsonapi/serializable/resource_builder'
2
3
 
3
4
  module JSONAPI
4
5
  module Serializable
5
6
  class Renderer
6
- def self.render(resources, options)
7
- new(resources, options).render
7
+ def self.render(objects, options)
8
+ new(objects, options).render
8
9
  end
9
10
 
10
- def initialize(resources, options)
11
- @resources = resources
11
+ def initialize(objects, options)
12
+ @objects = objects
12
13
  @options = options.dup
13
14
  @klass = @options.delete(:class)
14
15
  @namespace = @options.delete(:namespace)
@@ -29,7 +30,7 @@ module JSONAPI
29
30
 
30
31
  def jsonapi_resources
31
32
  toplevel_inferer = @klass || @inferer
32
- JSONAPI::Serializable::ResourceBuilder.build(@resources,
33
+ JSONAPI::Serializable::ResourceBuilder.build(@objects,
33
34
  @exposures,
34
35
  toplevel_inferer)
35
36
  end
@@ -1,106 +1,12 @@
1
- require 'jsonapi/serializable/link'
2
- require 'jsonapi/serializable/relationship'
1
+ require 'jsonapi/serializable/abstract_resource'
3
2
  require 'jsonapi/serializable/resource_dsl'
4
3
 
5
4
  module JSONAPI
6
5
  module Serializable
7
- class Resource
6
+ class Resource < AbstractResource
8
7
  include ResourceDSL
9
8
 
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.type_val = type_val
22
- klass.type_block = type_block
23
- klass.id_block = id_block
24
- klass.meta_val = meta_val
25
- klass.meta_block = meta_block
26
- klass.attribute_blocks = attribute_blocks.dup
27
- klass.relationship_blocks = relationship_blocks.dup
28
- klass.link_blocks = link_blocks.dup
29
- end
30
-
31
- def initialize(param_hash = {})
32
- @_param_hash = param_hash
33
- param_hash.each { |k, v| instance_variable_set("@#{k}", v) }
34
- end
35
-
36
- def as_jsonapi(params = {})
37
- return nil if nil?
38
-
39
- {}.tap do |hash|
40
- hash[:id] = jsonapi_id
41
- hash[:type] = jsonapi_type
42
- requested_attrs = params[:fields] || self.class.attribute_blocks.keys
43
- attrs = attributes(requested_attrs)
44
- hash[:attributes] = attrs if attrs.any?
45
- requested_rels = params[:fields] ||
46
- self.class.relationship_blocks.keys
47
- rels = relationships(requested_rels, params[:include] || [])
48
- hash[:relationships] = rels if rels.any?
49
- hash[:links] = links if links.any?
50
- hash[:meta] = meta unless meta.nil?
51
- end
52
- end
53
-
54
- def jsonapi_type
55
- @_type ||= self.class.type_val || instance_eval(&self.class.type_block)
56
- end
57
-
58
- def jsonapi_id
59
- @_id ||= instance_eval(&self.class.id_block)
60
- end
61
-
62
- def jsonapi_related(include)
63
- @_relationships
64
- .select { |k, _| include.include?(k) }
65
- .each_with_object({}) { |(k, v), h| h[k] = Array(v.data) }
66
- end
67
-
68
- private
69
-
70
- def attributes(fields)
71
- @_attributes ||= {}
72
- self.class.attribute_blocks
73
- .select { |k, _| !@_attributes.key?(k) && fields.include?(k) }
74
- .each { |k, v| @_attributes[k] = instance_eval(&v) }
75
- @_attributes.select { |k, _| fields.include?(k) }
76
- end
77
-
78
- def relationships(fields, include)
79
- @_relationships ||= self.class.relationship_blocks
80
- .each_with_object({}) do |(k, v), h|
81
- h[k] = Relationship.new(@_param_hash, &v)
82
- end
83
- @_relationships
84
- .select { |k, _| fields.include?(k) }
85
- .each_with_object({}) do |(k, v), h|
86
- h[k] = v.as_jsonapi(include.include?(k))
87
- end
88
- end
89
-
90
- def meta
91
- @_meta ||=
92
- if self.class.meta_val
93
- self.class.meta_val
94
- elsif self.class.meta_block
95
- instance_eval(&self.class.meta_block)
96
- end
97
- end
98
-
99
- def links
100
- @_links ||= self.class.link_blocks.each_with_object({}) do |(k, v), h|
101
- h[k] = Link.as_jsonapi(@_param_hash, &v)
102
- end
103
- end
9
+ id { @object.public_send(:id).to_s }
104
10
  end
105
11
  end
106
12
  end
@@ -1,8 +1,8 @@
1
1
  module JSONAPI
2
2
  module Serializable
3
3
  class ResourceBuilder
4
- DEFAULT_RESOURCE_INFERER = lambda do |model_klass_name|
5
- names = model_klass_name.split('::'.freeze)
4
+ DEFAULT_RESOURCE_INFERER = lambda do |object_klass_name|
5
+ names = object_klass_name.split('::'.freeze)
6
6
  klass_name = names.pop
7
7
  namespace = names.join('::'.freeze)
8
8
 
@@ -14,20 +14,21 @@ module JSONAPI
14
14
  Object.const_get(klass_name)
15
15
  end
16
16
 
17
- def self.build(models, expose, klass)
18
- return models if models.nil? ||
19
- Array(models).first.respond_to?(:as_jsonapi)
17
+ def self.build(objects, expose, klass)
18
+ return objects if objects.nil? ||
19
+ Array(objects).first.respond_to?(:as_jsonapi)
20
20
 
21
- resources =
22
- Array(models).map { |model| new(model, expose, klass).resource }
23
-
24
- models.respond_to?(:each) ? resources : resources.first
21
+ if objects.respond_to?(:each)
22
+ objects.map { |obj| new(obj, expose, klass).resource }
23
+ else
24
+ new(objects, expose, klass).resource
25
+ end
25
26
  end
26
27
 
27
28
  attr_reader :resource
28
29
 
29
- def initialize(model, expose, klass)
30
- @model = model
30
+ def initialize(object, expose, klass)
31
+ @object = object
31
32
  @expose = expose || {}
32
33
  @klass = klass
33
34
  @resource = serializable_class.new(serializable_params)
@@ -37,18 +38,18 @@ module JSONAPI
37
38
  private
38
39
 
39
40
  def serializable_params
40
- @expose.merge(model: @model)
41
+ @expose.merge(object: @object)
41
42
  end
42
43
 
43
44
  # rubocop:disable Metrics/MethodLength
44
45
  def serializable_class
45
46
  klass =
46
47
  if @klass.respond_to?(:call)
47
- @klass.call(@model.class.name)
48
+ @klass.call(@object.class.name)
48
49
  elsif @klass.is_a?(Hash)
49
- @klass[@model.class.name.to_sym]
50
+ @klass[@object.class.name.to_sym]
50
51
  elsif @klass.nil?
51
- DEFAULT_RESOURCE_INFERER.call(@model.class.name)
52
+ DEFAULT_RESOURCE_INFERER.call(@object.class.name)
52
53
  else
53
54
  @klass
54
55
  end
@@ -6,124 +6,27 @@ module JSONAPI
6
6
  end
7
7
 
8
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
9
+ def attribute(attr, &block)
10
+ block ||= proc { @object.public_send(attr) }
11
+ super(attr, &block)
25
12
  end
26
13
 
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
14
+ def attributes(*args)
15
+ args.each do |attr|
16
+ attribute(attr)
17
+ end
35
18
  end
36
19
 
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
20
+ def relationship(rel, resource_class = nil, &block)
21
+ rel_block = proc do
22
+ resources(resource_class) { @object.public_send(rel) }
23
+ instance_eval(&block) unless block.nil?
24
+ end
25
+ super(rel, &rel_block)
99
26
  end
100
27
  alias has_many relationship
101
28
  alias has_one relationship
102
29
  alias belongs_to relationship
103
-
104
- # Declare a link for this resource. The properties of the link are set
105
- # by providing a block in which the DSL methods of
106
- # +JSONAPI::Serializable::Link+ are called, or the value of the link
107
- # is returned directly.
108
- # @see JSONAPI::Serialiable::Link
109
- #
110
- # @param [Symbol] name The key of the link.
111
- # @yieldreturn [Hash, String, nil] The block to compute the value, if
112
- # any.
113
- #
114
- # @example
115
- # link(:self) do
116
- # "http://api.example.com/users/#{@user.id}"
117
- # end
118
- #
119
- # @example
120
- # link(:self) do
121
- # href "http://api.example.com/users/#{@user.id}"
122
- # meta is_self: true
123
- # end
124
- def link(name, &block)
125
- link_blocks[name] = block
126
- end
127
30
  end
128
31
  end
129
32
  end
@@ -1,4 +1,3 @@
1
1
  require 'jsonapi/serializable/error'
2
- require 'jsonapi/serializable/model'
3
2
  require 'jsonapi/serializable/resource'
4
3
  require 'jsonapi/serializable/renderer'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsonapi-serializable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1.beta3
4
+ version: 0.1.1.beta4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lucas Hosseini
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-11-15 00:00:00.000000000 Z
11
+ date: 2016-11-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jsonapi-renderer
@@ -60,11 +60,11 @@ extra_rdoc_files: []
60
60
  files:
61
61
  - README.md
62
62
  - lib/jsonapi/serializable.rb
63
+ - lib/jsonapi/serializable/abstract_resource.rb
64
+ - lib/jsonapi/serializable/abstract_resource_dsl.rb
63
65
  - lib/jsonapi/serializable/error.rb
64
66
  - lib/jsonapi/serializable/error_dsl.rb
65
67
  - lib/jsonapi/serializable/link.rb
66
- - lib/jsonapi/serializable/model.rb
67
- - lib/jsonapi/serializable/model_dsl.rb
68
68
  - lib/jsonapi/serializable/relationship.rb
69
69
  - lib/jsonapi/serializable/relationship_dsl.rb
70
70
  - lib/jsonapi/serializable/renderer.rb
@@ -1,12 +0,0 @@
1
- require 'jsonapi/serializable/model_dsl'
2
- require 'jsonapi/serializable/resource'
3
-
4
- module JSONAPI
5
- module Serializable
6
- class Model < Resource
7
- include ModelDSL
8
-
9
- id { @model.public_send(:id).to_s }
10
- end
11
- end
12
- end
@@ -1,27 +0,0 @@
1
- module JSONAPI
2
- module Serializable
3
- module ModelDSL
4
- def self.included(base)
5
- base.extend(ClassMethods)
6
- end
7
-
8
- module ClassMethods
9
- def attribute(attr, &block)
10
- block ||= proc { @model.public_send(attr) }
11
- super(attr, &block)
12
- end
13
-
14
- def relationship(rel, resource_class = nil, &block)
15
- rel_block = proc do
16
- data(resource_class) { @model.public_send(rel) }
17
- instance_eval(&block) unless block.nil?
18
- end
19
- super(rel, &rel_block)
20
- end
21
- alias has_many relationship
22
- alias has_one relationship
23
- alias belongs_to relationship
24
- end
25
- end
26
- end
27
- end