View Source EctoGraf (EctoGraf v0.2.0)
Documentation for EctoGraf
.
Summary
Functions
Deep clones a target record.
Types
@type target() :: struct()
Functions
clone(target, repo, new_attrs, related_schemas_with_options, global_opts \\ [])
View SourceDeep clones a target record.
target
is the record to be cloned.
repo
is the ecto repo to operate on, the current implementation supports only a single repo.
new_attrs
is a map that will override values on the clone of the target.
related_schemas_with_options
is the list of schemas to clone in relation to the target.
The order of schemas does not matter, EctoGraf determines clone order by associations.
See examples for setting options per schema. Suppored options include:
map
- a function that takes the source row fields as a map and returns the cloned row fields
where
- a function that appends a where clause to the sql query and thus filters which rows should be cloned
Examples
To clone a post with it's comments:
post = Repo.insert!(%Post{title: "hello"})
Repo.insert!(%Comment{body: "p", post_id: post.id})
{:ok, clone_id} = EctoGraf.clone(post, Repo, %{title: "New Title"}, [Comment])
To clone a post with it's tags, comments, comment edits:
EctoGraf.clone(post, Repo, %{}, [
PostTag,
Comment,
CommentEdit
])
Options on schemas allow chaning values of cloned records:
EctoGraf.clone(post, Repo, %{}, [
PostTag,
[Comment, map: fn comment -> Map.put(comment, :likes, 0) end],
CommentEdit
])
The where option allows selective cloning at the schema level:
EctoGraf.clone(post, Repo, %{}, [
PostTag,
[Comment, where: fn query -> where(query, [comment], comment.likes >= 0) end],
CommentEdit
])
If you use the ecto timestamps and want to set inserted_at on all of your cloned records, you can use the map option globally:
now = your_get_timestamp_function()
EctoGraf.clone(
post,
Repo,
%{},
[PostTag, Comment, CommentEdit],
map: fn r -> Map.put(r, :inserted_at, now) end
)
Association Requirements
Each related schema being cloned must have either a belongs_to or has_one through association to the target.
A Comment can be cloned along with a Post because it has belongs_to :post, Post
A CommentEdit can be cloned with a Post because it has belongs_to :comment, Comment
and has_one :post, through: [:comment, :post]
It is possible to have multiple possible relations to the target. A schema can have multiple has_one through:
schema "comment_pair" do
belongs_to :comment_a, Comment
belongs_to :comment_b, Comment
has_one :post_a, through: [:comment_a, :post]
has_one :post_b, through: [:comment_b, :post]
end
Or a schema can have both a belongs_to AND has_one through:
schema "moderation_flag" do
belongs_to :post, Post
belongs_to :comment, Comment
has_one :comment_post, through: [:comment, :post]
end
These examples, while contrived, show that a record can have multple potential paths to being related to the target. Every possible path is exhausted when determing if a record is related to the clone target.