-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Change interpolation syntax to be similar to string interpolation
After a discussion with Ecto maintainers * elixir-ecto/ecto#4239 (comment) * https://elixirforum.com/t/how-to-pass-named-or-numbered-parameters-to-ectos-fragment/49525/5?u=tessi * https://groups.google.com/g/elixir-ecto/c/gEqI9lE3HGE it seems like this feature will not be part of Ecto proper. However, the discussion revealed a better implementation of this library (thanks @bamorim !) by using string interpolation instead of atom-like names. This gives: * better syntax highlighting for param names within fragment query strings * better compilation time (for usage of the macro as well as for this lib) * less dependencies for this library
- Loading branch information
Showing
11 changed files
with
192 additions
and
153 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# Changelog | ||
|
||
All notable changes to this project will be documented in this file. | ||
|
||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | ||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | ||
|
||
Types of changes | ||
|
||
- `Added` for new features. | ||
- `Changed` for changes in existing functionality. | ||
- `Deprecated` for soon-to-be removed features. | ||
- `Removed` for now removed features. | ||
- `Fixed` for any bug fixes. | ||
- `Security` in case of vulnerabilities. | ||
|
||
## unreleased | ||
|
||
put your changes here | ||
|
||
## [0.1.0] - 2023-07-24 | ||
|
||
### Changed | ||
|
||
* the `EctoNamedFragment` module needs to be imported now (no more `use`) | ||
* changed interpolation syntax to use string interpolation (#{}). Please change your query strings from `named_fragment("foo(:a, :b)", a:1, b:2)` to `named_fragment("foo(#{:a}, #{:b})", a:1, b:2)` | ||
* this allows better syntax highlighting of param names within the query | ||
* it also drastically simplifies implementation of this library and improves compile times of this library as well as of the named_fragment macro | ||
|
||
|
||
## [0.1.0] - 2023-07-17 | ||
|
||
### Added | ||
|
||
* initial release |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
defmodule EctoNamedFragment.ConvertToEctoFragment do | ||
@moduledoc false | ||
|
||
import EctoNamedFragment.Exceptions, only: [error!: 1] | ||
|
||
def call({:<<>>, _, pieces}, params) do | ||
if not Keyword.keyword?(params) do | ||
error!( | ||
"named_fragment(...) expect a keyword list as the last argument, got: #{Macro.to_string(params)}" | ||
) | ||
end | ||
|
||
query = | ||
pieces | ||
|> Enum.map(fn | ||
"" <> binary -> binary | ||
_ -> "?" | ||
end) | ||
|> Enum.join() | ||
|
||
frags = | ||
Enum.flat_map(pieces, fn | ||
{:"::", _, [{_, _, [val]} | _]} when is_atom(val) -> | ||
[Keyword.fetch!(params, val)] | ||
|
||
{:"::", _, [{_, _, [val]} | _]} -> | ||
error!( | ||
"names in named_fragment(...) queries must be atoms, got: #{Macro.to_string(val)}" | ||
) | ||
|
||
_ -> | ||
[] | ||
end) | ||
|
||
{query, frags} | ||
end | ||
end |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
defmodule EctoNamedFragment.Exceptions do | ||
defmodule CompileError do | ||
@moduledoc """ | ||
Raised at compilation time when the named fragment cannot be compiled. | ||
""" | ||
defexception [:message] | ||
end | ||
|
||
def error!(message) when is_binary(message) do | ||
{:current_stacktrace, [_ | t]} = Process.info(self(), :current_stacktrace) | ||
|
||
t = | ||
Enum.drop_while(t, fn | ||
{mod, _, _, _} -> | ||
String.starts_with?(Atom.to_string(mod), ["Elixir.EctoNamedFragment."]) | ||
|
||
_ -> | ||
false | ||
end) | ||
|
||
reraise CompileError, [message: message], t | ||
end | ||
end |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
68 changes: 68 additions & 0 deletions
68
test/ecto_named_fragment/convert_to_ecto_fragment_test.exs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
defmodule ConvertToEctoFragmentTest do | ||
use ExUnit.Case | ||
doctest EctoNamedFragment | ||
|
||
alias EctoNamedFragment.ConvertToEctoFragment | ||
alias EctoNamedFragment.Exceptions.CompileError | ||
|
||
test "builds a fragment query string and splits params from kw list" do | ||
assert ConvertToEctoFragment.call( | ||
quote do | ||
"foo(#{:a}, #{:b})" | ||
end, | ||
a: 0, | ||
b: 1 | ||
) == | ||
{"foo(?, ?)", [0, 1]} | ||
end | ||
|
||
test "allows repeated param names" do | ||
assert ConvertToEctoFragment.call( | ||
quote do | ||
"foo(#{:a}, #{:b}, #{:a})" | ||
end, | ||
a: 0, | ||
b: 1 | ||
) == | ||
{"foo(?, ?, ?)", [0, 1, 0]} | ||
end | ||
|
||
test "raises for unknown params" do | ||
assert_raise KeyError, | ||
"key :b not found in: [a: 0]", | ||
fn -> | ||
ConvertToEctoFragment.call( | ||
quote do | ||
"foo(#{:a}, #{:b})" | ||
end, | ||
a: 0 | ||
) | ||
end | ||
end | ||
|
||
test "raises on non-atom names" do | ||
assert_raise CompileError, | ||
"names in named_fragment(...) queries must be atoms, got: a", | ||
fn -> | ||
ConvertToEctoFragment.call( | ||
quote do | ||
"foo(#{a})" | ||
end, | ||
a: 0 | ||
) | ||
end | ||
end | ||
|
||
test "raises on non-kw name lists" do | ||
assert_raise CompileError, | ||
"named_fragment(...) expect a keyword list as the last argument, got: [\"a\"]", | ||
fn -> | ||
ConvertToEctoFragment.call( | ||
quote do | ||
"foo(#{:a})" | ||
end, | ||
["a"] | ||
) | ||
end | ||
end | ||
end |
25 changes: 0 additions & 25 deletions
25
test/ecto_named_fragment/convert_to_positioned_args_test.exs
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.