dry-transformer icon indicating copy to clipboard operation
dry-transformer copied to clipboard

Simplified creation of pipes

Open apohllo opened this issue 4 years ago • 0 comments

Provide an example in the user documentation for short pipe definition + provide helper function to make it possible.

Dry::Transformer introduced the idea of wrapping transformation into classes, which I really like. The new syntax with nested block of code is much more readable than the original one with function composition .>>. The problem I see in the new syntax, is the fact that you have to define a new class for each complex transformation. I know the rationale for that, but this seems awkward. I would suggest a simple helper function in the Pipe class, that would make the code more rubish.

Examples

Let's assume we have the following module and a class:

module Transformations
  extend Dry::Transformer::Registry
  import Dry::Transformer::HashTransformations
  import Dry::Transformer::ArrayTransformations
  import Dry::Transformer::Recursion
  import Dry::Transformer::Conditional
  import Dry::Transformer::ClassTransformations
  import Dry::Transformer::ProcTransformations
end

class TPipe < Dry::Transformer::Pipe
  import Transformations
end

The module is defined as an entry point for user-defined, global transformations. The class is defined as a base class for all user-defined pipes. At present defining a new complex transformation requires the following code:

class Service
  def fetch(params)
    converter.call(make_request(params))
  end

  private
  def converter
    Class.new(TPipe) do
      define! do
        map_array do
          rename_keys data: :content
          unwrap :metadata, [:id, :similarity]
        end
      end
    end.new
  end
end

The anonymous class is used to avoid namespace pollution, but the code seems awkward.

We can make it look better with the following definition of TPipe:

class TPipe < Dry::Transformer::Pipe
  import Transformations

  def self.make!(&block)
    Class.new(TPipe) do
      define!(&block)
    end.new
  end
end

The converter then looks as follows:

class Service
  private
  def converter
    TPipe.make! do
      map_array do
        rename_keys data: :content
        unwrap :metadata, [:id, :similarity]
      end
    end
  end
end

As a result it is easier to define new conversions as methods in the local context. I suggest make! was defined in the Dry::Transoformer::Pipe, so we won't have to define it. Alternatively current implementation of define! could be changed to allow the syntax introduced by make!.

apohllo avatar Apr 21 '20 10:04 apohllo