Skip to content
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

Refactor Ecto.Strategy to use Ecto.Schema reflections #248

Merged
merged 2 commits into from
Oct 13, 2017

Conversation

germsvel
Copy link
Collaborator

@germsvel germsvel commented Sep 8, 2017

Summary

Ecto.Schema has a __schema__/1 function that allows us to
instrospect which fields are non-virtual, which are embeds, and which
are associations.

We already make use of this __schema__ function for determining
associations and embeds, but the code seemed somewhat complicated in
that when we cast fields, we had to know to skip associations and
embeds by checking each fields type. By using __schema__(:fields) and __schema__(:embeds) we are able to cast fields first, then embeds, and finally associations.

This has the advantage of allowing us to untangle associations and
embeds code which, although is somewhat similar, does not overlap
completely (there are more conditions to check when casting
associations).

`Ecto.Schema` has a `__schema__/1` function that allows us to
instrospect which fields are non-virtual, which are embeds, and which
are associations.

We already make use of this `__schema__` function for determining
associations and embeds, but the code seemed somewhat complicated in
that when we cast fields, we had to know to skip associations and
embeds. By using `__schema__(:fields)` and `__schema__(:embeds)` we are
able to cast fields first, then embeds, and finally associations.

This has the advantage of allowing us to untangle associations and
embeds code which, although is somewhat similar, does not overlap
completely (there are more conditions to check when casting
associations).
@germsvel germsvel force-pushed the gv-use-ecto-schema-reflection branch from 2457adc to 40cbaf8 Compare September 8, 2017 18:43
Copy link
Contributor

@paulcsmith paulcsmith left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it! Just one small suggestion.

end
defp cast_embed(embed, embed_key, %{__struct__: schema}) do
case embed do
%{} ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this could be simplified using an if

if embed
  # do stuff
end

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 updated in b2ec4ca

defp get_schema_assocs(schema) do
schema.__schema__(:associations) ++ schema.__schema__(:embeds)
defp schema_fields(schema) do
schema_non_virtual_fields(schema) -- schema_embeds(schema)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra space here before --

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch. Updated in b2ec4ca

assoc_keys = schema_associations(schema)

Enum.reduce(assoc_keys, struct, fn(assoc_key, struct) ->
casted_value = Map.get(struct, assoc_key) |> cast_assoc(assoc_key, struct)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this in a couple places, but generally I think the accepted style is to use pipe chains when you can,
like struct |> Map.get(assoc_key) |> cast_assoc(assoc_key, struct)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good catch. I tend to do this, but I think the preferred style is to have the first argument and then |>. I've updated this and the other mentions in b2ec4ca

defp cast_all_fields(%{__struct__: schema} = struct) do
field_keys = schema_fields(schema)

Enum.reduce(field_keys, struct, fn(field_key, struct) ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it doesn't look like you use field_keys anywhere else, so stylistically you could do this:

schema
|> schema_fields
|> Enum.reduce(struct, fn(field_key, struct) -> 
...


Enum.reduce(assocs, struct, fn(assoc, struct) ->
casted_value = struct |> Map.get(assoc) |> cast_assoc(assoc, struct)
Enum.reduce(embed_keys, struct, fn(embed_key, struct) ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here about using a pipe chain

schema
|> schema_embeds
|> Enum.reduce(struct, fn(embed_key, struct) ->
...

@dbernheisel
Copy link
Contributor

LGTM 👍 just have style comments.

@germsvel germsvel merged commit 3776a51 into master Oct 13, 2017
@germsvel
Copy link
Collaborator Author

Thanks @paulcsmith and @dbernheisel!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants