jsonapi_compliable 0.7.9 → 0.8.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: 725d1d0d6d2f31535a981d6894f6f49fa4d59686
4
- data.tar.gz: 04d9c2419e99b379cb2884eaaf63f3f022bb8e9e
3
+ metadata.gz: 10190f5b7e0cd15456e21552f847b9a41d5fae80
4
+ data.tar.gz: 002b8c3ffb2fe962e682182199519a8182a909a0
5
5
  SHA512:
6
- metadata.gz: 8b2ac3294051a12d5fc84e508be50a6d8b890109ef253e27ac975ed545e649e007b1035e8a6ff2c7da58861a0c27e5ade4f96a90c07564649ea084783d058dfc
7
- data.tar.gz: afef6fafdc7ef0a4b4b36ad3c2ad0f0d8c23286c55dd61b20bec29688e2e3b1d7c3bbb9ff4ead5d3c3f1aeb7637682bb66cfe1d7a5a0bfadf9147fa752b39ebc
6
+ metadata.gz: 5744a2c7eba2af24ea476210cf9804fdbc16d0c636b6e9d55c586e198a6ddf196122f8e6e27029d7f723cd07670efbea3d6aa21d4368ece68152c212249dd0ca
7
+ data.tar.gz: dbae4b77df449427b11d0dd5135a722b713a11c8f7fd05d62192dbd13c043ce48fcd3517179fb72afef9a05d460aadc6a4137fda0ef200f1bd1b6f9a99eca2c8
@@ -237,6 +237,46 @@ module JsonapiCompliable
237
237
  raise 'you must override #associate in an adapter subclass'
238
238
  end
239
239
 
240
+ # Remove the association without destroying objects
241
+ #
242
+ # This is NOT needed in the standard use case. The standard use case would be:
243
+ #
244
+ # def update(attrs)
245
+ # # attrs[:the_foreign_key] is nil, so updating the record disassociates
246
+ # end
247
+ #
248
+ # However, sometimes you need side-effect or elsewise non-standard behavior. Consider
249
+ # using {{https://github.com/mbleigh/acts-as-taggable-on acts_as_taggable_on}} gem:
250
+ #
251
+ # # Not actually needed, just an example
252
+ # def disassociate(parent, child, association_name, association_type)
253
+ # parent.tag_list.remove(child.name)
254
+ # end
255
+ #
256
+ # @example Basic accessor
257
+ # def disassociate(parent, child, association_name, association_type)
258
+ # if association_type == :has_many
259
+ # parent.send(association_name).delete(child)
260
+ # else
261
+ # child.send(:"#{association_name}=", nil)
262
+ # end
263
+ # end
264
+ #
265
+ # +association_name+ and +association_type+ come from your sideload
266
+ # configuration:
267
+ #
268
+ # allow_sideload :the_name, type: the_type do
269
+ # # ... code.
270
+ # end
271
+ #
272
+ # @param parent The parent object (via the JSONAPI 'relationships' graph)
273
+ # @param child The child object (via the JSONAPI 'relationships' graph)
274
+ # @param association_name The 'relationships' key we are processing
275
+ # @param association_type The Sideload type (see Sideload#type). Usually :has_many/:belongs_to/etc
276
+ def disassociate(parent, child, association_name, association_type)
277
+ raise 'you must override #disassociate in an adapter subclass'
278
+ end
279
+
240
280
  # This module gets mixed in to Sideload classes
241
281
  # This is where you define methods like has_many,
242
282
  # belongs_to etc that wrap the lower-level Sideload#allow_sideload
@@ -75,11 +75,24 @@ module JsonapiCompliable
75
75
  if association_type == :has_many
76
76
  parent.association(association_name).loaded!
77
77
  parent.association(association_name).add_to_target(child, :skip_callbacks)
78
+ elsif association_type == :habtm
79
+ parent.send(association_name) << child
78
80
  else
79
81
  child.send("#{association_name}=", parent)
80
82
  end
81
83
  end
82
84
 
