jsonapi-serializable 0.2.1 → 0.3.0

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: a2bfb6228e6b03c8bde3724af76fdbcedc097e0e
4
- data.tar.gz: 28e75ac8293502f8e2a617c79fff3f803ca172d7
3
+ metadata.gz: e06e5be75e2c53c3f257807493bb7c896a669241
4
+ data.tar.gz: b0c29ac61252006bc763a1e08567037cff3f9e49
5
5
  SHA512:
6
- metadata.gz: 909fa65c618b4507da112491cc7b84646c07146dd027c5ba52386509ed9ea090916b413e78940f3fd4dad8cc2c518d56bd727953ad871ef4b404205ba5c9f39d
7
- data.tar.gz: 8d924e246526821a8ab66d571054cdbe50b235c3d909e5c4c3ca935d6f57fac9b6221e3dcde1ce81eeebfcd32e99bf40bbc87e980a1113c0811bf7c76a32133a
6
+ metadata.gz: a3d7759a8a216e92a71e56f1324082a69b6f51ce69653136c39187b527a37dafd2dd17919c70b1389b48412938098af9d6f83fdfaa0167be257e09cb18b6c8fd
7
+ data.tar.gz: 333ba881f8e1799bd2db5a0ed8c92b86f5906530550f0a0e03de63d451d41d66b0674198e91c572d312aee21c9f02d6048480fddc615451373828f06f62b504f
@@ -35,12 +35,6 @@ module JSONAPI
35
35
  :link_blocks
36
36
  end
37
37
 
38
- def self.create(hash)
39
- hash.each_with_object(new) do |(k, v), e|
40
- e.instance_variable_set("@_#{k}", v)
41
- end
42
- end
43
-
44
38
  self.link_blocks = {}
45
39
 
46
40
  def self.inherited(klass)
@@ -1,38 +1,31 @@
1
- require 'jsonapi/serializable/resource_builder'
2
-
3
1
  module JSONAPI
4
2
  module Serializable
5
3
  class Relationship
6
4
  module DSL
7
5
  # Declare the related resources for this relationship.
8
6
  # @yieldreturn The related resources for this relationship.
9
- # If it is nil, an object implementing the Serializable::Resource
10
- # interface, an empty array, or an array of objects implementing the
11
- # Serializable::Resource interface, then it is used as is.
12
- # Otherwise an appropriate Serializable::Resource subclass is inferred
13
- # from the object(s)' namespace/class, the `class` relationship
14
- # option, and the @_resource_builder.
15
- #
16
- # @example
17
- # data do
18
- # @user.posts.map { |p| PostResource.new(post: p) }
19
- # end
20
- #
21
- # @example
22
- # data do
23
- # @post.author && UserResource.new(user: @user.author)
24
- # end
25
7
  #
26
8
  # @example
27
9
  # data do
28
- # @user.posts
10
+ # @object.posts
29
11
  # end
30
12
  # end
31
13
  def data
32
14
  # NOTE(beauby): Lazify computation since it is only needed when
33
15
  # the corresponding relationship is included.
34
16
  @_resources_block = proc do
35
- @_resource_builder.build(yield, @_exposures, @_options[:class])
17
+ resources = yield
18
+ if resources.nil?
19
+ nil
20
+ elsif resources.respond_to?(:to_ary)
21
+ Array(resources).map do |obj|
22
+ @_class[obj.class.name.to_sym]
23
+ .new(@_exposures.merge(object: obj))
24
+ end
25
+ else
26
+ @_class[resources.class.name.to_sym]
27
+ .new(@_exposures.merge(object: resources))
28
+ end
36
29
  end
37
30
  end
38
31
 
@@ -1,9 +1,8 @@
1
1
  require 'jsonapi/renderer'
2
- require 'jsonapi/serializable/resource_builder'
3
2
 
4
3
  module JSONAPI
5
4
  module Serializable
6
- class SuccessRenderer
5
+ class Renderer
7
6
  def initialize(renderer = JSONAPI::Renderer.new)
8
7
  @renderer = renderer
9
8
  end
@@ -11,13 +10,9 @@ module JSONAPI
11
10
  # Serialize resources into a JSON API document.
12
11
  #
13
12
  # @param resources [nil,Object,Array]
