class LunaPark::Mappers::Codirectional

DSL for describe Nested Schema translation: entity attributes to database row and vice-versa

@example

class Mappers::Transaction < LunaPark::Mappers::Codirectional
  map attr: :uid,                 row: :id
  map attr: [:charge, :amount],   row: :charge_amount
  map attr: [:charge, :currency], row: :charge_currency # using aliased args
  map :comment
end

mapper = Mappers::Transaction

attrs = { charge: { amount: 10, currency: 'USD' }, comment: 'Foobar' }
transaction = Entities::Transaction.new(attrs)

# Mapper transforms attr attributes to database row and vice-verse
row       = mapper.to_row(transaction)        # => {          charge_amount: 10, charge_currency: 'USD', comment: 'Foobar' }
new_row   = sequel_database_table.insert(row) # => { id:  42, charge_amount: 10, charge_currency: 'USD', comment: 'Foobar' }
new_attrs = mapper.from_row(new_row)          # => { uid: 42, charge: { amount: 10, currency: 'USD' },   comment: 'Foobar' }

transaction.set_attributes(new_attrs)
transaction.to_h # => { uid: 42, charge: { amount: 10, currency: 'USD' }, comment: 'Foobar' }

Public Class Methods

attr(attr, row: nil) click to toggle source

@example

class Mappers::Transaction < LunaPark::Mappers::Codirectional
  attr :uid,              row: :id
  attr %i[charge amount], row: :charge_amount
end
# File lib/luna_park/mappers/codirectional.rb, line 57
def attr(attr, row: nil)
  return attrs(attr) if row.nil?

  attr_path = to_path(attr)
  row_path  = to_path(row)

  if attr_path == row_path
    attrs(attr_path)
  else
    nested_copyists << Copyists::Nested.new(attrs_path: attr_path, row_path: row_path)
  end
end
attrs(*common_keys) click to toggle source

@example

class Mappers::Transaction < LunaPark::Mappers::Codirectional
  attrs :comment, :uid, %i[addresses home], :created_at
end
# File lib/luna_park/mappers/codirectional.rb, line 74
def attrs(*common_keys)
  common_keys.each do |common_key|
    path = to_path(common_key)
    if path.is_a?(Array)
      nested_copyists << Copyists::Nested.new(attrs_path: path, row_path: path)
    else
      slice_copyist.add_key(path)
    end
  end
end
from_row(input) click to toggle source
# File lib/luna_park/mappers/codirectional.rb, line 85
def from_row(input)
  row = input.to_h
  {}.tap do |attrs|
    slice_copyist.from_row(row: row, attrs: attrs)
    nested_copyists.each { |copyist| copyist.from_row(row: row, attrs: attrs) }
  end
end
map(*common_keys, attr: nil, row: nil) click to toggle source

Describe translation between two schemas: attr and table

@example

class Mappers::Transaction < LunaPark::Mappers::Codirectional
  map attr: :id,                row: :uid
  map attr: [:charge, :amount], row: :charge_amount
  map :comment
end

Mappers::Transaction.from_row({ id: 1, charge_amount: 2 })     # => { uid: 1, charge: { amount: 2 } }
Mappers::Transaction.to_row({ uid: 1, charge: { amount: 2 } }) # => { id: 1, charge_amount: 2 }
# File lib/luna_park/mappers/codirectional.rb, line 46
def map(*common_keys, attr: nil, row: nil)
  attrs(*common_keys) if common_keys.any?

  self.attr attr, row: row if attr
end
to_row(input) click to toggle source
# File lib/luna_park/mappers/codirectional.rb, line 93
def to_row(input)
  attrs = input.to_h
  {}.tap do |row|
    slice_copyist.to_row(row: row, attrs: attrs)
    nested_copyists.each { |copyist| copyist.to_row(row: row, attrs: attrs) }
  end
end

Private Class Methods

nested_copyists() click to toggle source
# File lib/luna_park/mappers/codirectional.rb, line 124
def nested_copyists
  @nested_copyists ||= []
end
slice_copyist() click to toggle source
# File lib/luna_park/mappers/codirectional.rb, line 120
def slice_copyist
  @slice_copyist ||= Copyists::Slice.new
end
to_path(input, full: input) click to toggle source

@example

to_path :email              # => :email
to_path ['email']           # => :email
to_path [:charge, 'amount'] # => [:charge, :amount]
# File lib/luna_park/mappers/codirectional.rb, line 107
def to_path(input, full: input)
  case input
  when Symbol then input
  when String then input.to_sym
  when Array
    return to_path(input.first, full: full) if input.size <= 1

    input.flat_map { |elem| to_path(elem, full: full) }
  else raise ArgumentError, "Unexpected path part `#{input.inspect}` in `#{full.inspect}`. " \
                            'Expected Symbol, String or Array'
  end
end