85
+ # When a has_and_belongs_to_many relationship, we don't have a foreign
86
+ # key that can be null'd. Instead, go through the ActiveRecord API.
87
+ # @see Adapters::Abstract#disassociate
88
+ def disassociate(parent, child, association_name, association_type)
89
+ if association_type == :habtm
90
+ parent.send(association_name).delete(child)
91
+ else
92
+ # Nothing to do here, happened when we merged foreign key
93
+ end
94
+ end
95
+
83
96
  # (see Adapters::Abstract#create)
84
97
  def create(model_class, create_params)
85
98
  instance = model_class.new(create_params)
@@ -543,10 +543,28 @@ module JsonapiCompliable
543
543
  adapter.destroy(model, id)
544
544
  end
545
545
 
546
+ # Delegates #associate to adapter. Built for overriding.
547
+ #
548
+ # @see .use_adapter
549
+ # @see Adapters::Abstract#associate
550
+ # @see Adapters::ActiveRecord#associate
551
+ def associate(parent, child, association_name, type)
552
+ adapter.associate(parent, child, association_name, type)
553
+ end
554
+
555
+ # Delegates #disassociate to adapter. Built for overriding.
556
+ #
557
+ # @see .use_adapter
558
+ # @see Adapters::Abstract#disassociate
559
+ # @see Adapters::ActiveRecord#disassociate
560
+ def disassociate(parent, child, association_name, type)
561
+ adapter.disassociate(parent, child, association_name, type)
562
+ end
563
+
546
564
  # @api private
547
- def persist_with_relationships(meta, attributes, relationships)
565
+ def persist_with_relationships(meta, attributes, relationships, caller_model = nil)
548
566
  persistence = JsonapiCompliable::Util::Persistence \
549
- .new(self, meta, attributes, relationships)
567
+ .new(self, meta, attributes, relationships, caller_model)
550
568
  persistence.run
551
569
  end
552
570
 
@@ -170,24 +170,25 @@ module JsonapiCompliable
170
170
  end
171
171
 
172
172
  # Configure how to associate parent and child records.
173
- #
174
- # @example Basic attr_accessor
175
- # def associate(parent, child)
176
- # if type == :has_many
177
- # parent.send(:"#{name}").push(child)
178
- # else
179
- # child.send(:"#{name}=", parent)
180
- # end
181
- # end
173
+ # Delegates to #resource
182
174
  #
183
175
  # @see #name
184
176
  # @see #type
177
+ # @api private
185
178
  def associate(parent, child)
186
179
  association_name = @parent ? @parent.name : name
187
- resource_class.config[:adapter].associate parent,
188
- child,
189
- association_name,
190
- type
180
+ resource.associate(parent, child, association_name, type)
181
+ end
182
+
183
+ # Configure how to disassociate parent and child records.
184
+ # Delegates to #resource
185
+ #
186
+ # @see #name
187
+ # @see #type
188
+ # @api private
189
+ def disassociate(parent, child)
190
+ association_name = @parent ? @parent.name : name
191
+ resource.disassociate(parent, child, association_name, type)
191
192
  end
192
193
 
193
194
  # Define an attribute that groups the parent records. For instance, with
@@ -5,11 +5,12 @@ class JsonapiCompliable::Util::Persistence
5
5
  # @param [Hash] meta see (Deserializer#meta)
6
6
  # @param [Hash] attributes see (Deserializer#attributes)
7
7
  # @param [Hash] relationships see (Deserializer#relationships)
8
- def initialize(resource, meta, attributes, relationships)
8
+ def initialize(resource, meta, attributes, relationships, caller_model)
9
9
  @resource = resource
10
10
  @meta = meta
11
11
  @attributes = attributes
12
12
  @relationships = relationships
13
+ @caller_model = caller_model
13
14
  end
14
15
 
15
16
  # Perform the actual save logic.
@@ -37,7 +38,7 @@ class JsonapiCompliable::Util::Persistence
37
38
  assign_temp_id(persisted, @meta[:temp_id])
38
39
  associate_parents(persisted, parents)