14
- # @param options [Hash]@see JSONAPI.render
15
- # @option class [Class,Symbol,String,Hash{Symbol,String=>Class,Symbol,String}]
16
- # The serializable resource class(es) to use for the primary resources.
17
- # @option namespace [String] The namespace in which to look for
18
- # serializable resource classes.
19
- # @option inferrer [#call] The callable used for inferring a serializable
20
- # resource class name from a resource class name.
13
+ # @param options [Hash] @see JSONAPI.render
14
+ # @option class [Hash{Symbol=>Class}] A map specifying for each type
15
+ # the corresponding serializable resource class.
21
16
  # @option expose [Hash] The exposures made available in serializable
22
17
  # resource class instances as instance variables.
23
18
  # @return [Hash]
@@ -27,7 +22,7 @@ module JSONAPI
27
22
  # # => { data: nil }
28
23
  #
29
24
  # @example
30
- # renderer.render(user)
25
+ # renderer.render(user, class: { User: SerializableUser })
31
26
  # # => {
32
27
  # data: {
33
28
  # type: 'users',
@@ -38,52 +33,60 @@ module JSONAPI
38
33
  # }
39
34
  #
40
35
  # @example
41
- # renderer.render([user1, user2])
36
+ # renderer.render([user1, user2], class: { User: SerializableUser })
42
37
  # # => { data: [{ type: 'users', id: 'foo', ... }, ...] }
43
38
  def render(resources, options = {})
44
39
  options = options.dup
45
- klass = options.delete(:class)
46
- namespace = options.delete(:namespace)
47
- inferrer = options.delete(:inferrer) || namespace_inferrer(namespace)
48
- expose = options.delete(:expose) || {}
49
- resource_builder = JSONAPI::Serializable::ResourceBuilder.new(inferrer)
50
- exposures = expose.merge(_resource_builder: resource_builder)
40
+ klass = options.delete(:class) || {}
41
+ exposures = options.delete(:expose) || {}
42
+ exposures = exposures.merge(_class: klass)
51
43
 
52
- resources = resource_builder.build(resources, exposures, klass)
44
+ resources = build_resources(resources, exposures, klass)
53
45
 
54
46
  @renderer.render(options.merge(data: resources))
55
47
  end
56
48
 
57
- private
58
-
59
- # @api private
60
- def namespace_inferrer(namespace)
61
- proc do |class_name|
62
- names = class_name.split('::')
63
- klass = names.pop
64
- [namespace, *names, "Serializable#{klass}"].reject(&:nil?).join('::')
65
- end
66
- end
67
- end
68
-
69
- class ErrorsRenderer
70
- def initialize(renderer = JSONAPI::Renderer.new)
71
- @renderer = renderer
72
- end
73
-
74
49
  # Serialize errors into a JSON API document.
75
50
  #
76
51
  # @param errors [Array]
77
52
  # @param options [Hash] @see JSONAPI.render
53
+ # @option class [Hash{Symbol=>Class}] A map specifying for each type
54
+ # the corresponding serializable error class.
55
+ # @option expose [Hash] The exposures made available in serializable
56
+ # error class instances as instance variables.
78
57
  # @return [Hash]
79
58
  #
80
59
  # @example
81
60
  # error = JSONAPI::Serializable::Error.create(id: 'foo', title: 'bar')
82
61
  # renderer.render([error])
83
62
  # # => { errors: [{ id: 'foo', title: 'bar' }] }
84
- def render(errors, options = {})
63
+ def render_errors(errors, options = {})
64
+ options = options.dup
65
+ klass = options.delete(:class) || {}
66
+ exposures = options.delete(:expose) || {}
67
+
68
+ errors = errors.map { |e| _build(e, exposures, klass) }
69
+
85
70
  @renderer.render(options.merge(errors: errors))
86
71
  end
72
+
73
+ private
74
+
75
+ # @api private
76
+ def build_resources(resources, exposures, klass)
77
+ if resources.nil?
78
+ nil
79
+ elsif resources.respond_to?(:to_ary)
80
+ Array(resources).map { |obj| _build(obj, exposures, klass) }
81
+ else
82
+ _build(resources, exposures, klass)
83
+ end
84
+ end
85
+
86
+ # @api private
87
+ def _build(object, exposures, klass)
88
+ klass[object.class.name.to_sym].new(exposures.merge(object: object))
89
+ end
87
90
  end
88
91
  end
89
92
  end
