jsonapi-resources 0.5.9 → 0.6.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: f421adb3d1c9d4ac14ec14de18bffb8e03233b8f
4
- data.tar.gz: 738efbc31c5bd1d1d1a7dc1c3bd83c7f9163e6ae
3
+ metadata.gz: 6f1f659549f6b8804be7a7bed6ca3a359a37fc5e
4
+ data.tar.gz: 3703986e36f362f348ce463d4977b268171d8f0b
5
5
  SHA512:
6
- metadata.gz: 16c6f58ddc71ba0f97489b7ff725ff41905f43b1329a23b60004f250567180ff385d87d822dd4c4197ec13b9f7bfad6ee20062335dd558328ce6e78e5bb182c0
7
- data.tar.gz: f1263437a5904619e8f11ff34f6bf7f701ad9f48bb1b272a6790ec45fcffb361121cfe5f0d66f8332f8283aa16b2b7e68e003895919f4f5e7e1998315e1512b0
6
+ metadata.gz: 7b368231330b4c2a21bcfde117c7c4797a16c3bf145e20525b6fcdff429fde0a276e2b9b95b0e265dee890f37b4862529e7780f4caef90886d99ea36a4877af5
7
+ data.tar.gz: 4518564dceb728f5a594dd93c9fcf22de5051ad6397a4282513ad8f8f7d27c7bae355cae3bc8a9fd593fc11bdfdb91885f878a17a809d0150d1ef4daa4a7a174
@@ -1,9 +1,8 @@
1
1
  language: ruby
2
2
  sudo: false
3
3
  env:
4
- - "RAILS_VERSION=4.0"
5
- - "RAILS_VERSION=4.1"
6
- - "RAILS_VERSION=4.2"
4
+ - "RAILS_VERSION=4.1.0"
5
+ - "RAILS_VERSION=4.2.0"
7
6
  rvm:
8
7
  - 2.0
9
8
  - 2.1
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # JSONAPI::Resources [![Build Status](https://secure.travis-ci.org/cerebris/jsonapi-resources.png?branch=master)](http://travis-ci.org/cerebris/jsonapi-resources) [![Code Climate](https://codeclimate.com/github/cerebris/jsonapi-resources/badges/gpa.svg)](https://codeclimate.com/github/cerebris/jsonapi-resources)
2
2
 
