Skip to content

Commit

Permalink
Merge pull request #18 from woylie/default-limit
Browse files Browse the repository at this point in the history
add default limit option
  • Loading branch information
woylie authored May 22, 2020
2 parents 1498c41 + 9fab885 commit 5a379b4
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 25 deletions.
7 changes: 4 additions & 3 deletions .dialyzer_ignore.exs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[
{"lib/flop/schema.ex:69:no_return Function filterable/1 has no local return."},
{"lib/flop/schema.ex:78:no_return Function max_limit/1 has no local return."},
{"lib/flop/schema.ex:87:no_return Function sortable/1 has no local return."}
{"lib/flop/schema.ex:73:no_return Function filterable/1 has no local return."},
{"lib/flop/schema.ex:82:no_return Function sortable/1 has no local return."},
{"lib/flop/schema.ex:91:no_return Function default_limit/1 has no local return."},
{"lib/flop/schema.ex:100:no_return Function max_limit/1 has no local return."}
]
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## [0.3.0] - 2020-05-22

### Added

- Added a `default_limit` option to `Flop.Schema`.

## [0.2.0] - 2020-05-20

### Added
Expand Down
23 changes: 23 additions & 0 deletions lib/flop.ex
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ defmodule Flop do
)
|> validate_sortable(opts[:for])
|> validate_page_and_page_size(opts[:for])
|> put_default_limit(opts[:for])
end

@spec validate_exclusive(Changeset.t(), [[atom]], keyword) :: Changeset.t()
Expand Down Expand Up @@ -407,4 +408,26 @@ defmodule Flop do
do: changeset,
else: validate_number(changeset, field, less_than_or_equal_to: max_limit)
end

defp put_default_limit(changeset, nil), do: changeset

defp put_default_limit(%Changeset{valid?: false} = changeset, _),
do: changeset

defp put_default_limit(changeset, module) do
default_limit = module |> struct() |> default_limit()

if is_nil(default_limit) do
changeset
else
limit = get_field(changeset, :limit)
page_size = get_field(changeset, :page_size)

if is_nil(limit) && is_nil(page_size) do
put_change(changeset, :limit, default_limit)
else
changeset
end
end
end
end
67 changes: 46 additions & 21 deletions lib/flop/schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,19 @@ defprotocol Flop.Schema do
[validation: :subset, enum: [:name, :age, :species]]}
]
### Defining a maximum limit
### Defining default and maximum limits
To define a maximum limit, you can set the `max_limit` option when deriving
`Flop.Schema`. The limit will be validated accordingly by `Flop.validate/1`.
To define a default or maximum limit, you can set the `default_limit` and
`max_limit` option when deriving `Flop.Schema`. The maximum limit will be
validated and the default limit applied by `Flop.validate/1`.
@derive {Flop.Schema,
filterable: [:name, :species],
sortable: [:name, :age],
max_limit: 100}
filterable: [:name, :species],
sortable: [:name, :age],
max_limit: 100,
default_limit: 50}
"""

@fallback_to_any true
Expand All @@ -68,15 +72,6 @@ defprotocol Flop.Schema do
@spec filterable(any) :: [atom]
def filterable(data)

@doc """
Returns the maximum limit of a schema.
iex> Flop.Schema.max_limit(%Pet{})
20
"""
@spec max_limit(any) :: pos_integer | nil
def max_limit(data)

@doc """
Returns the sortable fields of a schema.
Expand All @@ -85,6 +80,24 @@ defprotocol Flop.Schema do
"""
@spec sortable(any) :: [atom]
def sortable(data)

@doc """
Returns the default limit of a schema.
iex> Flop.Schema.default_limit(%Fruit{})
50
"""
@spec default_limit(any) :: pos_integer | nil
def default_limit(data)

@doc """
Returns the maximum limit of a schema.
iex> Flop.Schema.max_limit(%Pet{})
20
"""
@spec max_limit(any) :: pos_integer | nil
def max_limit(data)
end

defimpl Flop.Schema, for: Any do
Expand Down Expand Up @@ -112,6 +125,7 @@ defimpl Flop.Schema, for: Any do
if is_nil(filterable_fields) || is_nil(sortable_fields),
do: raise(ArgumentError, @instructions)

default_limit = Keyword.get(options, :default_limit)
max_limit = Keyword.get(options, :max_limit)

quote do
Expand All @@ -120,13 +134,17 @@ defimpl Flop.Schema, for: Any do
unquote(filterable_fields)
end

def max_limit(_) do
unquote(max_limit)
end

def sortable(_) do
unquote(sortable_fields)
end

def default_limit(_) do
unquote(default_limit)
end

def max_limit(_) do
unquote(max_limit)
end
end
end
end
Expand All @@ -138,14 +156,21 @@ defimpl Flop.Schema, for: Any do
description: @instructions
end

def max_limit(struct) do
def sortable(struct) do
raise Protocol.UndefinedError,
protocol: @protocol,
value: struct,
description: @instructions
end

def sortable(struct) do
def default_limit(struct) do
raise Protocol.UndefinedError,
protocol: @protocol,
value: struct,
description: @instructions
end

def max_limit(struct) do
raise Protocol.UndefinedError,
protocol: @protocol,
value: struct,
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule Flop.MixProject do
def project do
[
app: :flop,
version: "0.2.0",
version: "0.3.0",
elixir: "~> 1.8",
start_permanent: Mix.env() == :prod,
elixirc_paths: elixirc_paths(Mix.env()),
Expand Down
30 changes: 30 additions & 0 deletions test/flop_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,36 @@ defmodule FlopTest do
]
end

test "applies default limit" do
# struct without configured default limit

assert {:ok, %Flop{limit: nil, page_size: nil}} =
Flop.validate(%{}, for: Pet)

assert {:ok, %Flop{limit: nil, page_size: nil}} =
Flop.validate(%{offset: 10}, for: Pet)

assert {:ok, %Flop{limit: 12}} =
Flop.validate(%{offset: 10, limit: 12}, for: Pet)

assert {:ok, %Flop{limit: nil, page_size: 5}} =
Flop.validate(%{page: 10, page_size: 5}, for: Pet)

# struct with configured default limit

assert {:ok, %Flop{limit: 50, page_size: nil}} =
Flop.validate(%{}, for: Fruit)

assert {:ok, %Flop{limit: 50, page_size: nil}} =
Flop.validate(%{offset: 10}, for: Fruit)

assert {:ok, %Flop{limit: 12, page_size: nil}} =
Flop.validate(%{offset: 10, limit: 12}, for: Fruit)

assert {:ok, %Flop{limit: nil, page_size: 12}} =
Flop.validate(%{page: 10, page_size: 12}, for: Pet)
end

test "validates offset" do
params = %{offset: -1}
assert {:error, %Changeset{} = changeset} = Flop.validate(params)
Expand Down
18 changes: 18 additions & 0 deletions test/support/fruit.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
defmodule Fruit do
@moduledoc """
Defines an Ecto schema for testing.
"""
use Ecto.Schema

@derive {Flop.Schema,
filterable: [:name, :species],
sortable: [:name, :age, :species],
default_limit: 50}

embedded_schema do
field :name, :string
field :age, :integer
field :species, :string
field :social_security_number, :string
end
end

0 comments on commit 5a379b4

Please sign in to comment.