@@ -1,5 +1,3 @@
1
- require 'jsonapi/serializable/resource_builder'
2
-
3
1
  require 'jsonapi/serializable/resource/dsl'
4
2
 
5
3
  require 'jsonapi/serializable/link'
@@ -18,9 +16,7 @@ module JSONAPI
18
16
 
19
17
  # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
20
18
  def initialize(exposures = {})
21
- @_exposures = {
22
- _resource_builder: JSONAPI::Serializable::ResourceBuilder.new
23
- }.merge(exposures)
19
+ @_exposures = exposures
24
20
  @_exposures.each { |k, v| instance_variable_set("@#{k}", v) }
25
21
 
26
22
  @_id = instance_eval(&self.class.id_block).to_s
@@ -81,6 +77,12 @@ module JSONAPI
81
77
  .each_with_object({}) { |(k, v), h| h[k] = v.related_resources }
82
78
  end
83
79
 
80
+ def jsonapi_cache_key(options)
81
+ "#{jsonapi_type} - #{jsonapi_id}" \
82
+ "- #{options[:include].to_a.sort}" \
83
+ "- #{(options[:fields] || Set.new).to_a.sort}"
84
+ end
85
+
84
86
  private
85
87
 
86
88
  # @api private
@@ -29,14 +29,14 @@ module JSONAPI
29
29
  attr_accessor :link_condition_blocks
30
30
  end
31
31
  self.field_condition_blocks ||= {}
32
- self.link_condition_blocks ||= {}
32
+ self.link_condition_blocks ||= {}
33
33
  end
34
34
  end
35
35
 
36
36
  def inherited(klass)
37
37
  super
38
38
  klass.field_condition_blocks = field_condition_blocks.dup
39
- klass.link_condition_blocks = link_condition_blocks.dup
39
+ klass.link_condition_blocks = link_condition_blocks.dup
40
40
  end
41
41
 
42
42
  # Handle the `if` and `unless` options for attributes.
@@ -34,7 +34,7 @@ module JSONAPI
34
34
  # @yieldreturn [String] The id of the resource.
35
35
  #
36
36
  # @example
37
- # id { @user.id.to_s }
37
+ # id { @object.id.to_s }
38
38
  def id(&block)
39
39
  self.id_block = block
40
40
  end
@@ -93,12 +93,12 @@ module JSONAPI
93
93
  #
94
94
  # @example
95
95
  # link(:self) do
96
- # "http://api.example.com/users/#{@user.id}"
96
+ # "http://api.example.com/users/#{@object.id}"
97
97
  # end
98
98
  #
99
99
  # @example
100
100
  # link(:self) do
101
- # href "http://api.example.com/users/#{@user.id}"
101
+ # href "http://api.example.com/users/#{@object.id}"
102
102
  # meta is_self: true
103
103
  # end
104
104
  def link(name, &block)
@@ -133,26 +133,21 @@ module JSONAPI
133
133
  # @param [Hash] options The options for the relationship.
134
134
  #
135
135
  # @example
136
- # relationship :posts do
137
- # resources { @user.posts.map { |p| PostResource.new(post: p) } }
138
- # end
139
- #
140
- # @example
141
136
  # relationship :author do
142
- # resources do
143
- # @post.author && UserResource.new(user: @post.author)
144
- # end
145
137
  # data do
146
- # { type: 'users', id: @post.author_id }
138
+ # @object.author
139
+ # end
140
+ # linkage do
141
+ # { type: 'users', id: @object.author_id }
147
142
  # end
148
143
  # link(:self) do
149
- # "http://api.example.com/posts/#{@post.id}/relationships/author"
144
+ # "http://api.example.com/posts/#{@object.id}/relationships/author"
150
145
  # end
151
146
  # link(:related) do
152
- # "http://api.example.com/posts/#{@post.id}/author"
147
+ # "http://api.example.com/posts/#{@object.id}/author"
153
148
  # end
154
149
  # meta do
155
- # { author_online: @post.author.online? }
150
+ # { author_online: @object.author.online? }
156
151
  # end
157
152
  # end
158
153
  def relationship(name, options = {}, &block)
@@ -164,8 +159,8 @@ module JSONAPI
164
159
  relationship_options[name.to_sym] = options
165
160
  end
166
161
 
167
- alias has_many relationship
168
- alias has_one relationship
162
+ alias has_many relationship
163
+ alias has_one relationship
169
164
  alias belongs_to relationship
