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 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