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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5179ca42789493695ca03e5fb2d7a1874a372056
4
- data.tar.gz: f14d99e1adf2e3d475e3b0bcb91cd4ec7e49899e
3
+ metadata.gz: 2166fc2b8abb1545a666d9d9e20bbffcb20666d0
4
+ data.tar.gz: dac348b43650955f6c16b8321e283c8d437dbd9c
5
5
  SHA512:
6
- metadata.gz: 5fba032a7f1e4543edd66dae4f4ebf4aa0774ffdf0d0580cac2a1b46a4a779abef14af898a4f23f6089b02d3a8a5a2f1071f06d783b6837721c904a03b5fa24c
7
- data.tar.gz: 1a9de55405e112dd2ad26e8c8479293e43425da2c9111251ff1267b202bab80e2d6ac6259e6c06321e5e4677ef5e860df179b6928fe37b1a5c873e17d5fe70bd
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.5'
24
+ spec.add_dependency 'active_model_serializers', '0.10.6'
25
25
  spec.add_dependency 'kaminari', '~> 0.16.0'
26
- spec.add_dependency 'rails', '>= 4.2.0'
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, included_associations)
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
- # Only render a relationship's data if it was included, if Caprese.config.optimize_relationships
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, :included_associations
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
- serializer = association.serializer
42
- if serializer.respond_to?(:each)
43
- serializer.map { |s| ResourceIdentifier.new(s, serializable_resource_options).as_json }
44
- elsif (virtual_value = association.options[:virtual_value])
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 && serializer.object
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
- relationships = relationships_for(serializer, requested_associations, included_associations)
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, requested_associations, included_associations)
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
- rescue_with_handler RecordInvalidError.new(e.record, engaged_field_aliases)
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
- else
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 => e
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
@@ -1,3 +1,3 @@
1
1
  module Caprese
2
- VERSION = '0.3.27'
2
+ VERSION = '0.4.0'
3
3
  end
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.3.27
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-08-03 00:00:00.000000000 Z
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.5
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.5
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