class RelationToJSON::Base
Attributes
relation[R]
schema[R]
Public Class Methods
new(relation, schema)
click to toggle source
# File lib/relation_to_json/base.rb, line 10 def initialize(relation, schema) @relation = relation @schema = schema end
Public Instance Methods
as_json()
click to toggle source
# File lib/relation_to_json/base.rb, line 15 def as_json # put everything here, anything else is private attributes, schema_associations = schema .partition { |e| e.is_a?(Symbol) } schema_associations = schema_associations.first.dup || [] attributes = Set[:id] + attributes reflections = RelationToJSON::ReflectionBuilder.build(schema_associations, relation) schema_associations.each do |schema_association, association_attributes| reflection = reflections[schema_association] case reflection when RelationToJSON::BelongsToReflection attributes << reflection.foreign_key.to_sym when RelationToJSON::HasOneReflection association_attributes << reflection.foreign_key.to_sym end end raise_unless_all_attributes_present_on_model!(relation, attributes) result = relation .reload # if when the relation isn't loaded, it may have strange ordering .pluck(*attributes) .map { |plucked| attributes.zip(Array.wrap(plucked)).to_h } transposed = transpose(result) reflections.each do |reflection_name, reflection| foreign_key = reflection.foreign_key primary_key = reflection.primary_key # if the current schema still has associations # then we need to recursively find the JSON # representation of that association # Otherwise, we can perform a shallow .pluck # of the association attributes # and map them back onto the transposed hash # this returns an array of hashes that map association attributes to plucked values plucked_values = reflection.pluck_association_columns(transposed) case reflection when RelationToJSON::BelongsToReflection # build a temporary mapping of id => assigned_attributes associated_model_primary_key_indexed_plucked_values = plucked_values .to_h { |attrs| [attrs&.fetch(primary_key, nil), attrs] } .compact result.each do |record| foreign_key_value = record[foreign_key] plucked_values = associated_model_primary_key_indexed_plucked_values[foreign_key_value] record[reflection_name] = plucked_values record.except!(foreign_key) end when RelationToJSON::HasOneReflection # build a temporary mapping of id => assigned_attributes associated_model_foreign_key_indexed_plucked_values = plucked_values .to_h { |attrs| [attrs&.fetch(foreign_key, nil), attrs] } .compact result.each do |record| primary_key_value = record[primary_key] plucked_values = associated_model_foreign_key_indexed_plucked_values[primary_key_value] plucked_values.except!(foreign_key) if plucked_values record[reflection_name] = plucked_values end end end result&.map { |partial| partial.with_indifferent_access } end
Private Instance Methods
raise_unless_all_attributes_present_on_model!(relation, requested)
click to toggle source
# File lib/relation_to_json/base.rb, line 114 def raise_unless_all_attributes_present_on_model!(relation, requested) available = Set[*relation.klass.column_names] requested = Set[*requested.map(&:to_s)] return if requested <= available missing = requested - available raise InvalidSchemaError.new(missing.to_a, relation.klass) end
transpose(values)
click to toggle source
# File lib/relation_to_json/base.rb, line 90 def transpose(values) # values is a list of hashes # each hash should be identical # i.e. [{id: 1, clinical_sender_id: 2}, {id: 2, clinical_sender_id: 3}] # and tranposes into a hash # with identical keys # but values are arrays # i.e. { id: [1, 2], clinical_sender_id: [2, 3] } result = {} values.each do |value| value.each do |k, v| if result.include?(k) result[k] << v else result[k] = [v] end end end result end