-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor JSONAPI adapter #1103
Refactor JSONAPI adapter #1103
Changes from all commits
8482abf
f95f736
343f8b9
d9c6805
c4faafd
0401205
f7612f2
91c5cbe
bae4951
c593adb
a8a0566
f8c553a
3793f3f
070a2e6
f27f13c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,13 @@ def initialize(serializer, options = {}) | |
super | ||
@hash = { data: [] } | ||
|
||
if fields = options.delete(:fields) | ||
@options[:include] ||= [] | ||
if @options[:include].is_a?(String) | ||
@options[:include] = @options[:include].split(',') | ||
end | ||
|
||
fields = options.delete(:fields) | ||
if fields | ||
@fieldset = ActiveModel::Serializer::Fieldset.new(fields, serializer.json_key) | ||
else | ||
@fieldset = options[:fieldset] | ||
|
@@ -31,150 +37,136 @@ def serializable_hash(options = nil) | |
|
||
add_links(options) | ||
else | ||
@hash[:data] = attributes_for_serializer(serializer, options) | ||
add_resource_relationships(@hash[:data], serializer) | ||
primary_data = primary_data_for(serializer, options) | ||
relationships = relationships_for(serializer) | ||
included = included_for(serializer) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
@hash[:data] = primary_data | ||
@hash[:data][:relationships] = relationships if relationships.any? | ||
@hash[:included] = included if included.any? | ||
end | ||
@hash | ||
end | ||
|
||
def fragment_cache(cached_hash, non_cached_hash) | ||
root = false if @options.include?(:include) | ||
JsonApi::FragmentCache.new().fragment_cache(root, cached_hash, non_cached_hash) | ||
JsonApi::FragmentCache.new.fragment_cache(root, cached_hash, non_cached_hash) | ||
end | ||
|
||
private | ||
|
||
def add_relationships(resource, name, serializers) | ||
resource[:relationships] ||= {} | ||
resource[:relationships][name] ||= { data: [] } | ||
resource[:relationships][name][:data] += serializers.map { |serializer| { type: serializer.json_api_type, id: serializer.id.to_s } } | ||
def resource_identifier_type_for(serializer) | ||
if ActiveModel::Serializer.config.jsonapi_resource_type == :singular | ||
serializer.object.class.model_name.singular | ||
else | ||
serializer.object.class.model_name.plural | ||
end | ||
end | ||
|
||
def add_relationship(resource, name, serializer, val = nil) | ||
resource[:relationships] ||= {} | ||
resource[:relationships][name] = { data: val } | ||
|
||
if serializer && serializer.object | ||
resource[:relationships][name][:data] = { type: serializer.json_api_type, id: serializer.id.to_s } | ||
def resource_identifier_id_for(serializer) | ||
if serializer.respond_to?(:id) | ||
serializer.id | ||
else | ||
serializer.object.id | ||
end | ||
end | ||
|
||
def add_included(resource_name, serializers, parent = nil) | ||
unless serializers.respond_to?(:each) | ||
return unless serializers.object | ||
serializers = Array(serializers) | ||
end | ||
resource_path = [parent, resource_name].compact.join('.') | ||
if include_assoc?(resource_path) | ||
@hash[:included] ||= [] | ||
def resource_identifier_for(serializer) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
type = resource_identifier_type_for(serializer) | ||
id = resource_identifier_id_for(serializer) | ||
|
||
serializers.each do |serializer| | ||
attrs = attributes_for_serializer(serializer, @options) | ||
{ id: id.to_s, type: type } | ||
end | ||
|
||
add_resource_relationships(attrs, serializer, add_included: false) | ||
def resource_object_for(serializer, options = {}) | ||
options[:fields] = @fieldset && @fieldset.fields_for(serializer) | ||
|
||
@hash[:included].push(attrs) unless @hash[:included].include?(attrs) | ||
end | ||
cache_check(serializer) do | ||
result = resource_identifier_for(serializer) | ||
attributes = serializer.attributes(options).except(:id) | ||
result[:attributes] = attributes if attributes.any? | ||
result | ||
end | ||
end | ||
|
||
serializers.each do |serializer| | ||
serializer.associations.each do |association| | ||
serializer = association.serializer | ||
|
||
add_included(association.key, serializer, resource_path) if serializer | ||
end if include_nested_assoc? resource_path | ||
def primary_data_for(serializer, options) | ||
if serializer.respond_to?(:each) | ||
serializer.map { |s| resource_object_for(s, options) } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
else | ||
resource_object_for(serializer, options) | ||
end | ||
end | ||
|
||
def attributes_for_serializer(serializer, options) | ||
def relationship_value_for(serializer, options = {}) | ||
if serializer.respond_to?(:each) | ||
result = [] | ||
serializer.each do |object| | ||
result << resource_object_for(object, options) | ||
end | ||
serializer.map { |s| resource_identifier_for(s) } | ||
else | ||
result = resource_object_for(serializer, options) | ||
if options[:virtual_value] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, we don't need There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exactly. |
||
options[:virtual_value] | ||
elsif serializer && serializer.object | ||
resource_identifier_for(serializer) | ||
end | ||
end | ||
result | ||
end | ||
|
||
def resource_object_for(serializer, options) | ||
options[:fields] = @fieldset && @fieldset.fields_for(serializer) | ||
options[:required_fields] = [:id, :json_api_type] | ||
def relationships_for(serializer) | ||
Hash[serializer.associations.map { |association| [association.key, { data: relationship_value_for(association.serializer, association.options) }] }] | ||
end | ||
|
||
cache_check(serializer) do | ||
attributes = serializer.attributes(options) | ||
def included_for(serializer) | ||
serializer.associations.flat_map { |assoc| _included_for(assoc.key, assoc.serializer) }.uniq | ||
end | ||
|
||
result = { | ||
id: attributes.delete(:id).to_s, | ||
type: attributes.delete(:json_api_type) | ||
} | ||
def _included_for(resource_name, serializer, parent = nil) | ||
if serializer.respond_to?(:each) | ||
serializer.flat_map { |s| _included_for(resource_name, s, parent) }.uniq | ||
else | ||
return [] unless serializer && serializer.object | ||
result = [] | ||
resource_path = [parent, resource_name].compact.join('.') | ||
|
||
result[:attributes] = attributes if attributes.any? | ||
if include_assoc?(resource_path) | ||
primary_data = primary_data_for(serializer, @options) | ||
relationships = relationships_for(serializer) | ||
primary_data[:relationships] = relationships if relationships.any? | ||
result.push(primary_data) | ||
end | ||
|
||
if include_nested_assoc?(resource_path) | ||
non_empty_associations = serializer.associations.select(&:serializer) | ||
|
||
non_empty_associations.each do |association| | ||
result.concat(_included_for(association.key, association.serializer, resource_path)) | ||
result.uniq! | ||
end | ||
end | ||
result | ||
end | ||
end | ||
|
||
def include_assoc?(assoc) | ||
return false unless @options[:include] | ||
check_assoc("#{assoc}$") | ||
end | ||
|
||
def include_nested_assoc?(assoc) | ||
return false unless @options[:include] | ||
check_assoc("#{assoc}.") | ||
end | ||
|
||
def check_assoc(assoc) | ||
include_opt = @options[:include] | ||
include_opt = include_opt.split(',') if include_opt.is_a?(String) | ||
include_opt.any? do |s| | ||
s.match(/^#{assoc.gsub('.', '\.')}/) | ||
end | ||
end | ||
|
||
def add_resource_relationships(attrs, serializer, options = {}) | ||
options[:add_included] = options.fetch(:add_included, true) | ||
|
||
serializer.associations.each do |association| | ||
key = association.key | ||
serializer = association.serializer | ||
opts = association.options | ||
|
||
attrs[:relationships] ||= {} | ||
|
||
if serializer.respond_to?(:each) | ||
add_relationships(attrs, key, serializer) | ||
else | ||
if opts[:virtual_value] | ||
add_relationship(attrs, key, nil, opts[:virtual_value]) | ||
else | ||
add_relationship(attrs, key, serializer) | ||
end | ||
end | ||
|
||
if options[:add_included] | ||
Array(serializer).each do |s| | ||
add_included(key, s) | ||
end | ||
end | ||
end | ||
@options[:include].any? { |s| s.match(/^#{assoc.gsub('.', '\.')}/) } | ||
end | ||
|
||
def add_links(options) | ||
links = @hash.fetch(:links) { {} } | ||
collection = serializer.object | ||
if is_paginated?(collection) | ||
@hash[:links] = add_pagination_links(links, collection, options) | ||
end | ||
@hash[:links] = add_pagination_links(links, collection, options) if paginated?(collection) | ||
end | ||
|
||
def add_pagination_links(links, collection, options) | ||
pagination_links = JsonApi::PaginationLinks.new(collection, options[:context]).serializable_hash(options) | ||
links.update(pagination_links) | ||
end | ||
|
||
def is_paginated?(collection) | ||
def paginated?(collection) | ||
collection.respond_to?(:current_page) && | ||
collection.respond_to?(:total_pages) && | ||
collection.respond_to?(:size) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain more about this. It's not related to
type
andid
, and I also don't understood the why ofString
verification +split
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nvm, understood 😁