Skip to content

Commit 4ea0ef2

Browse files
committed
Initial push
0 parents  commit 4ea0ef2

File tree

7 files changed

+172
-0
lines changed

7 files changed

+172
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/_build
2+
/deps
3+
erl_crash.dump
4+
*.ez

README.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
PatternTap
2+
==========
3+
4+
##### The pipe operator `|>` is an awesome feature of Elixir.
5+
6+
But when your result cannot be directly input into the next function, you have to stop, pattern match out the value you want and start piping again!
7+
8+
It is a common pattern to return data like `{:ok, result}` or `{:error, reason}`. When you want to handle both cases, theres not much you can do except use another function or a `case`. But otherwise you can `use PatternTap`!
9+
10+
#### Not fun way
11+
12+
```elixir
13+
defmodule Foo do
14+
def get_stuff(input) do
15+
{:ok, intermediate_result} = input
16+
|> Enum.map(&(to_string(&1)))
17+
|> Foo.HardWorker.work
18+
{:ok, result} = intermediate_result
19+
|> Enum.map(&(Foo.IntermediateResult.handle(&1)))
20+
result
21+
end
22+
end
23+
```
24+
25+
Anytime where the object you want requires pattern matching but you want to either return on one line or continue piping, you can `use PatternTap`!
26+
27+
```elixir
28+
{:ok, result} = something |> something_else
29+
result
30+
```
31+
32+
#### Pattern Tap
33+
34+
Heres the above example using `PatternTap`
35+
36+
```elixir
37+
defmodule Foo do
38+
def get_stuff(input) do
39+
input
40+
|> Enum.map(&(to_string(&1)))
41+
|> Foo.HardWorker.work
42+
|> tap(:r1, {:ok, r1})
43+
|> Enum.map(&(Foo.IntermediateResult.handle(&1)))
44+
|> tap(:r2, {:ok, r2})
45+
end
46+
end
47+
```
48+
49+
And the second example
50+
51+
```elixir
52+
something |> something_else |> tap(:result, {:ok, result})
53+
```
54+
55+
### Usage
56+
57+
The `tap/3` macro takes `data, return_variable, pattern` for its three parameters. This takes advantage of Elixir's `binding` call. The variables you create in your pattern will be available even after the tap call. Take this use case for example.
58+
59+
```elixir
60+
[:a] |> tap(:a, [a]) # => Returns :a
61+
IO.puts "#{a}" # The variable a is available
62+
```
63+
64+
The symbol `:a` passed into `tap` is what variable to return. All other variables will be available after the `tap` call, though `tap` will only return a single variable. This means `tap({:ok, 1}, :r, {e, r})` will return `r` (which has the value 1) but in the next statement, the variable `e` will be available (which has the value `:ok`).

config/config.exs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# This file is responsible for configuring your application
2+
# and its dependencies with the aid of the Mix.Config module.
3+
use Mix.Config
4+
5+
# This configuration is loaded before any dependency and is restricted
6+
# to this project. If another project depends on this project, this
7+
# file won't be loaded nor affect the parent project. For this reason,
8+
# if you want to provide default values for your application for third-
9+
# party users, it should be done in your mix.exs file.
10+
11+
# Sample configuration:
12+
#
13+
# config :logger, :console,
14+
# level: :info,
15+
# format: "$date $time [$level] $metadata$message\n",
16+
# metadata: [:user_id]
17+
18+
# It is also possible to import configuration files, relative to this
19+
# directory. For example, you can emulate configuration per environment
20+
# by uncommenting the line below and defining dev.exs, test.exs and such.
21+
# Configuration from the imported file will override the ones defined
22+
# here (which is why it is important to import them last).
23+
#
24+
# import_config "#{Mix.env}.exs"

lib/pattern_tap.ex

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
defmodule PatternTap do
2+
3+
defmacro __using__(_) do
4+
quote do
5+
import PatternTap
6+
end
7+
end
8+
9+
defmacro tap(data, var, pattern) do
10+
quote do
11+
unquote(pattern) = unquote(data)
12+
binding[unquote(var)]
13+
end
14+
end
15+
16+
end

mix.exs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
defmodule PatternTap.Mixfile do
2+
use Mix.Project
3+
4+
def project do
5+
[app: :pattern_tap,
6+
version: "0.1.0",
7+
elixir: "~> 1.0",
8+
deps: deps]
9+
end
10+
11+
# Configuration for the OTP application
12+
#
13+
# Type `mix help compile.app` for more information
14+
def application do
15+
[applications: [:logger]]
16+
end
17+
18+
# Dependencies can be Hex packages:
19+
#
20+
# {:mydep, "~> 0.3.0"}
21+
#
22+
# Or git/path repositories:
23+
#
24+
# {:mydep, git: "https://github.com/elixir-lang/mydep.git", tag: "0.1.0"}
25+
#
26+
# Type `mix help deps` for more examples and options
27+
defp deps do
28+
[]
29+
end
30+
end

test/pattern_tap_test.exs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
defmodule PatternTapTest do
2+
use ExUnit.Case
3+
use PatternTap
4+
5+
test "can do simple pattern matching" do
6+
assert tap(:ok, nil, :ok) == nil
7+
end
8+
9+
test "can do list pattern matching" do
10+
assert tap([:a], :a, [a]) == :a
11+
assert a == :a
12+
end
13+
14+
test "can do tuple pattern matching" do
15+
assert tap({:b}, :b, {b}) == :b
16+
assert b == :b
17+
end
18+
19+
@data [:a, :b, :c]
20+
test "can match with the |> operator" do
21+
assert @data |> Enum.map(&(to_string(&1))) |> tap(:b, [_, b, _]) == "b"
22+
end
23+
24+
@data [key: :val, key2: :val2]
25+
test "can match |> with keyword lists" do
26+
assert @data |> tap(:v, [_, {:key2, v}]) == :val2
27+
end
28+
29+
test "can match typical {:ok, result}" do
30+
assert {:ok, 1} |> tap(:result, {:ok, result}) == 1
31+
end
32+
33+
end

test/test_helper.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ExUnit.start()

0 commit comments

Comments
 (0)