class FunWithJsonApi::Attributes::RelationshipCollection

Attributes

deserializer_class[R]
options[R]

Public Class Methods

create(name, deserializer_class_or_callable, options = {}) click to toggle source
# File lib/fun_with_json_api/attributes/relationship_collection.rb, line 6
def self.create(name, deserializer_class_or_callable, options = {})
  new(name, deserializer_class_or_callable, options)
end
new(name, deserializer_class, options = {}) click to toggle source
Calls superclass method FunWithJsonApi::Attribute::new
# File lib/fun_with_json_api/attributes/relationship_collection.rb, line 14
def initialize(name, deserializer_class, options = {})
  options = options.reverse_merge(
    attributes: [],
    relationships: []
  )
  super(name, options)
  @deserializer_class = deserializer_class
end

Public Instance Methods

decode(values) click to toggle source

Expects an array of id values for a nested collection

# File lib/fun_with_json_api/attributes/relationship_collection.rb, line 24
def decode(values)
  unless values.nil? || values.is_a?(Array)
    raise build_invalid_relationship_collection_error(values)
  end

  collection = deserializer.load_collection_from_id_values(values)

  # Ensure the collection size matches
  check_collection_matches_values!(collection, values)

  # Ensure the user is authorized to access the collection
  check_collection_is_authorized!(collection, values)

  # Call ActiceRecord#pluck if it is available
  convert_collection_to_ids(collection)
end
deserializer() click to toggle source
# File lib/fun_with_json_api/attributes/relationship_collection.rb, line 54
def deserializer
  @deserializer ||= build_deserializer_from_options
end
has_many?() click to toggle source

rubocop:disable Style/PredicateName

# File lib/fun_with_json_api/attributes/relationship_collection.rb, line 43
def has_many?
  true
end
param_value() click to toggle source

User the singular of ‘as` that is how AMS converts the value

# File lib/fun_with_json_api/attributes/relationship_collection.rb, line 50
def param_value
  :"#{as.to_s.singularize}_ids"
end

Private Instance Methods

build_deserializer_from_options() click to toggle source
# File lib/fun_with_json_api/attributes/relationship_collection.rb, line 60
def build_deserializer_from_options
  if @deserializer_class.respond_to?(:call)
    @deserializer_class.call
  else
    @deserializer_class
  end.create(options)
end
build_invalid_relationship_collection_error(values) click to toggle source
# File lib/fun_with_json_api/attributes/relationship_collection.rb, line 90
def build_invalid_relationship_collection_error(values)
  exception_message = "#{name} relationship should contain a array of"\
                      " '#{deserializer.type}' data"
  payload = ExceptionPayload.new
  payload.pointer = "/data/relationships/#{name}/data"
  payload.detail = exception_message
  Exceptions::InvalidRelationship.new(exception_message + ": #{values.inspect}", payload)
end
build_missing_relationship_error_from_collection(collection, values) click to toggle source
# File lib/fun_with_json_api/attributes/relationship_collection.rb, line 99
def build_missing_relationship_error_from_collection(collection, values)
  collection_ids = deserializer.format_collection_ids(collection)

  payload = build_missing_relationship_payload(collection_ids, values)

  missing_values = values.reject { |value| collection_ids.include?(value.to_s) }
  exception_message = "Couldn't find #{deserializer.resource_class} items with "\
                      "#{deserializer.id_param} in #{missing_values.inspect}"
  Exceptions::MissingRelationship.new(exception_message, payload)
end
build_missing_relationship_payload(collection_ids, values) click to toggle source
# File lib/fun_with_json_api/attributes/relationship_collection.rb, line 110
def build_missing_relationship_payload(collection_ids, values)
  values.each_with_index.map do |resource_id, index|
    next if collection_ids.include?(resource_id)

    ExceptionPayload.new.tap do |payload|
      payload.pointer = "/data/relationships/#{name}/data/#{index}"
      payload.detail = "Unable to find '#{deserializer.type}' with matching id"\
                       ": \"#{resource_id}\""
    end
  end.reject(&:nil?)
end
check_collection_is_authorized!(collection, values) click to toggle source
# File lib/fun_with_json_api/attributes/relationship_collection.rb, line 76
def check_collection_is_authorized!(collection, values)
  SchemaValidators::CheckCollectionIsAuthorised.call(
    collection, values, deserializer, prefix: "/data/relationships/#{name}/data"
  )
end
check_collection_matches_values!(collection, values) click to toggle source
# File lib/fun_with_json_api/attributes/relationship_collection.rb, line 68
def check_collection_matches_values!(collection, values)
  expected_size = values.size
  result_size = collection.size
  if result_size != expected_size
    raise build_missing_relationship_error_from_collection(collection, values)
  end
end
convert_collection_to_ids(collection) click to toggle source
# File lib/fun_with_json_api/attributes/relationship_collection.rb, line 82
def convert_collection_to_ids(collection)
  if collection.respond_to? :pluck
    # Well... pluck+arel doesn't work with SQLite, but select at least is safe
    collection = collection.select(deserializer.resource_class.arel_table[:id])
  end
  collection.map(&:id)
end