caprese 0.3.27 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|