3
+ [![Join the chat at https://gitter.im/cerebris/jsonapi-resources](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/cerebris/jsonapi-resources?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
4
+
3
5
  `JSONAPI::Resources`, or "JR", provides a framework for developing a server that complies with the
4
6
  [JSON API](http://jsonapi.org/) specification.
5
7
 
@@ -29,6 +31,7 @@ backed by ActiveRecord models or by custom objects.
29
31
  * [Namespaces] (#namespaces)
30
32
  * [Error Codes] (#error-codes)
31
33
  * [Handling Exceptions] (#handling-exceptions)
34
+ * [Action Callbacks] (#action-callbacks)
32
35
  * [Serializer] (#serializer)
33
36
  * [Configuration] (#configuration)
34
37
  * [Contributing] (#contributing)
@@ -150,7 +153,7 @@ class AuthorResource < JSONAPI::Resource
150
153
  has_many :posts
151
154
 
152
155
  def fetchable_fields
153
- if (context.current_user.guest)
156
+ if (context[:current_user].guest)
154
157
  super - [:email]
155
158
  else
156
159
  super
@@ -225,6 +228,41 @@ end
225
228
  The system will lookup a value formatter named `DateWithTimezoneValueFormatter` and will use this when serializing and
226
229
  updating the attribute. See the [Value Formatters](#value-formatters) section for more details.
227
230
 
231
+ ##### Flattening a Rails relationship
232
+
233
+ It is possible to flatten Rails relationships into attributes by using getters and setters. This can become handy if a relation needs to be created alongside the creation of the main object which can be the case if there is a bi-directional presence validation. For example:
234
+
235
+ ```ruby
236
+ # Given Models
237
+ class Person < ActiveRecord::Base
238
+ has_many :spoken_languages
239
+ validates :name, :email, :spoken_languages, presence: true
240
+ end
241
+
242
+ class SpokenLanguage < ActiveRecord::Base
243
+ belongs_to :person, inverse_of: :spoken_languages
244
+ validates :person, :language_code, presence: true
245
+ end
246
+
247
+ # Resource with getters and setter
248
+ class PersonResource < JSONAPI::Resource
249
+ attributes :name, :email, :spoken_languages
250
+
251
+ # Getter
252
+ def spoken_languages
253
+ @model.spoken_languages.pluck(:language_code)
254
+ end
255
+
256
+ # Setter (because spoken_languages needed for creation)
257
+ def spoken_languages=(new_spoken_language_codes)
258
+ @model.spoken_languages.destroy_all
259
+ new_spoken_language_codes.each do |new_lang_code|
260
+ @model.spoken_languages.build(language_code: new_lang_code)
261
+ end
262
+ end
263
+ end
264
+ ```
265
+
228
266
  #### Primary Key
229
267
 
230
268
  Resources are always represented using a key of `id`. The resource will interrogate the model to find the primary key.
@@ -445,25 +483,27 @@ class PostResource < JSONAPI::Resource
445
483
 
446
484
  def self.records(options = {})
447
485
  context = options[:context]
448
- context.current_user.posts
486
+ context[:current_user].posts
449
487
  end
450
488
  end
451
489
  ```
452
490
 
453
- When you create a relationship, a method is created to fetch record(s) for that relationship. This method calls
454
- `records_for(relationship_name)` by default.
491
+ When you create a relationship, a method is created to fetch record(s) for that relationship, using the relation name
492
+ for the relationship.
455
493
 
456
494
  ```ruby
457
495
  class PostResource < JSONAPI::Resource
458
496
  has_one :author
459
497
  has_many :comments
460
498
 
461
- # def record_for_author(options = {})
462
- # records_for("author", options)
499
+ # def record_for_author
500
+ # relation_name = relationship.relation_name(context: @context)
501
+ # records_for(relation_name)
463
502
  # end
464
503
 
465
- # def records_for_comments(options = {})
466
- # records_for("comments", options)
504
+ # def records_for_comments
505
+ # relation_name = relationship.relation_name(context: @context)
506
+ # records_for(relation_name)
467
507
  # end
468
508
  end
469
509
 
@@ -474,11 +514,11 @@ section for additional details on raising errors.
474
514
 
475
515
  ```ruby
476
516
  class BaseResource < JSONAPI::Resource
477
- def records_for(relationship_name, options={})
517
+ def records_for(relation_name)
478
518
  context = options[:context]
479
- records = model.public_send(relationship_name)
519
+ records = model.public_send(relation_name)
480
520
 
481
- unless context.current_user.can_view?(records)
521
+ unless context[:current_user].can_view?(records)
482
522
  raise NotAuthorizedError
483
523
  end
484
524
 
@@ -567,10 +607,10 @@ class AuthorResource < JSONAPI::Resource
567
607
 
568
608
  def self.find(filters, options = {})
569
609
  context = options[:context]
570
- authors = context.current_user.find_authors(filters)
610
+ authors = context[:current_user].find_authors(filters)
571
611
 
572
612
  return authors.map do |author|
573
- self.new(author)
613
+ self.new(author, context)
574
614
  end
575
615
  end
576
616
  end
@@ -626,7 +666,7 @@ The default paginator, which will be used for all resources, is set using `JSONA
626
666
 
627
667
  ```ruby
628
668
  JSONAPI.configure do |config|
629
- # built in paginators are :none, :offset, :cursor, :paged
669
+ # built in paginators are :none, :offset, :paged
630
670
  config.default_paginator = :offset
631
671
 
632
672
  config.default_page_size = 10
@@ -709,8 +749,6 @@ Will get you the following payload by default:
709
749
  }
710
750
  ```
711
751
 
712
- You can also pass an `include` option to [Serializer#serialize_to_hash](#include) if you want to define this inline.
713
-
714
752
  #### Callbacks
715
753
 
716
754
  `ActiveSupport::Callbacks` is used to provide callback functionality, so the behavior is very similar to what you may be
@@ -781,6 +819,7 @@ To return the total record count of a find operation in the meta data of a find
781
819
  OperationsProcessor. For example:
782
820
 
783
821
  ```ruby
822
+ # lib/jsonapi/counting_active_record_operations_processor.rb
784
823
  class CountingActiveRecordOperationsProcessor < ActiveRecordOperationsProcessor
785
824
  after_find_operation do
786
825
  @operation_meta[:total_records] = @operation.record_count
@@ -792,6 +831,8 @@ Set the configuration option `operations_processor` to use the new `CountingActi
792
831
  specifying the snake cased name of the class (without the `OperationsProcessor`).
793
832
 
794
833
  ```ruby
834
+ require 'jsonapi/counting_active_record_operations_processor'
835
+
795
836
  JSONAPI.configure do |config|
796
837
  config.operations_processor = :counting_active_record
797
838
  end
@@ -840,6 +881,8 @@ class PeopleController < ApplicationController
840
881
  end
841
882
  ```
842
883
 
884
+ > __Note__: This gem [uses the filter chain to set up the request](https://github.com/cerebris/jsonapi-resources/issues/458#issuecomment-143297055). In some instances, variables that are set in the filter chain (such as `current_user`) may not be set at the right time. If this happens (i.e. `current_user` is `nil` in `context` but it's set properly everywhere else), you may want to have your authentication occur earlier in the filter chain, using `prepend_before_action` instead of `before_action`.
885
+
843
886
  ##### ActsAsResourceController
844
887
 
845
888
  `JSONAPI::Resources` also provides a module, `JSONAPI::ActsAsResourceController`. You can include this module to
@@ -994,12 +1037,12 @@ end
994
1037
 
995
1038
  By default, all exceptions raised downstream from a resource controller will be caught, logged, and a ```500 Internal Server Error``` will be rendered. Exceptions can be whitelisted in the config to pass through the handler and be caught manually, or you can pass a callback from a resource controller to insert logic into the rescue block without interrupting the control flow. This can be particularly useful for additional logging or monitoring without the added work of rendering responses.
996
1039
 
997
- Pass a block, refer to controller class methods, or both. Note that methods must be defined as class methods on a controller and accept one parameter, which is passed the exception object that was rescued.
1040
+ Pass a block, refer to controller class methods, or both. Note that methods must be defined as class methods on a controller and accept one parameter, which is passed the exception object that was rescued.
998
1041
 
999
1042
  ```ruby
1000
1043
  class ApplicationController < JSONAPI::ResourceController
1001
1044
 
1002
- on_server_error :first_callback
1045
+ on_server_error :first_callback
1003
1046
 
1004
1047
  #or
1005
1048
 
@@ -1014,6 +1057,26 @@ Pass a block, refer to controller class methods, or both. Note that methods must
1014
1057
 
1015
1058
  ```
1016
1059
 
1060
+ #### Action Callbacks
1061
+
1062
+ ##### ensure_correct_media_type
1063
+
1064
+ By default, when controllers extend functionalities from `jsonapi-resources`, the `ActsAsResourceController#ensure_correct_media_type`
1065
+ method will be triggered before `create`, `update`, `create_relationship` and `update_relationship` actions. This method is reponsible
1066
+ for checking if client's request corresponds to the correct media type required by [JSON API](http://jsonapi.org/format/#content-negotiation-clients): `application/vnd.api+json`.
1067
+
1068
+ In case you need to check the media type for custom actions, just make sure to call the method in your controller's `before_action`:
1069
+
1070
+ ```ruby
1071
+ class UsersController < JSONAPI::ResourceController
1072
+ before_action :ensure_correct_media_type, only: [:auth]
1073
+
1074
+ def auth
1075
+ # some crazy auth code goes here
1076
+ end
1077
+ end
1078
+ ```
1079
+
1017
1080
  ### Serializer
1018
1081
 
1019
1082
  The `ResourceSerializer` can be used to serialize a resource into JSON API compliant JSON. `ResourceSerializer` must be
@@ -1022,7 +1085,7 @@ The `ResourceSerializer` can be used to serialize a resource into JSON API compl
1022
1085
 
1023
1086
  ```ruby
1024
1087
  post = Post.find(1)
1025
- JSONAPI::ResourceSerializer.new(PostResource).serialize_to_hash(PostResource.new(post))
1088
+ JSONAPI::ResourceSerializer.new(PostResource).serialize_to_hash(PostResource.new(post, nil))
1026
1089
  ```
1027
1090
 
1028
1091
  This returns results like this:
@@ -1075,9 +1138,9 @@ This returns results like this:
1075
1138
  }
1076
1139
  ```
1077
1140
 
1078
- #### serialize_to_hash method options
1141
+ #### Serializer options
1079
1142
 
1080
- The `serialize_to_hash` method also takes some optional parameters:
1143
+ The `ResourceSerializer` can be initialized with some optional parameters:
1081
1144
 
1082
1145
  ##### `include`
1083
1146
 
@@ -1107,13 +1170,9 @@ JSONAPI::ResourceSerializer.new(PostResource, include: include_resources,
1107
1170
  tags: [:name],
1108
1171
  comments: [:body, :post]
1109
1172
  }
1110
- ).serialize_to_hash(PostResource.new(post))
1173
+ ).serialize_to_hash(PostResource.new(post, nil))
1111
1174
  ```
1112
1175
 
1113
- ##### `context`
1114
-
1115
- Context data can be provided to the serializer, which passes it to each resource as it is inspected.
1116
-
1117
1176
  #### Routing
1118
1177
 
1119
1178
  JR has a couple of helper methods available to assist you with setting up routes.
@@ -1283,18 +1342,23 @@ phone_number_contact GET /phone-numbers/:phone_number_id/contact(.:format) co
1283
1342
 
1284
1343
  #### Formatting
1285
1344
 
1286
- JR by default uses some simple rules to format an attribute for serialization. Strings and Integers are output to JSON
1345
+ JR by default uses some simple rules to format (and unformat) an attribute for (de-)serialization. Strings and Integers are output to JSON
1287
1346
  as is, and all other values have `.to_s` applied to them. This outputs something in all cases, but it is certainly not
1288
1347
  correct for every situation.
1289
1348
 
1290
- If you want to change the way an attribute is serialized you have a couple of ways. The simplest method is to create a
1291
- getter method on the resource which overrides the attribute and apply the formatting there. For example:
1349
+ If you want to change the way an attribute is (de-)serialized you have a couple of ways. The simplest method is to create a
1350
+ getter (and setter) method on the resource which overrides the attribute and apply the (un-)formatting there. For example:
1292
1351
 
1293
1352
  ```ruby
1294
1353
  class PersonResource < JSONAPI::Resource
1295
- attributes :name, :email
1296
- attribute :last_login_time
1354
+ attributes :name, :email, :last_login_time
1297
1355
 
1356
+ # Setter example
1357
+ def email=(new_email)
1358
+ @model.email = new_email.downcase
1359
+ end
1360
+
1361
+ # Getter example
1298
1362
  def last_login_time
1299
1363
  @model.last_login_time.in_time_zone(@context[:current_user].time_zone).to_s
1300
1364
  end
@@ -1312,8 +1376,10 @@ handled for an attribute. The `format` can be set per attribute as it is declare
1312
1376
 
1313
1377
  ```ruby
1314
1378
  class PersonResource < JSONAPI::Resource
1315
- attributes :name, :email
1379
+ attributes :name, :email, :spoken_languages
1316
1380
  attribute :last_login_time, format: :date_with_utc_timezone
1381
+
1382
+ # Getter/Setter for spoken_languages ...
1317
1383
  end
1318
1384
  ```
1319
1385
 
@@ -1431,6 +1497,9 @@ JR has a few configuration options. Some have already been mentioned above. To s
1431
1497
  initializer and add the options you wish to set. All options have defaults, so you only need to set the options that
1432
1498
  are different. The default options are shown below.
1433
1499
 
1500
+ If using custom classes (such as the CountingActiveRecordOperationsProcessor, or a CustomPaginator),
1501
+ be sure to require them at the top of the initializer before usage.
1502
+
1434
1503
  ```ruby
1435
1504
  JSONAPI.configure do |config|
1436
1505
  #:underscored_key, :camelized_key, :dasherized_key, or custom
@@ -23,7 +23,6 @@ Gem::Specification.new do |spec|
23
23
  spec.add_development_dependency 'rake'
24
24
  spec.add_development_dependency 'minitest'
25
25
  spec.add_development_dependency 'minitest-spec-rails'
26
- spec.add_development_dependency 'minitest-reporters'
27
26
  spec.add_development_dependency 'simplecov'
28
27
  spec.add_development_dependency 'pry'
29
28
  spec.add_dependency 'rails', '>= 4.0'
@@ -5,9 +5,9 @@ module JSONAPI
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
8
- before_filter :ensure_correct_media_type, only: [:create, :update, :create_relationship, :update_relationship]
9
- append_before_filter :setup_request
10
- after_filter :setup_response
8
+ before_action :ensure_correct_media_type, only: [:create, :update, :create_relationship, :update_relationship]
9
+ append_before_action :setup_request
10
+ after_action :setup_response
11
11
  end
12
12
 
13
13
  def index
@@ -187,22 +187,22 @@ module JSONAPI
187
187
  @request.server_error_callbacks = callbacks || []
188
188
  end
189
189
 
190
- # Pass in a methods or a block to be run when an exception is
190
+ # Pass in a methods or a block to be run when an exception is
191
191
  # caught that is not a JSONAPI::Exceptions::Error
192
- # Useful for additional logging or notification configuration that
192
+ # Useful for additional logging or notification configuration that
193
193
  # would normally depend on rails catching and rendering an exception.
194
194
  # Ignores whitelist exceptions from config
195
195
 
196
- class_methods do
196
+ module ClassMethods
197
197
  def on_server_error(*args, &callback_block)
198
198
  callbacks = []
199
199
 
200
- if callback_block
200
+ if callback_block
201
201
  callbacks << callback_block
202
202
  end
203
203
 
204
204
  method_callbacks = args.map do |method|
205
- ->(error) do
205
+ ->(error) do
206
206
  if self.respond_to? method
207
207
  send(method, error)
208
208
  else
@@ -211,7 +211,7 @@ module JSONAPI
211
211
  end
212
212
  end.compact
213
213
  callbacks += method_callbacks
214
- append_before_filter { add_error_callbacks(callbacks) }
214
+ append_before_action { add_error_callbacks(callbacks) }
215
215
  end
216
216
  end
217
217
  end
@@ -81,7 +81,7 @@ module JSONAPI
81
81
  scopes = module_scopes_from_class(source.class)[1..-1]
82
82
  base_path_name = scopes.map { |scope| scope.underscore }.join("_")
83
83
  end_path_name = source.class._type.to_s.singularize
84
- "#{ base_path_name }_#{ end_path_name }_path"
84
+ [base_path_name, end_path_name, "path"].reject(&:blank?).join("_")
85
85
  end
86
86
 
87
87
  def engine_resource_url(source)
@@ -150,7 +150,7 @@ module JSONAPI
150
150
  end
151
151
 
152
152
  def record_count
153
- @_record_count ||= records.count
153
+ @_record_count ||= records.count(:all)
154
154
  end
155
155
 
156
156
  def source_resource
@@ -92,15 +92,11 @@ module JSONAPI
92
92
  end
93
93
 
94
94
  def setup_create_relationship_action(params)
95
- parse_add_relationship_operation(params.require(:data),
96
- params.require(:relationship),
97
- params.require(@resource_klass._as_parent_key))
95
+ parse_modify_relationship_action(params, :add)
98
96
  end
99
97
 
100
98
  def setup_update_relationship_action(params)
101
- parse_update_relationship_operation(params.fetch(:data),
102
- params.require(:relationship),
103
- params.require(@resource_klass._as_parent_key))
99
+ parse_modify_relationship_action(params, :update)
104
100
  end
105
101
 
106
102
  def setup_update_action(params)
@@ -114,7 +110,28 @@ module JSONAPI
114
110
  end
115
111
 
116
112
  def setup_destroy_relationship_action(params)
117
- parse_remove_relationship_operation(params)
113
+ parse_modify_relationship_action(params, :remove)
114
+ end
115
+
116
+ def parse_modify_relationship_action(params, modification_type)
117
+ relationship_type = params.require(:relationship)
118
+ parent_key = params.require(@resource_klass._as_parent_key)
119
+ relationship = @resource_klass._relationship(relationship_type)
120
+
121
+ # Removals of to-one relationships are done implicitly and require no specification of data
122
+ data_required = !(modification_type == :remove && relationship.is_a?(JSONAPI::Relationship::ToOne))
123
+
124
+ if data_required
125
+ data = params.fetch(:data)
126
+ object_params = { relationships: { format_key(relationship.name) => { data: data } } }
127
+ verified_params = parse_params(object_params, updatable_fields)
128
+
129
+ parse_arguments = [verified_params, relationship, parent_key]
130
+ else
131
+ parse_arguments = [params, relationship, parent_key]
132
+ end
133
+
134
+ send(:"parse_#{modification_type}_relationship_operation", *parse_arguments)
118
135
  end
119
136
 
120
137
  def initialize_source(params)
@@ -159,8 +176,7 @@ module JSONAPI
159
176
  @errors.concat(e.errors)
160
177
  end
161
178
 
162
- if type_resource.nil? || !(@resource_klass._type == underscored_type ||
163
- @resource_klass._has_relationship?(underscored_type))
179
+ if type_resource.nil?
164
180
  @errors.concat(JSONAPI::Exceptions::InvalidResource.new(type).errors)
165
181
  else
166
182
  unless values.nil?
@@ -548,66 +564,47 @@ module JSONAPI
548
564
  end
549
565
  # :nocov:
550
566
 
551
- def parse_add_relationship_operation(data, relationship_type, parent_key)
552
- relationship = resource_klass._relationship(relationship_type)
553
-
567
+ def parse_add_relationship_operation(verified_params, relationship, parent_key)
554
568
  if relationship.is_a?(JSONAPI::Relationship::ToMany)
555
- object_params = { relationships: { format_key(relationship.name) => { data: data } } }
556
- verified_param_set = parse_params(object_params, updatable_fields)
557
-
558
569
  @operations.push JSONAPI::CreateToManyRelationshipOperation.new(
559
570
  resource_klass,
560
571
  context: @context,
561
572
  resource_id: parent_key,
562
- relationship_type: relationship_type,
563
- data: verified_param_set[:to_many].values[0]
573
+ relationship_type: relationship.name,
574
+ data: verified_params[:to_many].values[0]
564
575
  )
565
576
  end
566
577
  end
567
578
 
568
- def parse_update_relationship_operation(data, relationship_type, parent_key)
569
- relationship = resource_klass._relationship(relationship_type)
579
+ def parse_update_relationship_operation(verified_params, relationship, parent_key)
580
+ operation_args = [resource_klass].push(
581
+ context: @context,
582
+ resource_id: parent_key,
583
+ relationship_type: relationship.name
584
+ )
585
+
570
586
  if relationship.is_a?(JSONAPI::Relationship::ToOne)
571
587
  if relationship.polymorphic?
572
- object_params = { relationships: { format_key(relationship.name) => { data: data } } }
573
- verified_param_set = parse_params(object_params, updatable_fields)
574
-
575
- @operations.push JSONAPI::ReplacePolymorphicToOneRelationshipOperation.new(
576
- resource_klass,
577
- context: @context,
578
- resource_id: parent_key,
579
- relationship_type: relationship_type,
580
- key_value: verified_param_set[:to_one].values[0][:id],
581
- key_type: verified_param_set[:to_one].values[0][:type]
582
- )
588
+ operation_args[1].merge!(
589
+ key_value: verified_params[:to_one].values[0][:id],
590
+ key_type: verified_params[:to_one].values[0][:type]
591
+ )
592
+
593
+ operation_klass = JSONAPI::ReplacePolymorphicToOneRelationshipOperation
583
594
  else
584
- object_params = { relationships: { format_key(relationship.name) => { data: data } } }
585
- verified_param_set = parse_params(object_params, updatable_fields)
586
-
587
- @operations.push JSONAPI::ReplaceToOneRelationshipOperation.new(
588
- resource_klass,
589
- context: @context,
590
- resource_id: parent_key,
591
- relationship_type: relationship_type,
592
- key_value: verified_param_set[:to_one].values[0]
593
- )
595
+ operation_args[1].merge!(key_value: verified_params[:to_one].values[0])
596
+ operation_klass = JSONAPI::ReplaceToOneRelationshipOperation
594
597
  end
595
598
  elsif relationship.is_a?(JSONAPI::Relationship::ToMany)
596
599
  unless relationship.acts_as_set
597
600
  fail JSONAPI::Exceptions::ToManySetReplacementForbidden.new
598
601
  end
599
602
 
600
- object_params = { relationships: { format_key(relationship.name) => { data: data } } }
601
- verified_param_set = parse_params(object_params, updatable_fields)
602
-
603
- @operations.push JSONAPI::ReplaceToManyRelationshipOperation.new(
604
- resource_klass,
605
- context: @context,
606
- resource_id: parent_key,
607
- relationship_type: relationship_type,
608
- data: verified_param_set[:to_many].values[0]
609
- )
603
+ operation_args[1].merge!(data: verified_params[:to_many].values[0])
604
+ operation_klass = JSONAPI::ReplaceToManyRelationshipOperation
610
605
  end
606
+
607
+ @operations.push(operation_klass.send(:new, *operation_args))
611
608
  end
612
609
 
613
610
  def parse_single_replace_operation(data, keys, id_key_presence_check_required: true)
@@ -665,29 +662,25 @@ module JSONAPI
665
662
  @errors.concat(e.errors)
666
663
  end
667
664
 
668
- def parse_remove_relationship_operation(params)
669
- relationship_type = params[:relationship]
670
-
671
- parent_key = params[resource_klass._as_parent_key]
665
+ def parse_remove_relationship_operation(params, relationship, parent_key)
666
+ operation_base_args = [resource_klass].push(
667
+ context: @context,
668
+ resource_id: parent_key,
669
+ relationship_type: relationship.name
670
+ )
672
671
 
673
- relationship = resource_klass._relationship(relationship_type)
674
672
  if relationship.is_a?(JSONAPI::Relationship::ToMany)
675
- keys = parse_key_array(params[:keys])
673
+ keys = params[:to_many].values[0]
676
674
  keys.each do |key|
675
+ operation_args = operation_base_args.dup
676
+ operation_args[1] = operation_args[1].merge(associated_key: key)
677
677
  @operations.push JSONAPI::RemoveToManyRelationshipOperation.new(
678
- resource_klass,
679
- context: @context,
680
- resource_id: parent_key,
681
- relationship_type: relationship_type,
682
- associated_key: key
678
+ *operation_args
683
679
  )
684
680
  end
685
681
  else
686
682
  @operations.push JSONAPI::RemoveToOneRelationshipOperation.new(
687
- resource_klass,
688
- context: @context,
689
- resource_id: parent_key,
690
- relationship_type: relationship_type
683
+ *operation_base_args
691
684
  )
692
685
  end
693
686
  end