Need help with transproc?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

solnic
418 Stars 30 Forks MIT License 491 Commits 0 Opened issues

Description

The project was ported to dry-rb/dry-transformer

Services available

!
?

Need anything else?

Contributors list

No Data

:warning: The project was ported to dry-rb/dry-transformer :warning:

Transproc

Gem Version Build Status Code Climate Test Coverage Inline docs

Transproc is a small library that allows you to compose procs into a functional pipeline using left-to-right function composition.

The approach came from Functional Programming, where simple functions are composed into more complex functions in order to transform some data. It works like

|>
in Elixir or
>>
in F#.

transproc
provides a mechanism to define and compose transformations, along with a number of built-in transformations.

It's currently used as the data mapping backend in Ruby Object Mapper.

Installation

Add this line to your application's Gemfile:

gem 'transproc'

And then execute:

$ bundle

Or install it yourself as:

$ gem install transproc

Basics

Simple transformations are defined as easy as:

increment = Transproc::Function.new(-> (data) { data + 1 })
increment[1] # => 2

It's easy to compose transformations:

to_string = Transproc::Function.new(:to_s.to_proc)
(increment >> to_string)[1] # => '2'

It's easy to pass additional arguments to transformations:

append = Transproc::Function.new(-> (value, suffix) { value + suffix })
append_bar = append.with('_bar')
append_bar['foo'] # => foo_bar

Or even accept another transformation as an argument:

map_array = Transproc::Function.new(-> (array, fn) { array.map(&fn) })
map_array.with(to_string).call([1, 2, 3]) # => ['1', '2', '3']

To improve this low-level definition, you can use class methods with

Transproc::Registry
:
M = Module.new do
  extend Transproc::Registry

def self.to_string(value) value.to_s end

def self.map_array(array, fn) array.map(&fn) end end M[:map_array, M[:to_string]].([1, 2, 3]) # => ['1', '2', '3']

Built-in transformations

transproc
comes with a lot of built-in functions. They come in the form of modules with class methods, which you can import into a registry:

You can import everything with:

module T
  extend Transproc::Registry

import Transproc::Coercions import Transproc::ArrayTransformations import Transproc::HashTransformations import Transproc::ClassTransformations import Transproc::ProcTransformations import Transproc::Conditional import Transproc::Recursion end T[:to_string].call(:abc) # => 'abc'

Or import selectively with:

module T
  extend Transproc::Registry

import :to_string, from: Transproc::Coercions, as: :stringify end T[:stringify].call(:abc) # => 'abc' T[:to_string].call(:abc)

=> Transproc::FunctionNotFoundError: No registered function T[:to_string]

Transformer

Transformer is a class-level DSL for composing transformation pipelines, for example:

T = Class.new(Transproc::Transformer) do
  map_array do
    symbolize_keys
    rename_keys user_name: :name
    nest :address, [:city, :street, :zipcode]
  end
end

T.new.call( [ { 'user_name' => 'Jane', 'city' => 'NYC', 'street' => 'Street 1', 'zipcode' => '123' } ] )

=> [{:name=>"Jane", :address=>{:city=>"NYC", :street=>"Street 1", :zipcode=>"123"}}]

It converts every method call to its corresponding transformation, and joins these transformations into a transformation pipeline (a transproc).

Transproc Example Usage

require 'json'
require 'transproc/all'

create your own local registry for transformation functions

module Functions extend Transproc::Registry end

import necessary functions from other transprocs...

module Functions

import all singleton methods from a module/class

import Transproc::HashTransformations import Transproc::ArrayTransformations end

...or from any external library

require 'inflecto' module Functions

import only necessary singleton methods from a module/class

and rename them locally

import :camelize, from: Inflecto, as: :camel_case end

def t(*args) Functions[*args] end

use imported transformation

transformation = t(:camel_case)

transformation.call 'i_am_a_camel'

=> "IAmACamel"

transformation = t(:map_array, ( t(:symbolize_keys).>> t(:rename_keys, user_name: :user) )).>> t(:wrap, :address, [:city, :street, :zipcode])

transformation.call( [ { 'user_name' => 'Jane', 'city' => 'NYC', 'street' => 'Street 1', 'zipcode' => '123' } ] )

=> [{:user=>"Jane", :address=>{:city=>"NYC", :street=>"Street 1", :zipcode=>"123"}}]

define your own composable transformation easily

transformation = t(-> v { JSON.dump(v) })

transformation.call(name: 'Jane')

=> "{"name":"Jane"}"

...or add it to registered functions via singleton method of the registry

module Functions

...

def self.load_json(v) JSON.load(v) end end

...or add it to registered functions via .register method

Functions.register(:load_json) { |v| JSON.load(v) }

transformation = t(:load_json) >> t(:map_array, t(:symbolize_keys))

transformation.call('[{"name":"Jane"}]')

=> [{ :name => "Jane" }]

Credits

This project is inspired by the work of the following people:

Contributing

  1. Fork it ( https://github.com/solnic/transproc/fork )
  2. Create your feature branch (
    git checkout -b my-new-feature
    )
  3. Commit your changes (
    git commit -am 'Add some feature'
    )
  4. Push to the branch (
    git push origin my-new-feature
    )
  5. Create a new Pull Request

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.