39
40
 
40
- children = process_has_many(@relationships) do |x|
41
+ children = process_has_many(@relationships, persisted) do |x|
41
42
  update_foreign_key(persisted, x[:attributes], x)
42
43
  end
43
44
 
@@ -49,7 +50,11 @@ class JsonapiCompliable::Util::Persistence
49
50
 
50
51
  # The child's attributes should be modified to nil-out the
51
52
  # foreign_key when the parent is being destroyed or disassociated
53
+ #
54
+ # This is not the case for HABTM, whose "foreign key" is a join table
52
55
  def update_foreign_key(parent_object, attrs, x)
56
+ return if x[:sideload].type == :habtm
57
+
53
58
  if [:destroy, :disassociate].include?(x[:meta][:method])
54
59
  attrs[x[:foreign_key]] = nil
55
60
  update_foreign_type(attrs, x, null: true) if x[:is_polymorphic]
@@ -72,33 +77,45 @@ class JsonapiCompliable::Util::Persistence
72
77
 
73
78
  def associate_parents(object, parents)
74
79
  parents.each do |x|
75
- x[:sideload].associate(x[:object], object) if x[:object] && object
80
+ if x[:object] && object
81
+ if x[:meta][:method] == :disassociate
82
+ x[:sideload].disassociate(x[:object], object)
83
+ else
84
+ x[:sideload].associate(x[:object], object)
85
+ end
86
+ end
76
87
  end
77
88
  end
78
89
 
79
90
  def associate_children(object, children)
80
91
  children.each do |x|
81
- x[:sideload].associate(object, x[:object]) if x[:object] && object
92
+ if x[:object] && object
93
+ if x[:meta][:method] == :disassociate
94
+ x[:sideload].disassociate(object, x[:object])
95
+ else
96
+ x[:sideload].associate(object, x[:object])
97
+ end
98
+ end
82
99
  end
83
100
  end
84
101
 
85
102
  def persist_object(method, attributes)
86
103
  case method
87
104
  when :destroy
88
- @resource.destroy(attributes[:id])
89
- when :disassociate, nil
90
- @resource.update(attributes)
105
+ call_resource_method(:destroy, attributes[:id], @caller_model)
106
+ when :update, nil, :disassociate
107
+ call_resource_method(:update, attributes, @caller_model)
91
108
  else
92
- @resource.send(method, attributes)
109
+ call_resource_method(:create, attributes, @caller_model)
93
110
  end
94
111
  end
95
112
 
96
- def process_has_many(relationships)
113
+ def process_has_many(relationships, caller_model)
97
114
  [].tap do |processed|
98
115
  iterate(except: [:polymorphic_belongs_to, :belongs_to]) do |x|
99
116
  yield x
100
117
  x[:object] = x[:sideload].resource
101
- .persist_with_relationships(x[:meta], x[:attributes], x[:relationships])
118
+ .persist_with_relationships(x[:meta], x[:attributes], x[:relationships], caller_model)
102
119
  processed << x
103
120
  end
104
121
  end
@@ -128,4 +145,23 @@ class JsonapiCompliable::Util::Persistence
128
145
  yield x
129
146
  end
130
147
  end
148
+
149
+ # In the Resource, we want to allow:
150
+ #
151
+ # def create(attrs)
152
+ #
153
+ # and
154
+ #
155
+ # def create(attrs, parent = nil)
156
+ #
157
+ # 'parent' is an optional parameter that should not be part of the
158
+ # method signature in most use cases.
159
+ def call_resource_method(method_name, attributes, caller_model)
160
+ method = @resource.method(method_name)
161
+ if [2,-2].include?(method.arity)
162
+ method.call(attributes, caller_model)
163
+ else
164
+ method.call(attributes)
165
+ end
166
+ end
131
167
  end
@@ -1,3 +1,3 @@
1
1
  module JsonapiCompliable
2
- VERSION = "0.7.9"
2
+ VERSION = "0.8.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsonapi_compliable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.9
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lee Richmond