class ROM::Processor::Transproc

Data mapping transformer builder using Transproc

This builds a transproc function that is used to map a whole relation

@see github.com/solnic/transproc too

@private

Attributes

header[R]

@return [Header] header from a mapper

@api private

mapper[R]

@return [Mapper] mapper that this processor belongs to

@api private

mapping[R]

@return [Hash] header's attribute mapping

@api private

model[R]

@return [Class] model class from a mapper

@api private

row_proc[R]

@return [Proc] row-processing proc

@api private

Public Class Methods

build(mapper, header) click to toggle source

Build a transproc function from the header

@param [ROM::Header] header

@return [Transproc::Function]

@api private

# File lib/rom/processor/transproc.rb, line 81
def self.build(mapper, header)
  new(mapper, header).to_transproc
end
new(mapper, header) click to toggle source

@api private

# File lib/rom/processor/transproc.rb, line 86
def initialize(mapper, header)
  @mapper = mapper
  @header = header
  @model = header.model
  @mapping = header.mapping
  initialize_row_proc
end

Public Instance Methods

to_transproc() click to toggle source

Coerce mapper header to a transproc data mapping function

@return [Transproc::Function]

@api private

# File lib/rom/processor/transproc.rb, line 99
def to_transproc
  compose(t(:identity)) do |ops|
    combined = header.combined
    ops << t(:combine, combined.map(&method(:combined_args))) if combined.any?
    ops << header.preprocessed.map { |attr| visit(attr, true) }
    ops << t(:map_array, row_proc) if row_proc
    ops << header.postprocessed.map { |attr| visit(attr, true) }
  end
end

Private Instance Methods

combined_args(attribute) click to toggle source

@api private

# File lib/rom/processor/transproc.rb, line 345
def combined_args(attribute)
  other = attribute.header.combined

  if other.any?
    children = other.map(&method(:combined_args))
    [attribute.name, attribute.meta[:keys], children]
  else
    [attribute.name, attribute.meta[:keys]]
  end
end
initialize_row_proc() click to toggle source

Build row_proc

This transproc function is applied to each row in a dataset

@api private

# File lib/rom/processor/transproc.rb, line 361
def initialize_row_proc
  @row_proc = compose { |ops|
    alias_handler = header.copy_keys ? :copy_keys : :rename_keys
    process_header_keys(ops)

    ops << t(alias_handler, mapping) if header.aliased?
    ops << header.map { |attr| visit(attr) }
    ops << t(:constructor_inject, model) if model
  }
end
new(*args) click to toggle source

Return a new instance of the processor

@api private

# File lib/rom/processor/transproc.rb, line 405
def new(*args)
  self.class.new(*args)
end
process_header_keys(ops) click to toggle source

Process row_proc header keys

@api private

# File lib/rom/processor/transproc.rb, line 375
def process_header_keys(ops)
  if header.reject_keys
    all_keys = header.tuple_keys + header.non_primitives.map(&:key)
    ops << t(:accept_keys, all_keys)
  end
  ops
end
row_proc_from(attribute) click to toggle source

Build a row_proc from a given attribute

This is used by embedded attribute visitors

@api private

# File lib/rom/processor/transproc.rb, line 398
def row_proc_from(attribute)
  new(mapper, attribute.header).row_proc
end
t(*args) click to toggle source

@api private

# File lib/rom/processor/transproc.rb, line 410
def t(*args)
  Functions[*args]
end
visit(attribute, *args) click to toggle source

Visit an attribute from the header

This forwards to a specialized visitor based on the attribute type

@param [Header::Attribute] attribute @param [Array] args Allows to send `preprocess: true`

@api private

# File lib/rom/processor/transproc.rb, line 119
def visit(attribute, *args)
  type = attribute.class.name.split('::').last.downcase
  send("visit_#{type}", attribute, *args)
end
visit_array(attribute) click to toggle source

Visit array attribute

@param [Header::Attribute::Array] attribute

@api private

# File lib/rom/processor/transproc.rb, line 185
def visit_array(attribute)
  with_row_proc(attribute) do |row_proc|
    t(:map_value, attribute.name, t(:map_array, row_proc))
  end
end
visit_attribute(attribute) click to toggle source

Visit plain attribute

It will call block transformation if it's used

If it's a typed attribute a coercion transformation is added

@param [Header::Attribute] attribute

@api private

# File lib/rom/processor/transproc.rb, line 133
def visit_attribute(attribute)
  coercer = attribute.meta[:coercer]
  if attribute.union?
    compose do |ops|
      ops << t(:inject_union_value, attribute.name, attribute.key, coercer)
      ops << t(:reject_keys, attribute.key) unless header.copy_keys
    end
  elsif coercer
    t(:map_value, attribute.name, t(:bind, mapper, coercer))
  elsif attribute.typed?
    t(:map_value, attribute.name, t(:"to_#{attribute.type}"))
  end
end
visit_combined(attribute) click to toggle source

Visit combined attribute

@api private

# File lib/rom/processor/transproc.rb, line 161
def visit_combined(attribute)
  op = with_row_proc(attribute) do |row_proc|
    array_proc =
      if attribute.type == :hash
        t(:map_array, row_proc) >> t(:get, 0)
      else
        t(:map_array, row_proc)
      end

    t(:map_value, attribute.name, array_proc)
  end

  if op
    op
  elsif attribute.type == :hash
    t(:map_value, attribute.name, t(:get, 0))
  end
