RestPack/restpack_serializer

View on GitHub
lib/restpack_serializer/serializable/side_loading.rb

Summary

Maintainability
A
0 mins
Test Coverage
module RestPack::Serializer::SideLoading
  extend ActiveSupport::Concern

  module ClassMethods
    def side_loads(models, options)
      { meta: { } }.tap do |side_loads|
        return side_loads if models.empty? || options.include.nil?

        options.include.each do |include|
          side_load_data = side_load(include, models, options)
          side_loads[:meta].merge!(side_load_data[:meta] || {})
          side_loads.merge! side_load_data.except(:meta)
        end
      end
    end

    def can_includes
      @can_includes || []
    end

    def can_include(*includes)
      @can_includes ||= []
      @can_includes += includes
    end

    def links
      {}.tap do |links|
        associations.each do |association|
          if association.macro == :belongs_to
            link_key = "#{self.key}.#{association.name}"
            href = "/#{association.plural_name}/{#{link_key}}"
          elsif association.macro.to_s.match(/has_/)
            singular_key = self.key.to_s.singularize
            link_key = "#{self.key}.#{association.plural_name}"
            href = "/#{association.plural_name}?#{singular_key}_id={#{key}.id}"
          end

          links.merge!(link_key => {
            :href => href_prefix + href,
            :type => association.plural_name.to_sym
            }
          )
        end
      end
    end

    def has_associations?
      @can_includes
    end

    def associations
      return [] unless has_associations?
      can_includes.map do |include|
        association = association_from_include(include)
        association if supported_association?(association.macro)
      end.compact
    end

    private

    def side_load(include, models, options)
      association = association_from_include(include)
      return {} unless supported_association?(association.macro)
      serializer = RestPack::Serializer::Factory.create(association.class_name)
      builder = RestPack::Serializer::SideLoadDataBuilder.new(association,
                                                              models,
                                                              serializer)
      builder.send("side_load_#{association.macro}")
    end

    def supported_association?(association_macro)
      [:belongs_to, :has_many, :has_and_belongs_to_many].include?(association_macro)
    end

    def association_from_include(include)
      raise_invalid_include(include) unless can_include?(include)
      possible_relations = [include.to_s.singularize.to_sym, include]
      select_association_from_possibles(possible_relations)
    end

    def select_association_from_possibles(possible_relations)
      possible_relations.each do |relation|
        if association = self.model_class.reflect_on_association(relation)
          return association
        end
      end
      raise_invalid_include(include)
    end

    def can_include?(include)
      !!self.can_includes.index do |can_include|
        can_include == include || can_include.to_s == include
      end
    end

    def raise_invalid_include(include)
      raise RestPack::Serializer::InvalidInclude.new,
        ":#{include} is not a valid include for #{self.model_class}"
    end
  end
end