8000 Base query option by renatorfr · Pull Request #15 · dmarkow/ecto_ranked · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Base query option #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ To get started:
- ```import EctoRanked```
- Add a `:rank` integer field to your model (NOTE: Setting a unique index on this column may cause issues depending on your database platform)
- Call `set_rank()` in your changeset
- Optionally, add a virtual `:position` field (with a type of `:any`) so you can move items in your list.
- Optionally, add a virtual `:position` field (with a type of `:any`) so you can move items in your list. `:position` accepts `:up`, `:down`, `:first`, `:last` or an integer, the integer being the desired position in a 0-based index.

```elixir
defmodule MyApp.Item do
Expand Down Expand Up @@ -99,6 +99,20 @@ struct
|> set_rank(rank: :global_rank, position: :global_position)
```

You can also pass a `:base_queryable` to scope more complex cases:

```elixir
def changeset(struct, params) do
struct
|> cast(params, [:position])
|> set_rank(base_queryable: &not_deleted_query/2)
end

def not_deleted_query(module, _options) do
from(items in module, where: not is_nil(items.deleted))
end
```

Position is a write-only virtual attribute that's meant for placing an item at
a specific rank. By default the `position` attribute will be `nil` but you can
calculate it on demand:
Expand Down
12 changes: 10 additions & 2 deletions lib/ecto_ranked.ex 8000
8000
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ defmodule EctoRanked do
scope_field: Keyword.get(opts, :scope),
position_field: Keyword.get(opts, :position, :position),
rank_field: Keyword.get(opts, :rank, :rank),
prefix: Keyword.get(opts, :prefix, nil)
prefix: Keyword.get(opts, :prefix, nil),
base_queryable: Keyword.get(opts, :base_queryable)
}

cs
Expand Down Expand Up @@ -294,7 +295,8 @@ defmodule EctoRanked do

defp finder(cs, options) do
query =
options.module
options
|> base_query()
|> scope_query(cs, options.scope_field)

if cs.data.id do
Expand All @@ -304,6 +306,12 @@ defmodule EctoRanked do
end
end

defp base_query(%{base_queryable: base_queryable, module: module} = options)
when not is_nil(base_queryable),
do: apply(base_queryable, [module, options])

defp base_query(options), do: options.module

defp scope_query(query, cs, scope_field) when is_list(scope_field) do
Enum.reduce(scope_field, query, fn field, acc ->
scope_query(acc, cs, field)
Expand Down
10 changes: 10 additions & 0 deletions priv/repo/migrations/20210315102137_base_queryable.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
defmodule EctoRanked.Test.Repo.Migrations.PrefixedModel do
use Ecto.Migration

def change do
create table(:base_queryable_models) do
add :my_field, :string, null: true
add :rank, :integer, null: false
end
end
end
34 changes: 34 additions & 0 deletions test/base_queryable_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
defmodule EctoRanked.BaseQueryableTest do
use EctoRanked.TestCase
import Ecto.Query
alias EctoRa 8000 nked.Test.{BaseQueryableModel, Repo}

def ranked_ids() do
BaseQueryableModel
|> select([m], m.id)
|> order_by(:rank)
|> Repo.all()
end

test "moving an item with a base queryable" do
model1 =
%BaseQueryableModel{}
|> BaseQueryableModel.changeset(%{my_field: "My field"})
|> Repo.insert!()

model2 =
%BaseQueryableModel{}
|> BaseQueryableModel.changeset(%{})
|> Repo.insert!()

model3 =
%BaseQueryableModel{}
|> BaseQueryableModel.changeset(%{my_field: "My field"})
|> Repo.insert!()

model3 =
model3 |> BaseQueryableModel.base_queryable_changeset(%{position: :up}) |> Repo.update!()

assert ranked_ids() == [model3.id, model1.id, model2.id]
end
end
29 changes: 29 additions & 0 deletions test/support/base_queryable_model.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
defmodule EctoRanked.Test.BaseQueryableModel do
use Ecto.Schema
import Ecto.Changeset
import Ecto.Query
import EctoRanked

schema "base_queryable_models" do
field :my_field, :string
field :rank, :integer
field :position, :any, virtual: true
end

def changeset(model, params) do
model
|> cast(params, [:my_field])
|> set_rank()
end

def base_queryable_changeset(model, params) do
model
|> cast(params, [:position])
|> set_rank(base_queryable: &build_base_queryable/2)
end

defp build_base_queryable(module, _options) do
module
|> where([m], not is_nil(m.my_field))
end
end
0