• This repository has been archived on 28/Dec/2019
  • Stars
    star
    410
  • Rank 105,468 (Top 3 %)
  • Language
    Ruby
  • License
    MIT License
  • Created almost 10 years ago
  • Updated almost 5 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

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

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

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

More Repositories

1

virtus

[DISCONTINUED ] Attributes on Steroids for Plain Old Ruby Objects
Ruby
3,767
star
2

rom-relation

This is an old ROM prototype that's no longer developed.
Ruby
363
star
3

coercible

Powerful, flexible and configurable coercion library. And nothing more.
Ruby
137
star
4

transflow

[DISCONTINUED] Business transaction flow DSL
Ruby
101
star
5

charlatan

Neat delegation for ruby objects
Ruby
69
star
6

rom-rails-skeleton

My Rails app skeleton with ROM and other goodies
Ruby
35
star
7

rom-session

DEPRECATED / DEAD / MOVE ON
Ruby
33
star
8

hanami-api-system-template

⚠️ [WIP] ⚠️ hanami-api + dry-system + rom application template
Ruby
18
star
9

hanami-bookshelf-rom

Hanami 1.x bookshelf app based on tutorial, tweaked to use rom-rb 4.x standalone
Ruby
18
star
10

dm-validations-ext

Various additions to the dm-validations API
Ruby
12
star
11

rom-rails-demo

Experimental rails app demo using ROM
Ruby
10
star
12

ossy

Maintenance automation helper as a CLI tool used by dry-rb and rom-rb
Ruby
9
star
13

lotuskase

Custom web stack based on dry-component and lotus
Ruby
8
star
14

architecture-talk-example-app

Ruby
7
star
15

hanami-dockerized-demo

A sample Hanami app with a docker-compose-driven-development-and-CI aka one docker-compose to rule them all.
Ruby
7
star
16

virtus-dirty_tracking

ABANDONED - MOVE ON
Ruby
6
star
17

rom-workshop

Ruby
5
star
18

dm-timezones

Timezones support for DataMapper
5
star
19

dm-is-formattable

Ruby
3
star
20

rodakase

The project that gave birth to dry-(system|view|web)
Ruby
2
star
21

middleman-docsite

Various middleman extensions extracted from rom-rb and dry-rb websites
Ruby
2
star
22

docker-nginx-proxy-example

JavaScript
2
star
23

dm-is-configurable

A DataMapper plugin which adds configuration to resources
Ruby
2
star
24

ruby-devel-containers

WIP
Ruby
1
star
25

dm-gdata-adapters

A set of DataMapper adapters for Google Data API
Ruby
1
star
26

virtus-units

ABANDONED - MOVE ON
Ruby
1
star