Skip to content

Commit eb43272

Browse files
authored
Refactor to allow resources to be serialized without the developer needing to understand the internal components (#1348)
* Refactor to allow resources to be serialized without the developer needing to understand the internal components * Simplify and clarify naming for internal components * Refine include_directives
1 parent 1322b59 commit eb43272

20 files changed

+1024
-415
lines changed

lib/jsonapi-resources.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
require 'jsonapi/active_relation/join_manager'
3838
require 'jsonapi/resource_identity'
3939
require 'jsonapi/resource_fragment'
40-
require 'jsonapi/resource_id_tree'
40+
require 'jsonapi/resource_tree'
4141
require 'jsonapi/resource_set'
4242
require 'jsonapi/path'
4343
require 'jsonapi/path_segment'

lib/jsonapi/active_relation_resource.rb

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ module JSONAPI
22
class ActiveRelationResource < BasicResource
33
root_resource
44

5+
def find_related_ids(relationship, options = {})
6+
self.class.find_related_fragments([self], relationship.name, options).keys.collect { |rid| rid.id }
7+
end
8+
59
class << self
610
# Finds Resources using the `filters`. Pagination and sort options are used when provided
711
#
@@ -90,8 +94,11 @@ def find_to_populate_by_keys(keys, options = {})
9094
# the ResourceInstances matching the filters, sorting, and pagination rules along with any request
9195
# additional_field values
9296
def find_fragments(filters, options = {})
93-
include_directives = options[:include_directives] ? options[:include_directives].include_directives : {}
97+
include_directives = options.fetch(:include_directives, {})
9498
resource_klass = self
99+
100+
fragments = {}
101+
95102
linkage_relationships = to_one_relationships_for_linkage(include_directives[:include_related])
96103

97104
sort_criteria = options.fetch(:sort_criteria) { [] }
@@ -129,18 +136,26 @@ def find_fragments(filters, options = {})
129136
if linkage_relationship.polymorphic? && linkage_relationship.belongs_to?
130137
linkage_relationship.resource_types.each do |resource_type|
131138
klass = resource_klass_for(resource_type)
132-
linkage_fields << {relationship_name: name, resource_klass: klass}
133-
134139
linkage_table_alias = join_manager.join_details_by_polymorphic_relationship(linkage_relationship, resource_type)[:alias]
135140
primary_key = klass._primary_key
141+
142+
linkage_fields << {relationship_name: name,
143+
resource_klass: klass,
144+
field: "#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}",
145+
alias: "#{linkage_table_alias}_#{primary_key}"}
146+
136147
pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}")
137148
end
138149
else
139150
klass = linkage_relationship.resource_klass
140-
linkage_fields << {relationship_name: name, resource_klass: klass}
141-
142151
linkage_table_alias = join_manager.join_details_by_relationship(linkage_relationship)[:alias]
143152
primary_key = klass._primary_key
153+
154+
linkage_fields << {relationship_name: name,
155+
resource_klass: klass,
156+
field: "#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}",
157+
alias: "#{linkage_table_alias}_#{primary_key}"}
158+
144159
pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}")
145160
end
146161
end
@@ -158,7 +173,6 @@ def find_fragments(filters, options = {})
158173
pluck_fields << Arel.sql(field)
159174
end
160175

161-
fragments = {}
162176
rows = records.pluck(*pluck_fields)
163177
rows.each do |row|
164178
rid = JSONAPI::ResourceIdentity.new(resource_klass, pluck_fields.length == 1 ? row : row[0])
@@ -204,23 +218,23 @@ def find_fragments(filters, options = {})
204218
# @return [Hash{ResourceIdentity => {identity: => ResourceIdentity, cache: cache_field, attributes: => {name => value}, related: {relationship_name: [] }}}]
205219
# the ResourceInstances matching the filters, sorting, and pagination rules along with any request
206220
# additional_field values
207-
def find_related_fragments(source_rids, relationship_name, options = {})
221+
def find_related_fragments(source, relationship_name, options = {})
208222
relationship = _relationship(relationship_name)
209223

210224
if relationship.polymorphic? # && relationship.foreign_key_on == :self
211-
find_related_polymorphic_fragments(source_rids, relationship, options, false)
225+
find_related_polymorphic_fragments(source, relationship, options, false)
212226
else
213-
find_related_monomorphic_fragments(source_rids, relationship, options, false)
227+
find_related_monomorphic_fragments(source, relationship, options, false)
214228
end
215229
end
216230

217-
def find_included_fragments(source_rids, relationship_name, options)
231+
def find_included_fragments(source, relationship_name, options)
218232
relationship = _relationship(relationship_name)
219233

220234
if relationship.polymorphic? # && relationship.foreign_key_on == :self
221-
find_related_polymorphic_fragments(source_rids, relationship, options, true)
235+
find_related_polymorphic_fragments(source, relationship, options, true)
222236
else
223-
find_related_monomorphic_fragments(source_rids, relationship, options, true)
237+
find_related_monomorphic_fragments(source, relationship, options, true)
224238
end
225239
end
226240

@@ -231,7 +245,7 @@ def find_included_fragments(source_rids, relationship_name, options)
231245
# @option options [Hash] :context The context of the request, set in the controller
232246
#
233247
# @return [Integer] the count
234-
def count_related(source_rid, relationship_name, options = {})
248+
def count_related(source_resource, relationship_name, options = {})
235249
relationship = _relationship(relationship_name)
236250
related_klass = relationship.resource_klass
237251

@@ -244,7 +258,7 @@ def count_related(source_rid, relationship_name, options = {})
244258

245259
records = apply_request_settings_to_records(records: records(options),
246260
resource_klass: related_klass,
247-
primary_keys: source_rid.id,
261+
primary_keys: source_resource.id,
248262
join_manager: join_manager,
249263
filters: filters,
250264
options: options)
@@ -375,11 +389,11 @@ def find_records_by_keys(keys, options = {})
375389
apply_request_settings_to_records(records: records(options), primary_keys: keys, options: options)
376390
end
377391

378-
def find_related_monomorphic_fragments(source_rids, relationship, options, connect_source_identity)
392+
def find_related_monomorphic_fragments(source_fragments, relationship, options, connect_source_identity)
379393
filters = options.fetch(:filters, {})
380-
source_ids = source_rids.collect {|rid| rid.id}
394+
source_ids = source_fragments.collect {|item| item.identity.id}
381395

382-
include_directives = options[:include_directives] ? options[:include_directives].include_directives : {}
396+
include_directives = options.fetch(:include_directives, {})
383397
resource_klass = relationship.resource_klass
384398
linkage_relationships = resource_klass.to_one_relationships_for_linkage(include_directives[:include_related])
385399

@@ -501,12 +515,12 @@ def find_related_monomorphic_fragments(source_rids, relationship, options, conne
501515

502516
# Gets resource identities where the related resource is polymorphic and the resource type and id
503517
# are stored on the primary resources. Cache fields will always be on the related resources.
504-
def find_related_polymorphic_fragments(source_rids, relationship, options, connect_source_identity)
518+
def find_related_polymorphic_fragments(source_fragments, relationship, options, connect_source_identity)
505519
filters = options.fetch(:filters, {})
506-
source_ids = source_rids.collect {|rid| rid.id}
520+
source_ids = source_fragments.collect {|item| item.identity.id}
507521

508522
resource_klass = relationship.resource_klass
509-
include_directives = options[:include_directives] ? options[:include_directives].include_directives : {}
523+
include_directives = options.fetch(:include_directives, {})
510524

511525
linkage_relationships = []
512526

lib/jsonapi/basic_resource.rb

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,12 @@ def identity
4444
JSONAPI::ResourceIdentity.new(self.class, id)
4545
end
4646

47+
def cache_field_value
48+
_model.public_send(self.class._cache_field)
49+
end
50+
4751
def cache_id
48-
[id, self.class.hash_cache_field(_model.public_send(self.class._cache_field))]
52+
[id, self.class.hash_cache_field(cache_field_value)]
4953
end
5054

5155
def is_new?
@@ -285,9 +289,7 @@ def _replace_to_many_links(relationship_type, relationship_key_values, options)
285289
reflect = reflect_relationship?(relationship, options)
286290

287291
if reflect
288-
existing_rids = self.class.find_related_fragments([identity], relationship_type, options)
289-
290-
existing = existing_rids.keys.collect { |rid| rid.id }
292+
existing = find_related_ids(relationship, options)
291293

292294
to_delete = existing - (relationship_key_values & existing)
293295
to_delete.each do |key|
@@ -417,6 +419,10 @@ def _replace_fields(field_data)
417419
:completed
418420
end
419421

422+
def find_related_ids(relationship, options = {})
423+
send(relationship.foreign_key)
424+
end
425+
420426
class << self
421427
def inherited(subclass)
422428
super
@@ -637,7 +643,7 @@ def model_name(model, options = {})
637643
end
638644

639645
def model_hint(model: _model_name, resource: _type)
640-
resource_type = ((resource.is_a?(Class)) && (resource < JSONAPI::Resource)) ? resource._type : resource.to_s
646+
resource_type = ((resource.is_a?(Class)) && (resource < JSONAPI::BasicResource)) ? resource._type : resource.to_s
641647

642648
_model_hints[model.to_s.gsub('::', '/').underscore] = resource_type.to_s
643649
end
@@ -710,7 +716,7 @@ def resources_for(records, context)
710716
end
711717

712718
def resource_for(model_record, context)
713-
resource_klass = self.resource_klass_for_model(model_record)
719+
resource_klass = resource_klass_for_model(model_record)
714720
resource_klass.new(model_record, context)
715721
end
716722

@@ -1084,7 +1090,7 @@ def _add_relationship(klass, *attrs)
10841090
end
10851091
end
10861092

1087-
# ResourceBuilder methods
1093+
# ResourceBuilder methods
10881094
def define_relationship_methods(relationship_name, relationship_klass, options)
10891095
relationship = register_relationship(
10901096
relationship_name,

lib/jsonapi/include_directives.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ def initialize(resource_klass, includes_array)
2525
end
2626
end
2727

28-
def include_directives
29-
@include_directives_hash
28+
def [](name)
29+
@include_directives_hash[name]
3030
end
3131

3232
private

0 commit comments

Comments
 (0)