Skip to content

Commit 793cf27

Browse files
committed
optimize Req pipes
1 parent bb0cde8 commit 793cf27

4 files changed

Lines changed: 88 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,23 @@ they can and will change without that change being reflected in Styler's semanti
55

66
## main
77

8+
### Improvements
9+
10+
#### Req pipe optimizations
11+
12+
[Req](https://github.com/wojtekmach/req) is a popular HTTP Client. If you aren't using it, you can just ignore this whole section!
13+
14+
Reqs 1-arity "execute the request" functions (`delete get head patch post put request run`) have a 2-arity version that takes a superset of the arguments `Req.new/1` does as its first argument, and the typical `options` keyword list as its second argument. And so, many places developers are calling a 1-arity function can be replaced with a 2-arity function.
15+
16+
More succinctly, these two statements are equivalent:
17+
18+
- `foo |> Req.new() |> Req.merge(bar) |> Req.post!()`
19+
- `Req.post!(foo, bar)`
20+
21+
Styler now rewrites the former to the latter, since "less is more" or "code is a liability".
22+
23+
It also rewrites `|> Keyword.merge(bar) |> Req.foo()` to `|> Req.foo(bar)`. **This changes the program's behaviour**, since `Keyword.merge` would overwrite existing values in all cases, whereas `Req` 2-arity functions intelligently deep-merge values for some keys, like `:headers`.
24+
825
## 1.9.1
926

1027
### Fix

docs/pipes.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,23 @@ d(c(a |> b))
153153
# At which point Styler will pipe-ify the entire chain
154154
a |> b() |> c() |> d()
155155
```
156+
157+
## Req
158+
159+
[Req](https://github.com/wojtekmach/req) is a popular HTTP Client. If you aren't using it, you can just ignore this whole section!
160+
161+
Styler ensures a minimal number of functions are being called when using any Req 1-arity execution functions (`delete get head patch post put request run` and their bangified versions).
162+
163+
```elixir
164+
# before
165+
keyword |> Req.new() |> Req.merge(opts) |> Req.post!()
166+
# Styled:
167+
Req.post!(keyword, opts)
168+
169+
# before
170+
foo |> Keyword.merge(opts) |> Req.head()
171+
# Styled:
172+
Req.head(foo, opts)
173+
```
174+
175+
**This changes the program's behaviour**, since `Keyword.merge` would overwrite existing values in all cases, whereas `Req` 2-arity functions intelligently deep-merge values for some keys, like `:headers`.

lib/style/pipes.ex

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,30 @@ defmodule Styler.Style.Pipes do
413413
{:|>, pm, [lhs, {Style.set_line(new, em[:line]), em, [mapper]}]}
414414
end
415415

416+
@req2 for fun <- ~w(delete get head patch post put request run), bang <- ["", "!"], fun = :"#{fun}#{bang}", do: fun
417+
418+
# rewrite `Keyword.merge(opt) |> Req.fun1()` to `Req.fun2(opt)` for 2 arity functions that take `opts` as a second arg
419+
defp fix_pipe(
420+
pipe_chain(
421+
pm,
422+
lhs,
423+
{{:., _, [{_, _, [req_or_kw]}, :merge]}, m, [kw]},
424+
{{:., _, [{_, _, [:Req]}, fun]} = req, _, []}
425+
)
426+
)
427+
when req_or_kw in [:Req, :Keyword] and fun in @req2 do
428+
fix_pipe({:|>, pm, [lhs, {req, m, [kw]}]})
429+
end
430+
431+
# Req.new |> Req.fun1,2 -> Req.fun1,2
432+
# all `fun` options take the same args as `Req.new`, so it's redundant to call Req.new before them
433+
defp fix_pipe(
434+
pipe_chain(pm, lhs, {{:., _, [{_, _, [:Req]}, :new]}, m, []}, {{:., _, [{_, _, [:Req]}, fun]} = req, _, args})
435+
)
436+
when fun in @req2 do
437+
{:|>, pm, [lhs, {req, m, args}]}
438+
end
439+
416440
defp fix_pipe(node), do: node
417441

418442
defp valid_pipe_start?({op, _, _}) when op in @special_ops, do: true

test/style/pipes_test.exs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,4 +1004,31 @@ defmodule Styler.Style.PipesTest do
10041004
)
10051005
end
10061006
end
1007+
1008+
describe "Req pipes" do
1009+
@req2 for fun <- ~w(delete get head patch post put request run), bang <- ["", "!"], fun = :"#{fun}#{bang}", do: fun
1010+
1011+
test "X.merge |> Req.foo/1 -> Req.foo/2" do
1012+
for fun <- @req2, merger <- ~w(Req Keyword) do
1013+
assert_style "foo |> #{merger}.merge(opts) |> Req.#{fun}()", "Req.#{fun}(foo, opts)"
1014+
assert_style "a |> b |> #{merger}.merge(opts) |> Req.#{fun}()", "a |> b() |> Req.#{fun}(opts)"
1015+
end
1016+
end
1017+
1018+
test "Req.new |> Req.foo/1 -> Req.foo/2" do
1019+
for fun <- @req2 do
1020+
assert_style "foo |> Req.new() |> Req.#{fun}()", "Req.#{fun}(foo)"
1021+
assert_style "a |> b |> Req.new() |> Req.#{fun}()", "a |> b() |> Req.#{fun}()"
1022+
assert_style "foo |> Req.new() |> Req.#{fun}(c)", "Req.#{fun}(foo, c)"
1023+
assert_style "a |> b |> Req.new() |> Req.#{fun}(c)", "a |> b() |> Req.#{fun}(c)"
1024+
end
1025+
end
1026+
1027+
test "new |> merge |> foo" do
1028+
for fun <- @req2 do
1029+
assert_style "foo |> Req.new() |> Req.merge(bar) |> Req.#{fun}()", "Req.#{fun}(foo, bar)"
1030+
assert_style "a |> b() |> Req.new() |> Req.merge(bar) |> Req.#{fun}()", "a |> b() |> Req.#{fun}(bar)"
1031+
end
1032+
end
1033+
end
10071034
end

0 commit comments

Comments
 (0)