Skip to content

Commit

Permalink
Merge pull request #13 from redwirelabs/add-any-type
Browse files Browse the repository at this point in the history
Add :any attribute type
  • Loading branch information
abelino authored Dec 17, 2024
2 parents 0ac39c5 + 332aac8 commit 4bf9915
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 0 deletions.
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ attribute :serial_number, :string, length: 16
attribute :wifi_ssid, :string, optional: true
attribute :low_power_mode, :boolean, optional: true
attribute :dns_servers, [:string]
attribute :user_data, :any

attribute :metadata do
attribute :location, :string
Expand Down Expand Up @@ -130,6 +131,7 @@ attribute <name>, <type>, <options>
```

Types:
- `any`
- `boolean`
- `integer`
- `float`
Expand Down Expand Up @@ -160,6 +162,37 @@ Options:
- `format` - Regular expression for the valid format of a string.
- error - `wrong_format`

The `any` type:
- Intended to be a passthrough attribute where the value is not validated or coerced but the value must be not `nil` at a minimum if `optional: false`. The `any` type is useful if you want to split out schemas where the top level schema is a generic envelope containing a nested data structure that could possibly be of a polymorphic nature. **Example**:

```elixir
struct MQTT.AWS.Shadow.Update.V1

name "aws_shadow_update"

attribute :state do
attribute :desired, :any
end
```

```elixir
struct MQTT.Light.State.V1

name "light_state"

attribute [:schedule] do
attribute :start, :datetime
attribute :stop, :datetime
end
```

```elixir
with {:ok, shadow} <- Speck.validate(MQTT.AWS.Shadow.Update.V1, payload),
{:ok, light_state} <- Speck.validate(MQTT.Light.State.V1, shadow.state.desired) do
# do something with light_state
end
```

### Examples

Example schemas can be found in [/protocol](https://github.com/amclain/speck/tree/main/protocol).
Expand Down
3 changes: 3 additions & 0 deletions lib/speck.ex
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ defmodule Speck do
end
end

defp do_strict_validate(:any, value, _opts), do: value
defp do_strict_validate(:boolean, value, _opts) when is_boolean(value), do: value
defp do_strict_validate(:integer, value, _opts) when is_integer(value), do: value
defp do_strict_validate(:float, value, _opts) when is_float(value), do: value
Expand Down Expand Up @@ -162,6 +163,8 @@ defmodule Speck do
end)
end

defp do_validate(:any, value, _opts), do: value

defp do_validate(:boolean, value, _opts) when is_boolean(value), do: value

defp do_validate(:integer, value, _opts) when is_integer(value), do: value
Expand Down
7 changes: 7 additions & 0 deletions protocol/test/any.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
struct TestSchema.Any

name "any"

attribute :param1, :any
attribute :param2, :any, strict: true
attribute :param3, :any, optional: true
20 changes: 20 additions & 0 deletions test/speck_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,26 @@ defmodule Speck.Test do
}}
end

test "can coerce a map with attribute type of any" do
params = %{
"param1" => %{},
"param2" => "valid"
}

assert Speck.validate(TestSchema.Any, params) ==
{:ok, %TestSchema.Any{
param1: %{},
param2: "valid"
}}
end

test "returns errors if required params of type any are missing" do
params = %{}

assert Speck.validate(TestSchema.Any, params) ==
{:error, %{param1: :not_present, param2: :not_present}}
end

test "returns errors if items in a list of maps can't be coerced" do
params = %{
"devices" => [
Expand Down

0 comments on commit 4bf9915

Please sign in to comment.