Skip to content

ruby2elixir/plumber_girl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

22 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PlumberGirl takes care of your Elixir piping issues!

Build status Hex version Hex downloads


PlumberGirl provides some macros to enable railway-oriented programming in Elixir.

Collected from code snippets and wrapped into a simple library for your convenience.

For more examples please check the tests here:

Sources for inspiration + copying

Installation

  1. Add rop to your list of dependencies in mix.exs:
def deps do
  [{:plumber_girl, "~> 0.9.5"}]
end

Usage

Call

use PlumberGirl

in your module. That will give you access to following macros/functions:

>>>

No need to stop pipelining in case of an error somewhere in the middle

used like: 1 |> fn1 >>> fn2 >>> fn3 >>> fn4

defmodule TripleArrowExample do
  use PlumberGirl
  def tagged_inc(v) do
    IO.puts "inc for #{v}" # sideeffect for demonstration
    {:ok, v + 1}
  end

  def error_fn(_) do
    {:error, "I'm a bad fn!"}
  end

  def raising_fn(_) do
    raise "I'm raising!"
  end

  def result do
    1 |> tagged_inc >>> tagged_inc >>> tagged_inc
  end

  def error_result do
    1 |> tagged_inc >>> tagged_inc >>> error_fn >>> tagged_inc
  end

  def raising_result do
    1 |> tagged_inc >>> tagged_inc >>> raising_fn >>> tagged_inc
  end
end

iex> TripleArrowExample.result
inc for 1
inc for 2
inc for 3
{:ok, 4}

### increases twice, errors and tries to increase again
### notice that after errored result we don't execute any function anymore in the pipeline,
### e.g. only tagged_inc before error_fn were executed.
iex> TripleArrowExample.error_result
inc for 1
inc for 2
{:error, "I'm a bad fn!"}

### raises... We'll fix it in a later example for try_catch!
iex> TripleArrowExample.raising_result
inc for 1
inc for 2
** (RuntimeError) I'm raising!

bind

Wraps a simple function to return a tagged tuple with :ok to comply to the protocol {:ok, result}: e.g.

defmodule BindExample do
  use PlumberGirl
  def inc(v) do
    v + 1
  end

  def only_last_pipe_tagged_result(v) do
    v |> inc |> bind(inc)
  end

  def result_fully_tagged(v) do
    v |> bind(inc) >>> bind(inc) >>> bind(inc)
  end
end
iex> BindExample.only_last_pipe_tagged_result(2)
{:ok, 4}

iex> BindExample.result_fully_tagged(2)
{:ok, 5}

try_catch

Wraps raising functions to return a tagged tuple {:error, ErrorMessage} to comply with the protocol

# modified example from TripleArrowExample to handle raising functions
defmodule TryCatchExample do
  use PlumberGirl
  def tagged_inc(v) do
    IO.puts "inc for #{v}" # sideeffect for demonstration
    {:ok, v + 1}
  end

  def raising_fn(_) do
    raise "I'm raising!"
  end

  def result do
    1 |> tagged_inc >>> tagged_inc >>> tagged_inc
  end

  def raising_result_wrapped(v) do
    v |> tagged_inc >>> tagged_inc >>> try_catch(raising_fn) >>> tagged_inc
  end
end

iex> TryCatchExample.raising_result_wrapped(1)
inc for 1
inc for 2
{:error, %RuntimeError{message: "I'm raising!"}}

tee

Like a similar Unix utility it does some work and returns the input. See tee (command), Unix.

defmodule TeeExample do
  use PlumberGirl
  def tagged_inc(v) do
    IO.puts "inc for #{v}" # sideeffect for demonstration
    {:ok, v + 1}
  end

  def calc(v) do
    v |> tee(tagged_inc) >>> tee(tagged_inc) >>> tee(tagged_inc)
  end
end

# notice how the incremented value is not passed through the pipeline,
# but just the original argument `1`
iex> TeeExample.calc(1)
inc for 1
inc for 1
inc for 1
_{:ok, 1}

ok

A simple utility function to extract the value from {:ok, result} tuple and to raise the error in {:error, ErrorStruct}.

defmodule OkExample do
  use PlumberGirl
  def ok_result do
    {:ok, 1} |> ok
  end

  def error_result do
    {:error, %ArithmeticError{}} |> ok
  end

  def any_value_result do
    "bad value" |> ok
  end
end

iex> OkExample.ok_result
1

iex> OkExample.error_result
** (ArithmeticError) bad argument in arithmetic expression
    (rop) lib/rop.ex:70: Rop.ok/1


iex> OkExample.any_value_result
** (RuntimeError) bad value
    (rop) lib/rop.ex:71: Rop.ok/1

Background information

Some discussions about Railsway programming:

Code (Railway Programming)

Code (Monads)

Why Railway Oriented Programming might be simpler than Monads

Alternatives

About

PlumberGirl takes care of your Elixir piping issues!

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages