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
@return [Header] header from a mapper
@api private
@return [Mapper] mapper that this processor belongs to
@api private
@return [Hash] header's attribute mapping
@api private
@return [Class] model class from a mapper
@api private
@return [Proc] row-processing proc
@api private
Public Class Methods
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
@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
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
@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
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
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 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
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
@api private
# File lib/rom/processor/transproc.rb, line 410 def t(*args) Functions[*args] end
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
@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 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
@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 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 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 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
@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 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
: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
: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 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
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