Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ by adding `expression` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:expression, "~> 3.0.0-rc.0"}
{:expression, "~> 3.0.0-rc.1"}
]
end
```
Expand Down
14 changes: 11 additions & 3 deletions lib/expression/eval.ex
Original file line number Diff line number Diff line change
Expand Up @@ -390,8 +390,16 @@ defmodule Expression.Eval do
defp case_insensitive_scan(map, key) do
downcased = String.downcase(key)

Enum.find_value(map, {:not_found, [key]}, fn {k, v} ->
if is_binary(k) and String.downcase(k) == downcased, do: v
end)
# Wrap matches in a tuple so that falsy values (`false`, `nil`) are not
# mistaken by `find_value/3` for "no match".
match =
Enum.find_value(map, {:error, :not_found}, fn {k, v} ->
if is_binary(k) and String.downcase(k) == downcased, do: {:ok, v}
end)

case match do
{:ok, value} -> value
{:error, :not_found} -> {:not_found, [key]}
end
end
end
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule Expression.MixProject do
use Mix.Project

@version "3.0.0-rc.0"
@version "3.0.0-rc.1"

def project do
[
Expand Down
52 changes: 52 additions & 0 deletions test/expression_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,58 @@ defmodule ExpressionTest do
})
end

test "case-insensitive lookup of a camelCase key holding false" do
context = %{"contact" => %{"isActive" => false}}

assert Expression.evaluate("@contact.isactive", context) == {:ok, false}
assert Expression.evaluate("@contact.isActive", context) == {:ok, false}
assert Expression.evaluate("@contact.ISACTIVE", context) == {:ok, false}
end

test "case-insensitive lookup of a top-level key holding false" do
assert Expression.evaluate("@enabled", %{"Enabled" => false}) == {:ok, false}
end

test "case-insensitive lookup of false through a multi-level attribute chain" do
assert Expression.evaluate("@a.b.c", %{"A" => %{"B" => %{"C" => false}}}) == {:ok, false}
end

test "case-insensitive lookup of other falsy values" do
context = %{
"map" => %{
"itemCount" => 0,
"emptyTags" => [],
"blankNote" => "",
"zeroRate" => 0.0
}
}

assert Expression.evaluate("@map.itemcount", context) == {:ok, 0}
assert Expression.evaluate("@map.emptytags", context) == {:ok, []}
assert Expression.evaluate("@map.blanknote", context) == {:ok, ""}
assert Expression.evaluate("@map.zerorate", context) == {:ok, 0.0}
end

test "a missing key is still reported as not found alongside a false sibling" do
context = %{"contact" => %{"isActive" => false}}

assert Expression.evaluate_block("contact.missing", context) ==
{:ok, {:not_found, ["missing"]}}

assert_raise Expression.Error, "attribute is not found: `missing`", fn ->
Expression.evaluate_block!("contact.missing > 0", context)
end
end

test "a case-insensitively resolved false is usable downstream" do
context = %{"contact" => %{"isActive" => false}}

assert Expression.evaluate_block!("contact.isactive == false", context) == true
assert Expression.evaluate_as_boolean!("@contact.isactive", context) == false
assert Expression.evaluate!(~S|@IF(contact.isactive, "on", "off")|, context) == "off"
assert Expression.evaluate_as_string!("@contact.isactive", context) == "false"
end

test "delete an element from a map" do
assert {:ok, %{"age" => 32}} ==
Expression.evaluate("@delete(patient, \"gender\")", %{
Expand Down