caprese 0.3.27 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -1
- data/caprese.gemspec +2 -2
- data/lib/caprese/adapter/json_api/relationship.rb +32 -11
- data/lib/caprese/adapter/json_api/resource_identifier.rb +8 -0
- data/lib/caprese/adapter/json_api.rb +11 -10
- data/lib/caprese/controller/concerns/persistence.rb +7 -3
- data/lib/caprese/serializer/concerns/links.rb +1 -81
- data/lib/caprese/serializer/concerns/lookup.rb +4 -4
- data/lib/caprese/serializer/concerns/relationships.rb +102 -0
- data/lib/caprese/version.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2166fc2b8abb1545a666d9d9e20bbffcb20666d0
|
4
|
+
data.tar.gz: dac348b43650955f6c16b8321e283c8d437dbd9c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d416cf781dc717d62c82be61634c7c0eb09752088284b65c2787958733d4cd2f784937fd235ce363098ece7d66de333608e368f460ba25160d24385d28412f54
|
7
|
+
data.tar.gz: 7f55dac36dbf0a11ce6feb3f083e924fb18005ae5d01b5cae807b618f0d28193b13fdd5f6d360123f75d5861701320589f24f10e07344f9699a449c07b8d8f13
|
data/CHANGELOG.md
CHANGED
@@ -152,4 +152,16 @@
|
|
152
152
|
|
153
153
|
## 0.3.27
|
154
154
|
|
155
|
-
* Fix bug in JSON API adapter that inappropriately aliases relationships
|
155
|
+
* Fix bug in JSON API adapter that inappropriately aliases relationships
|
156
|
+
|
157
|
+
## 0.4.0
|
158
|
+
|
159
|
+
* Modifies behavior of `config.optimize_relationships`
|
160
|
+
* Original behavior: Only sends `links` of relationships (no `data`) except those that are in `includes`
|
161
|
+
* New behavior: Relationship is omitted from response entirely except those that are in `includes`
|
162
|
+
* **Breaking:** Modifies behavior of serializer relationships
|
163
|
+
* Original behavior: Use the serializer corresponding to the class of objects for the relationships
|
164
|
+
* Example: `has_many :productos => ProductSerializer`
|
165
|
+
* New behavior: Relationships in serializers use the name of the relationship as an assumption about the serializer for that relationship
|
166
|
+
* Example: `has_many :productos => ProductoSerializer`
|
167
|
+
* Can override by passing any serializer: `has_many :productos, serializer: ProductSerializer`
|
data/caprese.gemspec
CHANGED
@@ -21,9 +21,9 @@ Gem::Specification.new do |spec|
|
|
21
21
|
|
22
22
|
spec.required_ruby_version = '>= 2.1'
|
23
23
|
|
24
|
-
spec.add_dependency 'active_model_serializers', '0.10.
|
24
|
+
spec.add_dependency 'active_model_serializers', '0.10.6'
|
25
25
|
spec.add_dependency 'kaminari', '~> 0.16.0'
|
26
|
-
spec.add_dependency 'rails', '
|
26
|
+
spec.add_dependency 'rails', '~> 4.2.0'
|
27
27
|
|
28
28
|
spec.add_development_dependency 'bundler'
|
29
29
|
spec.add_development_dependency 'factory_girl'
|
@@ -6,19 +6,16 @@ module Caprese
|
|
6
6
|
# {http://jsonapi.org/format/#document-links Document Links}
|
7
7
|
# {http://jsonapi.org/format/#document-resource-object-linkage Document Resource Relationship Linkage}
|
8
8
|
# {http://jsonapi.org/format/#document-meta Document Meta}
|
9
|
-
def initialize(parent_serializer, serializable_resource_options, association
|
9
|
+
def initialize(parent_serializer, serializable_resource_options, association)
|
10
10
|
@parent_serializer = parent_serializer
|
11
11
|
@association = association
|
12
12
|
@serializable_resource_options = serializable_resource_options
|
13
|
-
@included_associations = included_associations
|
14
13
|
end
|
15
14
|
|
16
15
|
def as_json
|
17
16
|
hash = {}
|
18
17
|
|
19
|
-
|
20
|
-
if association.options[:include_data] &&
|
21
|
-
(!Caprese.config.optimize_relationships || included_associations[association.name])
|
18
|
+
if association.include_data?
|
22
19
|
hash[:data] = data_for(association)
|
23
20
|
end
|
24
21
|
|
@@ -33,18 +30,42 @@ module Caprese
|
|
33
30
|
|
34
31
|
protected
|
35
32
|
|
36
|
-
attr_reader :parent_serializer, :serializable_resource_options, :association
|
33
|
+
attr_reader :parent_serializer, :serializable_resource_options, :association
|
37
34
|
|
38
35
|
private
|
39
36
|
|
37
|
+
# TODO(BF): Avoid db hit on belong_to_ releationship by using foreign_key on self
|
40
38
|
def data_for(association)
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
39
|
+
if association.collection?
|
40
|
+
data_for_many(association)
|
41
|
+
else
|
42
|
+
data_for_one(association)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def data_for_one(association)
|
47
|
+
# TODO(BF): Process relationship without evaluating lazy_association
|
48
|
+
serializer = association.lazy_association.serializer
|
49
|
+
if (virtual_value = association.virtual_value)
|
45
50
|
virtual_value
|
46
|
-
elsif serializer &&
|
51
|
+
elsif serializer && association.object
|
47
52
|
ResourceIdentifier.new(serializer, serializable_resource_options).as_json
|
53
|
+
else
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def data_for_many(association)
|
59
|
+
# TODO(BF): Process relationship without evaluating lazy_association
|
60
|
+
collection_serializer = association.lazy_association.serializer
|
61
|
+
if collection_serializer.respond_to?(:each)
|
62
|
+
collection_serializer.map do |serializer|
|
63
|
+
ResourceIdentifier.new(serializer, serializable_resource_options).as_json
|
64
|
+
end
|
65
|
+
elsif (virtual_value = association.virtual_value)
|
66
|
+
virtual_value
|
67
|
+
else
|
68
|
+
[]
|
48
69
|
end
|
49
70
|
end
|
50
71
|
|
@@ -16,6 +16,14 @@ module Caprese
|
|
16
16
|
JsonApi.send(:transform_key_casing!, raw_type, transform_options)
|
17
17
|
end
|
18
18
|
|
19
|
+
def self.for_type_with_id(type, id, options)
|
20
|
+
return nil if id.blank?
|
21
|
+
{
|
22
|
+
id: id.to_s,
|
23
|
+
type: type_for(:no_class_needed, type, options)
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
19
27
|
# {http://jsonapi.org/format/#document-resource-identifier-objects Resource Identifier Objects}
|
20
28
|
def initialize(serializer, options)
|
21
29
|
@id = id_for(serializer)
|
@@ -244,7 +244,7 @@ module Caprese
|
|
244
244
|
|
245
245
|
def process_relationships(serializer, include_directive)
|
246
246
|
serializer.associations(include_directive).each do |association|
|
247
|
-
process_relationship(association.serializer, include_directive[association.key])
|
247
|
+
process_relationship(association.lazy_association.serializer, include_directive[association.key])
|
248
248
|
end
|
249
249
|
end
|
250
250
|
|
@@ -290,8 +290,14 @@ module Caprese
|
|
290
290
|
resource_object
|
291
291
|
end
|
292
292
|
|
293
|
-
requested_associations = fieldset.fields_for(resource_object[:type])
|
294
|
-
|
293
|
+
requested_associations = fieldset.fields_for(resource_object[:type])
|
294
|
+
requested_associations ||= included_associations.to_string if Caprese.config.optimize_relationships
|
295
|
+
requested_associations ||= '*'
|
296
|
+
requested_associations = JSONAPI::IncludeDirective.new(
|
297
|
+
requested_associations,
|
298
|
+
allow_wildcard: true
|
299
|
+
)
|
300
|
+
relationships = relationships_for(serializer, requested_associations)
|
295
301
|
resource_object[:relationships] = relationships if relationships.any?
|
296
302
|
|
297
303
|
links = links_for(serializer)
|
@@ -419,17 +425,12 @@ module Caprese
|
|
419
425
|
# id: 'required-id',
|
420
426
|
# meta: meta
|
421
427
|
# }.reject! {|_,v| v.nil? }
|
422
|
-
def relationships_for(serializer,
|
423
|
-
include_directive = JSONAPI::IncludeDirective.new(
|
424
|
-
requested_associations,
|
425
|
-
allow_wildcard: true
|
426
|
-
)
|
428
|
+
def relationships_for(serializer, include_directive)
|
427
429
|
serializer.associations(include_directive).each_with_object({}) do |association, hash|
|
428
430
|
hash[association.key] = Relationship.new(
|
429
431
|
serializer,
|
430
432
|
instance_options,
|
431
|
-
association
|
432
|
-
included_associations
|
433
|
+
association
|
433
434
|
).as_json
|
434
435
|
end
|
435
436
|
end
|
@@ -18,8 +18,12 @@ module Caprese
|
|
18
18
|
)
|
19
19
|
end
|
20
20
|
|
21
|
-
rescue_from ActiveRecord::RecordInvalid do |e|
|
22
|
-
|
21
|
+
rescue_from ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved do |e|
|
22
|
+
if e.record
|
23
|
+
rescue_with_handler RecordInvalidError.new(e.record, engaged_field_aliases)
|
24
|
+
else
|
25
|
+
rescue_with_handler ActionForbiddenError.new
|
26
|
+
end
|
23
27
|
end
|
24
28
|
|
25
29
|
rescue_from ActiveRecord::RecordNotDestroyed do |e|
|
@@ -291,7 +295,7 @@ module Caprese
|
|
291
295
|
end
|
292
296
|
|
293
297
|
ref
|
294
|
-
|
298
|
+
elsif !relationship_data.has_key?(:data)
|
295
299
|
raise Error.new(
|
296
300
|
field: "/data/relationships/#{relationship_name}/data",
|
297
301
|
code: :blank
|
@@ -12,7 +12,7 @@ module Caprese
|
|
12
12
|
# object = Order<@token='asd27h'>
|
13
13
|
# links = { self: '/api/v1/orders/asd27hß' }
|
14
14
|
link :self do
|
15
|
-
if(url = serializer.class.route_for(object))
|
15
|
+
if object.persisted? && (url = serializer.class.route_for(object))
|
16
16
|
serializer.url_helpers.send(
|
17
17
|
url,
|
18
18
|
object.read_attribute(Caprese.config.resource_primary_key),
|
@@ -30,86 +30,6 @@ module Caprese
|
|
30
30
|
fail 'Caprese requires that config.default_url_options[:host] be set when rendering links.'
|
31
31
|
end
|
32
32
|
end
|
33
|
-
|
34
|
-
# Overridden so we can define relationship links without any code in a specific
|
35
|
-
# serializer
|
36
|
-
def has_many(name, options = {}, &block)
|
37
|
-
super name, options, &build_association_block(name)
|
38
|
-
|
39
|
-
define_method name do
|
40
|
-
self.relationship_scope(name, object.send(name))
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
# @see has_many
|
45
|
-
def has_one(name, options = {}, &block)
|
46
|
-
super name, options, &build_association_block(name)
|
47
|
-
end
|
48
|
-
|
49
|
-
# @see has_many
|
50
|
-
def belongs_to(name, options = {}, &block)
|
51
|
-
super name, options, &build_association_block(name)
|
52
|
-
end
|
53
|
-
|
54
|
-
private
|
55
|
-
|
56
|
-
# Builds a block that is passed into an association when it is defined in a specific serializer
|
57
|
-
# The block is run, and links are added to each association so when it is rendered in the
|
58
|
-
# `relationships` object of the `data` for record, it contains links to the particular association
|
59
|
-
#
|
60
|
-
# @example
|
61
|
-
# object = Order<@token=5, @product_id=10>
|
62
|
-
# reflection_name = 'product'
|
63
|
-
# # => {
|
64
|
-
# id: 'asd27h',
|
65
|
-
# type: 'orders',
|
66
|
-
# relationships: {
|
67
|
-
# product: {
|
68
|
-
# id: 'hy7sql',
|
69
|
-
# type: 'products',
|
70
|
-
# links: {
|
71
|
-
# self: '/api/v1/orders/asd27h/relationships/product',
|
72
|
-
# related: '/api/v1/orders/asd27h/product'
|
73
|
-
# }
|
74
|
-
# }
|
75
|
-
# }
|
76
|
-
# }
|
77
|
-
#
|
78
|
-
# @param [String] reflection_name the name of the relationship
|
79
|
-
# @return [Block] a block to build links for the relationship
|
80
|
-
def build_association_block(reflection_name)
|
81
|
-
primary_key = Caprese.config.resource_primary_key
|
82
|
-
|
83
|
-
reflection_name = reflection_name.to_sym
|
84
|
-
|
85
|
-
Proc.new do |serializer|
|
86
|
-
link :self do
|
87
|
-
url = "relationship_definition_#{serializer.version_name("#{serializer.unnamespace(object.class.name).underscore}_url")}"
|
88
|
-
if serializer.url_helpers.respond_to? url
|
89
|
-
serializer.url_helpers.send(
|
90
|
-
url,
|
91
|
-
id: object.read_attribute(primary_key),
|
92
|
-
relationship: reflection_name,
|
93
|
-
host: serializer.class.send(:caprese_default_url_options_host)
|
94
|
-
)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
link :related do
|
99
|
-
url = "relationship_data_#{serializer.version_name("#{serializer.unnamespace(object.class.name).underscore}_url")}"
|
100
|
-
if serializer.url_helpers.respond_to? url
|
101
|
-
serializer.url_helpers.send(
|
102
|
-
url,
|
103
|
-
id: object.read_attribute(primary_key),
|
104
|
-
relationship: reflection_name,
|
105
|
-
host: serializer.class.send(:caprese_default_url_options_host)
|
106
|
-
)
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
:nil
|
111
|
-
end
|
112
|
-
end
|
113
33
|
end
|
114
34
|
end
|
115
35
|
end
|
@@ -52,13 +52,13 @@ module Caprese
|
|
52
52
|
# Gets a serializer for a klass, either as the serializer explicitly defined
|
53
53
|
# for this class, or as a serializer defined for one of the klass's parents
|
54
54
|
#
|
55
|
-
# @param [Class] klass the klass to get the serializer for
|
55
|
+
# @param [Class,String] klass the klass or klass name to get the serializer for
|
56
56
|
# @return [Serializer] the serializer for the class
|
57
57
|
def get_serializer_for(klass)
|
58
58
|
begin
|
59
|
-
namespaced_module("#{klass.name}Serializer").constantize
|
60
|
-
rescue NameError
|
61
|
-
get_serializer_for(klass.superclass) if klass.superclass
|
59
|
+
namespaced_module("#{klass.is_a?(Class) ? klass.name : klass}Serializer").constantize
|
60
|
+
rescue NameError
|
61
|
+
get_serializer_for(klass.superclass) if klass.is_a?(Class) && klass.superclass
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
@@ -23,6 +23,108 @@ module Caprese
|
|
23
23
|
def relationship_scope(name, scope)
|
24
24
|
scope
|
25
25
|
end
|
26
|
+
|
27
|
+
module ClassMethods
|
28
|
+
def has_many(name, options = {}, &block)
|
29
|
+
super(
|
30
|
+
name,
|
31
|
+
merge_serializer_option(name, options),
|
32
|
+
&build_association_block(name)
|
33
|
+
)
|
34
|
+
|
35
|
+
define_method name do
|
36
|
+
self.relationship_scope(name, object.send(object.class.caprese_unalias_field(name)))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def has_one(name, options = {}, &block)
|
41
|
+
super(
|
42
|
+
name,
|
43
|
+
merge_serializer_option(name, options),
|
44
|
+
&build_association_block(name)
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
def belongs_to(name, options = {}, &block)
|
49
|
+
super(
|
50
|
+
name,
|
51
|
+
merge_serializer_option(name, options),
|
52
|
+
&build_association_block(name)
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# Builds a block that is passed into an association when it is defined in a specific serializer
|
59
|
+
# The block is run, and links are added to each association so when it is rendered in the
|
60
|
+
# `relationships` object of the `data` for record, it contains links to the particular association
|
61
|
+
#
|
62
|
+
# @example
|
63
|
+
# object = Order<@token=5, @product_id=10>
|
64
|
+
# reflection_name = 'product'
|
65
|
+
# # => {
|
66
|
+
# id: 'asd27h',
|
67
|
+
# type: 'orders',
|
68
|
+
# relationships: {
|
69
|
+
# product: {
|
70
|
+
# id: 'hy7sql',
|
71
|
+
# type: 'products',
|
72
|
+
# links: {
|
73
|
+
# self: '/api/v1/orders/asd27h/relationships/product',
|
74
|
+
# related: '/api/v1/orders/asd27h/product'
|
75
|
+
# }
|
76
|
+
# }
|
77
|
+
# }
|
78
|
+
# }
|
79
|
+
#
|
80
|
+
# @param [String] reflection_name the name of the relationship
|
81
|
+
# @return [Block] a block to build links for the relationship
|
82
|
+
def build_association_block(reflection_name)
|
83
|
+
primary_key = Caprese.config.resource_primary_key
|
84
|
+
|
85
|
+
reflection_name = reflection_name.to_sym
|
86
|
+
|
87
|
+
Proc.new do |serializer|
|
88
|
+
link :self do
|
89
|
+
url = "relationship_definition_#{serializer.version_name("#{serializer.unnamespace(object.class.name).underscore}_url")}"
|
90
|
+
if serializer.url_helpers.respond_to? url
|
91
|
+
serializer.url_helpers.send(
|
92
|
+
url,
|
93
|
+
id: object.read_attribute(primary_key),
|
94
|
+
relationship: reflection_name,
|
95
|
+
host: serializer.class.send(:caprese_default_url_options_host)
|
96
|
+
)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
link :related do
|
101
|
+
url = "relationship_data_#{serializer.version_name("#{serializer.unnamespace(object.class.name).underscore}_url")}"
|
102
|
+
if serializer.url_helpers.respond_to? url
|
103
|
+
serializer.url_helpers.send(
|
104
|
+
url,
|
105
|
+
id: object.read_attribute(primary_key),
|
106
|
+
relationship: reflection_name,
|
107
|
+
host: serializer.class.send(:caprese_default_url_options_host)
|
108
|
+
)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
:nil
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Adds a default serializer for relationship based on the relationship name
|
117
|
+
#
|
118
|
+
# @example
|
119
|
+
# has_many :answers => AnswerSerializer
|
120
|
+
#
|
121
|
+
# @param [String] name the name of the relationship
|
122
|
+
# @param [Hash] options options to add default serializer to
|
123
|
+
# @return [Hash] the default options
|
124
|
+
def merge_serializer_option(name, options)
|
125
|
+
{ serializer: get_serializer_for(name.to_s.classify) }.merge(options)
|
126
|
+
end
|
127
|
+
end
|
26
128
|
end
|
27
129
|
end
|
28
130
|
end
|
data/lib/caprese/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: caprese
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Landgrebe
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date: 2017-
|
13
|
+
date: 2017-09-27 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: active_model_serializers
|
@@ -18,14 +18,14 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - '='
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: 0.10.
|
21
|
+
version: 0.10.6
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
requirements:
|
26
26
|
- - '='
|
27
27
|
- !ruby/object:Gem::Version
|
28
|
-
version: 0.10.
|
28
|
+
version: 0.10.6
|
29
29
|
- !ruby/object:Gem::Dependency
|
30
30
|
name: kaminari
|
31
31
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
name: rails
|
45
45
|
requirement: !ruby/object:Gem::Requirement
|
46
46
|
requirements:
|
47
|
-
- - "
|
47
|
+
- - "~>"
|
48
48
|
- !ruby/object:Gem::Version
|
49
49
|
version: 4.2.0
|
50
50
|
type: :runtime
|
51
51
|
prerelease: false
|
52
52
|
version_requirements: !ruby/object:Gem::Requirement
|
53
53
|
requirements:
|
54
|
-
- - "
|
54
|
+
- - "~>"
|
55
55
|
- !ruby/object:Gem::Version
|
56
56
|
version: 4.2.0
|
57
57
|
- !ruby/object:Gem::Dependency
|