170
165
  end
171
166
  end
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.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lucas Hosseini
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-07-13 00:00:00.000000000 Z
11
+ date: 2017-09-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jsonapi-renderer
@@ -16,20 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.1'
20
- - - ">="
21
- - !ruby/object:Gem::Version
22
- version: 0.1.3
19
+ version: 0.2.0
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
24
  - - "~>"
28
25
  - !ruby/object:Gem::Version
29
- version: '0.1'
30
- - - ">="
31
- - !ruby/object:Gem::Version
32
- version: 0.1.3
26
+ version: 0.2.0
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: rake
35
29
  requirement: !ruby/object:Gem::Requirement
@@ -59,19 +53,33 @@ dependencies:
59
53
  - !ruby/object:Gem::Version
60
54
  version: '3.5'
61
55
  - !ruby/object:Gem::Dependency
62
- name: codecov
56
+ name: simplecov
63
57
  requirement: !ruby/object:Gem::Requirement
64
58
  requirements:
65
- - - "~>"
59
+ - - ">="
66
60
  - !ruby/object:Gem::Version
67
- version: '0.1'
61
+ version: '0'
68
62
  type: :development
69
63
  prerelease: false
70
64
  version_requirements: !ruby/object:Gem::Requirement
71
65
  requirements:
72
- - - "~>"
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: benchmark-ips
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
73
81
  - !ruby/object:Gem::Version
74
- version: '0.1'
82
+ version: '0'
75
83
  description: Powerful DSL for building resource classes - efficient and flexible rendering.
76
84
  email: lucas.hosseini@gmail.com
77
85
  executables: []
@@ -90,9 +98,6 @@ files:
90
98
  - lib/jsonapi/serializable/resource/conditional_fields.rb
91
99
  - lib/jsonapi/serializable/resource/dsl.rb
92
100
  - lib/jsonapi/serializable/resource/key_format.rb
93
- - lib/jsonapi/serializable/resource/relationship.rb
94
- - lib/jsonapi/serializable/resource/relationship/dsl.rb
95
- - lib/jsonapi/serializable/resource_builder.rb
96
101
  homepage: https://github.com/jsonapi-rb/jsonapi-serializable
97
102
  licenses:
98
103
  - MIT
@@ -113,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
113
118
  version: '0'
114
119
  requirements: []
115
120
  rubyforge_project:
116
- rubygems_version: 2.6.12
121
+ rubygems_version: 2.6.13
117
122
  signing_key:
118
123
  specification_version: 4
119
124
  summary: Conveniently serialize JSON API resources.