end
visit_exclude(attribute) click to toggle source

Visit excluded attribute

@param [Header::Attribute::Exclude] attribute

@api private

# File lib/rom/processor/transproc.rb, line 340
def visit_exclude(attribute)
  t(:reject_keys, [attribute.name])
end
visit_fold(attribute, preprocess = false) click to toggle source

Visit fold hash attribute

:fold transformation is added to handle folding during preprocessing.

@param [Header::Attribute::Fold] attribute @param [Boolean] preprocess true if we are building a relation preprocessing

function that is applied to the whole relation

@api private

# File lib/rom/processor/transproc.rb, line 293
def visit_fold(attribute, preprocess = false)
  if preprocess
    name = attribute.name
    keys = attribute.tuple_keys

    compose do |ops|
      ops << t(:group, name, keys)
      ops << t(:map_array, t(:map_value, name, t(:filter_empty)))
      ops << t(:map_array, t(:fold, name, keys.first))
    end
  end
end
visit_group(attribute, preprocess = false) click to toggle source

Visit group hash attribute

:group transformation is added to handle grouping during preprocessing. Otherwise we simply use array visitor for the attribute.

@param [Header::Attribute::Group] attribute @param [Boolean] preprocess true if we are building a relation preprocessing

function that is applied to the whole relation

@api private

# File lib/rom/processor/transproc.rb, line 235
def visit_group(attribute, preprocess = false)
  if preprocess
    name = attribute.name
    header = attribute.header
    keys = attribute.tuple_keys

    others = header.preprocessed

    compose do |ops|
      ops << t(:group, name, keys)
      ops << t(:map_array, t(:map_value, name, t(:filter_empty)))
      ops << others.map { |attr|
        t(:map_array, t(:map_value, name, visit(attr, true)))
      }
    end
  else
    visit_array(attribute)
  end
end
visit_hash(attribute) click to toggle source

Visit hash attribute

@param [Header::Attribute::Hash] attribute

@api private

# File lib/rom/processor/transproc.rb, line 152
def visit_hash(attribute)
  with_row_proc(attribute) do |row_proc|
    t(:map_value, attribute.name, row_proc)
  end
end
visit_unfold(attribute, preprocess = false) click to toggle source

Visit unfold hash attribute

:unfold transformation is added to handle unfolding during preprocessing.

@param [Header::Attribute::Unfold] attribute @param [Boolean] preprocess true if we are building a relation preprocessing

function that is applied to the whole relation

@api private

# File lib/rom/processor/transproc.rb, line 315
def visit_unfold(attribute, preprocess = false)
  if preprocess
    name = attribute.name
    header = attribute.header
    keys = attribute.pop_keys
    key = keys.first

    others = header.postprocessed

    compose do |ops|
      ops << others.map { |attr|
        t(:map_array, t(:map_value, name, visit(attr, true)))
      }
      ops << t(:map_array, t(:map_value, name, t(:insert_key, key)))
      ops << t(:map_array, t(:reject_keys, [key] - [name]))
      ops << t(:ungroup, name, [key])
    end
  end
end
visit_ungroup(attribute, preprocess = false) click to toggle source

Visit ungroup attribute

:ungroup transforation is added to handle ungrouping during preprocessing. Otherwise we simply use array visitor for the attribute.

@param [Header::Attribute::Ungroup] attribute @param [Boolean] preprocess true if we are building a relation preprocessing

function that is applied to the whole relation

@api private

# File lib/rom/processor/transproc.rb, line 265
def visit_ungroup(attribute, preprocess = false)
  if preprocess
    name = attribute.name
    header = attribute.header
    keys = attribute.pop_keys

    others = header.postprocessed

    compose do |ops|
      ops << others.map { |attr|
        t(:map_array, t(:map_value, name, visit(attr, true)))
      }
      ops << t(:ungroup, name, keys)
    end
  else
    visit_array(attribute)
  end
end
visit_unwrap(attribute) click to toggle source

Visit unwrap attribute

:unwrap transformation is added to handle unwrapping

@param [Header::Attributes::Unwrap]

@api private

# File lib/rom/processor/transproc.rb, line 215
def visit_unwrap(attribute)
  name = attribute.name
  keys = attribute.pop_keys

  compose do |ops|
    ops << visit_hash(attribute)
    ops << t(:unwrap, name, keys)
  end
end
visit_wrap(attribute) click to toggle source

Visit wrapped hash attribute

:nest transformation is added to handle wrapping

@param [Header::Attribute::Wrap] attribute

@api private

# File lib/rom/processor/transproc.rb, line 198
def visit_wrap(attribute)
  name = attribute.name
  keys = attribute.tuple_keys

  compose do |ops|
    ops << t(:nest, name, keys)
    ops << visit_hash(attribute)
  end
end
with_row_proc(attribute) { |row_proc| ... } click to toggle source

Yield row proc for a given attribute if any

@param [Header::Attribute] attribute

@api private

# File lib/rom/processor/transproc.rb, line 388
def with_row_proc(attribute)
  row_proc = row_proc_from(attribute)
  yield(row_proc) if row_proc
end