lib/ree_lib/packages/ree_mapper/package/ree_mapper.rb



# frozen_string_literal: true

module ReeMapper
  include Ree::PackageDSL

  package do
    depends_on :ree_string
    depends_on :ree_datetime
  end

  package_require('ree_string/functions/underscore')
  package_require('ree_datetime/functions/in_default_time_zone')

  require_relative 'ree_mapper/types/abstract_type'
  require_relative 'ree_mapper/errors/error'
  require_relative 'ree_mapper/errors/coercion_error'
  require_relative 'ree_mapper/errors/type_error'
  require_relative 'ree_mapper/errors/unsupported_type_error'

  require_relative 'ree_mapper/field'

  require_relative 'ree_mapper/types/bool'
  require_relative 'ree_mapper/types/date_time'
  require_relative 'ree_mapper/types/time'
  require_relative 'ree_mapper/types/date'
  require_relative 'ree_mapper/types/float'
  require_relative 'ree_mapper/types/integer'
  require_relative 'ree_mapper/types/string'
  require_relative 'ree_mapper/types/array'

  require_relative 'ree_mapper/strategy_outputs/strategy_output'
  require_relative 'ree_mapper/strategy_outputs/object_output'
  require_relative 'ree_mapper/strategy_outputs/string_key_hash_output'
  require_relative 'ree_mapper/strategy_outputs/symbol_key_hash_output'

  require_relative 'ree_mapper/mapper_strategy'
  require_relative 'ree_mapper/mapper'
  require_relative 'ree_mapper/mapper_factory_proxy'
  require_relative 'ree_mapper/mapper_factory'

  require_relative 'ree_mapper/default_factory'
  require_relative 'ree_mapper/dsl'

  def self.get_mapper_factory(mod)
    if !mod.is_a?(Module)
      raise Ree::Error.new("module should be provided", :invalid_dsl_usage)
    end

    if mod.name.nil?
      raise Ree::Error.new("anonymous modules are not supported", :invalid_dsl_usage)
    end

    if mod.name.split('::').size > 1
      raise Ree::Error.new("top level module should be provided", :invalid_dsl_usage)
    end

    factory = mod.instance_variable_get(:@mapper_factory)
    return factory if factory

    if !mod.instance_variable_get(:@mapper_semaphore)
      mod.instance_variable_set(:@mapper_semaphore, Mutex.new)
    end

    mod.instance_variable_get(:@mapper_semaphore).synchronize do
      factory = self.build_mapper_factory(mod)
      mod.instance_variable_set(:@mapper_factory, factory)
    end

    factory
  end

  private

  def self.build_mapper_factory(mod)
    pckg_name = ReeString::Underscore.new.call(mod.name)
    factory_path = "#{pckg_name}/mapper_factory"

    mapper_factory_klass = if package_file_exists?(factory_path) && mod != self
      package_require(factory_path)
      Object.const_get("#{mod.name}::MapperFactory")
    else
      ReeMapper::DefaultFactory
    end

    mapper_factory_klass.new
  end
end

=begin

Example of mapper declaration:

  class Products::UserCaster
    include Mapper::DSL

    mapper :user_caster

    build_mapper(register_as: :user).use(:cast) do
      integer :id
      string  :name
    end
  end

  class Products::ProductCaster
    include Mapper::DSL

    mapper :product_caster do
      link :user_caster
    end

    build_mapper.use(:cast) do
      integer :id
      string  :title
      user    :creator
    end
  end


Example of mapper usage:

  class Products::ProductQuery
    include Ree::BeanDSL

    bean :product_query do
      link :product_caster
    end

    def call
      product_caster.cast({
        id: 1,
        title: 'Product',
        creator: { id: 1, name: 'John' }
      })
    end
  end


If you need to register other packages mappers define your custom mapper factory.
Create `mapper_factory.rb` file to declare `MapperFactory` class.

  class Products::MapperFactory
    include Ree::BeanDSL

    bean :mapper_factory do
      link :build_mapper_factory
      link :build_mapper_strategy
      link :user_caster, from: :cart

      factory :build
    end

    def build
      mapper_factory = build_mapper_factory(strategies: [
        build_mapper_strategy(method: :cast,      output: :symbol_key_hash),
        build_mapper_strategy(method: :serialize, output: :symbol_key_hash),
        build_mapper_strategy(method: :db_dump,   output: :string_key_hash),
        build_mapper_strategy(method: :db_load,   output: :object)
      ])

      mapper_factory.register(:cart_user, user_caster)

      mapper_factory
    end
  end

=end