@@ -1,62 +0,0 @@
1
- require 'jsonapi/serializable/link'
2
- require 'jsonapi/serializable/resource/relationship/dsl'
3
-
4
- module JSONAPI
5
- module Serializable
6
- class Resource
7
- class Relationship
8
- include DSL
9
-
10
- def initialize(exposures = {}, options = {}, &block)
11
- exposures.each { |k, v| instance_variable_set("@#{k}", v) }
12
- @_exposures = exposures
13
- @_options = options
14
- @_links = {}
15
- instance_eval(&block)
16
- end
17
-
18
- def as_jsonapi(included)
19
- {}.tap do |hash|
20
- hash[:links] = @_links if @_links.any?
21
- hash[:data] = linkage_data if included || @_include_linkage
22
- hash[:meta] = @_meta unless @_meta.nil?
23
- hash[:meta] = { included: false } if hash.empty?
24
- end
25
- end
26
-
27
- # @api private
28
- def related_resources
29
- @_related_resources ||= Array(resources)
30
- end
31
-
32
- # @api private
33
- def links
34
- @_links
35
- end
36
-
37
- # @api private
38
- def meta
39
- @_meta
40
- end
41
-
42
- # @api private
43
- def resources
44
- @_resources ||= @_resources_block.call
45
- end
46
-
47
- private
48
-
49
- # @api private
50
- def linkage_data
51
- return @_linkage_block.call if @_linkage_block
52
-
53
- linkage_data = related_resources.map do |res|
54
- { type: res.jsonapi_type, id: res.jsonapi_id }
55
- end
56
-
57
- resources.respond_to?(:each) ? linkage_data : linkage_data.first
58
- end
59
- end
60
- end
61
- end
62
- end
@@ -1,103 +0,0 @@
1
- require 'jsonapi/serializable/resource_builder'
2
-
3
- module JSONAPI
4
- module Serializable
5
- class Resource
6
- class Relationship
7
- module DSL
8
- # Declare the related resources for this relationship.
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
14
- # inferred from the object(s)' namespace/class, the `class`
15
- # relationship option, and the @_resource_builder.
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
- def data
33
- # NOTE(beauby): Lazify computation since it is only needed when
34
- # the corresponding relationship is included.
35
- @_resources_block = proc do
36
- @_resource_builder.build(yield, @_exposures, @_options[:class])
37
- end
38
- end
39
-
40
- # @overload linkage(options = {}, &block)
41
- # Explicitly declare linkage data.
42
- # @yieldreturn The resource linkage.
43
- #
44
- # @example
45
- # linkage do
46
- # @object.posts.map { |p| { id: p.id.to_s, type: 'posts' } }
47
- # end
48
- #
49
- # @overload linkage(options = {})
50
- # Forces standard linkage even if relationship not included.
51
- #
52
- # @example
53
- # linkage always: true
54
- def linkage(always: false, &block)
55
- @_include_linkage = always
56
- @_linkage_block = block
57
- end
58
-
59
- # @overload meta(value)
60
- # Declare the meta information for this relationship.
61
- # @param [Hash] value The meta information hash.
62
- #
63
- # @example
64
- # meta paginated: true
65
- #
66
- # @overload meta(&block)
67
- # Declare the meta information for this relationship.
68
- # @yieldreturn [Hash] The meta information hash.
69
- #
70
- # @example
71
- # meta do
72
- # { paginated: true }
73
- # end
74
- def meta(value = nil)
75
- @_meta = value || yield
76
- end
77
-
78
- # Declare a link for this relationship. The properties of the link are set
79
- # by providing a block in which the DSL methods of
80
- # +JSONAPI::Serializable::Link+ are called.
81
- # @see JSONAPI::Serialiable::Link
82
- #
83
- # @param [Symbol] name The key of the link.
84
- # @yieldreturn [Hash, String, nil] The block to compute the value, if any.
85
- #
86
- # @example
87
- # link(:self) do
88
- # "http://api.example.com/users/#{@user.id}/relationships/posts"
89
- # end
90
- #
91
- # @example
92
- # link(:related) do
93
- # href "http://api.example.com/users/#{@user.id}/posts"
94
- # meta authorization_needed: true
95
- # end
96
- def link(name, &block)
97
- @_links[name] = Link.as_jsonapi(@_exposures, &block)
98
- end
99
- end
100
- end
101
- end
102
- end
103
- end
@@ -1,56 +0,0 @@
1
- module JSONAPI
2
- module Serializable
3
- class ResourceBuilder
4
- # @api private
5
- def initialize(inferrer = nil)
6
- @inferrer = inferrer
7
- @lookup_cache = {}
8
-
9
- freeze
10
- end
11
-
12
- # @api private
13
- def build(objects, expose, klass = nil)
14
- return objects if objects.nil? ||
15
- Array(objects).first.respond_to?(:as_jsonapi)
16
-
17
- if objects.respond_to?(:to_ary)
18
- Array(objects).map { |obj| _build(obj, expose, klass) }
19
- else
20
- _build(objects, expose, klass)
21
- end
22
- end
23
-
24
- private
25
-
26
- # @api private
27
- def _build(object, expose, klass)
28
- serializable_class(object.class.name, klass)
29
- .new(expose.merge(object: object))
30
- end
31
-
32
- # @api private
33
- def serializable_class(object_class_name, klass)
34
- klass = klass[object_class_name.to_sym] if klass.is_a?(Hash)
35
-
36
- @lookup_cache[[object_class_name, klass.to_s]] ||=
37
- reify_class(klass || @inferrer.call(object_class_name))
38
- end
39
-
40
- # @api private
41
- def reify_class(klass)
42
- if klass.is_a?(Class)
43
- klass
44
- elsif klass.is_a?(String) || klass.is_a?(Symbol)
45
- begin
46
- Object.const_get(klass)
47
- rescue NameError
48
- raise NameError, "Undefined serializable class #{klass}"
49
- end
50
- else
51
- raise ArgumentError, "Invalid serializable class #{klass}"
52
- end
53
- end
54
- end
55
- end
56
- end