Skip to content
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
4 changes: 4 additions & 0 deletions .formatter.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]
2 changes: 2 additions & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
elixir 1.17.3
erlang 26.2.5.6
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,20 +130,31 @@ AsNestedSet.roots(Taxon, %{taxonomy_id: 1}) |> AsNestedSet.execute(TestRepo)
# find all children of target
AsNestedSet.children(target) |> AsNestedSet.execute(TestRepo)

# find children of a target filtered by an Ecto query
import Ecto.Query, only: [from: 2]
...
from(r in Records, where: r.public == true) |> AsNestedSet.children(target) |> AsNestedSet.execute(TestRepo)

# find all the leaves for given scope
AsNestedSet.leaves(Taxon, %{taxonomy_id: 1}) |> AsNestedSet.execute(TestRepo)

# find all descendants
AsNestedSet.descendants(target) |> AsNestedSet.execute(TestRepo)

# filtered descendants
from(r in Records, where: r.public == true) |> AsNestedSet.descendants(target) |> AsNestedSet.execute(TestRepo)

# include self
AsNestedSet.self_and_descendants(target) |> AsNestedSet.execute(TestRepo)

# find all ancestors
AsNestedSet.ancestors(target) |> AsNestedSet.execute(TestRepo)

#find all siblings (self included)
# find all siblings (self included)
AsNestedSet.self_and_siblings(target) |> AsNestedSet.execute(TestRepo)

# find siblings filtered by a query (self included)
from(r in Records, where: r.public == true) |> AsNestedSet.self_and_siblings(target) |> AsNestedSet.execute(TestRepo)
```

Traverse the tree
Expand Down
4 changes: 2 additions & 2 deletions config/config.exs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
use Mix.Config
import Config

# This configuration is loaded before any dependency and is restricted
# to this project. If another project depends on this project, this
Expand Down Expand Up @@ -28,6 +28,6 @@ use Mix.Config
# here (which is why it is important to import them last).
#

if Mix.env == :test do
if Mix.env() == :test do
import_config "test.exs"
end
4 changes: 3 additions & 1 deletion config/test.exs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use Mix.Config
import Config

config :as_nested_set, ecto_repos: [AsNestedSet.TestRepo]

config :as_nested_set, AsNestedSet.TestRepo,
hostname: "localhost",
Expand Down
2 changes: 1 addition & 1 deletion config/test.exs.travis
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use Mix.Config
import Config

config :as_nested_set, AsNestedSet.TestRepo,
hostname: "localhost",
Expand Down
11 changes: 9 additions & 2 deletions lib/as_nested_set.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
defmodule AsNestedSet do
defmacro __using__(args) do
scope = Keyword.get(args, :scope, [])

quote do
use AsNestedSet.Model
use AsNestedSet.Scoped, scope: unquote(scope)
Expand All @@ -9,7 +10,7 @@ defmodule AsNestedSet do

@type t :: struct

@type executable :: (Ecto.Repo.t -> any)
@type executable :: (Ecto.Repo.t() -> any)

@spec defined?(struct) :: boolean
def defined?(struct) when is_atom(struct) do
Expand All @@ -21,12 +22,14 @@ defmodule AsNestedSet do
false
end
end

def defined?(%{__struct__: struct}) do
defined?(struct)
end

def defined?(_), do: false

@spec execute(executable, Ecto.Repo.t) :: any
@spec execute(executable, Ecto.Repo.t()) :: any
def execute(call, repo) do
call.(repo)
end
Expand All @@ -39,13 +42,17 @@ defmodule AsNestedSet do
defdelegate move(model, position), to: AsNestedSet.Modifiable

defdelegate self_and_siblings(target), to: AsNestedSet.Queriable
defdelegate self_and_siblings(query, target), to: AsNestedSet.Queriable
defdelegate ancestors(target), to: AsNestedSet.Queriable
defdelegate self_and_descendants(target), to: AsNestedSet.Queriable
defdelegate self_and_descendants(query, target), to: AsNestedSet.Queriable
defdelegate root(module, scope), to: AsNestedSet.Queriable
defdelegate roots(module, scope), to: AsNestedSet.Queriable
defdelegate descendants(target), to: AsNestedSet.Queriable
defdelegate descendants(query, target), to: AsNestedSet.Queriable
defdelegate leaves(module, scope), to: AsNestedSet.Queriable
defdelegate children(target), to: AsNestedSet.Queriable
defdelegate children(query, target), to: AsNestedSet.Queriable
defdelegate dump(module, scope), to: AsNestedSet.Queriable
defdelegate dump(module, scope, parent_id), to: AsNestedSet.Queriable
defdelegate dump_one(module, scope), to: AsNestedSet.Queriable
Expand Down
5 changes: 1 addition & 4 deletions lib/as_nested_set/helper.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
defmodule AsNestedSet.Helper do

def get_column_name(%{__struct__: struct}, field) do
get_column_name(struct, field)
end
Expand All @@ -19,6 +18,4 @@ defmodule AsNestedSet.Helper do
def fields(module) when is_atom(module) do
module.__as_nested_set_fields__()
end


end
end
40 changes: 23 additions & 17 deletions lib/as_nested_set/model.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
defmodule AsNestedSet.Model do

defmacro __using__(_) do
quote do
@node_id_column :id
Expand All @@ -18,36 +17,43 @@ defmodule AsNestedSet.Model do
end

defp define_accessors(names, env) do
fields = Enum.map(names, fn
name ->
attribute_name = String.to_atom("#{name}_column")
column_name = Module.get_attribute(env.module, attribute_name)
{name, column_name}
end) |> Enum.into(%{})
fields =
Enum.map(names, fn
name ->
attribute_name = String.to_atom("#{name}_column")
column_name = Module.get_attribute(env.module, attribute_name)
{name, column_name}
end)
|> Enum.into(%{})

Enum.map(fields, fn
{name, column_name}->
{name, column_name} ->
quote do
def __as_nested_set_column_name__(unquote(name)) do
unquote(column_name)
end

def __as_nested_set_get_field__(model, unquote(name)) do
Map.get(model, unquote(column_name))
end

def __as_nested_set_set_field__(model, unquote(name), value) do
Map.put(model, unquote(column_name), value)
end
end
end) ++ [
quote do
def __as_nested_set_field__(field) do
raise ArgumentError, "Unknown AsNestedSet field #{inspect field} for #{inspect __MODULE__}"
end) ++
[
quote do
def __as_nested_set_field__(field) do
raise ArgumentError,
"Unknown AsNestedSet field #{inspect(field)} for #{inspect(__MODULE__)}"
end

def __as_nested_set_get_field__(model, _), do: nil
def __as_nested_set_set_field__(model, _, _), do: model
def __as_nested_set_fields__(), do: unquote(fields |> Macro.escape())
end
def __as_nested_set_get_field__(model, _), do: nil
def __as_nested_set_set_field__(model, _, _), do: model
def __as_nested_set_fields__(), do: unquote(fields |> Macro.escape)
end
]
]
end

defp define_queriers(_env) do
Expand Down
Loading