I was wondering how others are decorating their models when rendering in templates.
I found this article:
…which is surprisingly useless.
To give an example: I currently have a model with a boolean field. I don’t want to display a boolean value in my view, but want to display a particular graphic specific to this model (generic helpers won’t do), and this model will be rendered in more than one view so I can’t just duplicate a helper function across my views.
Note: this is a contrived example. I likely wouldn’t use a decorator at this point, but definitely would as the views and decoration become more complicated.
What would be a good way of achieving this? I was thinking of creating a decorator module and then including that in the model, but I’d prefer to attach this at a certain point (within the view, for example), and not just attach it to the module by default.
New to Elixir so a little lost on how to go about it cleanly.
I would probably define a helper function in the view module to do the conversion, for example for a is_foo field you could have a foo_img function used as foo_img(@schema) in the template.
Schemas are just data - they don’t have any behaviour attached to them. Because of this I would argue that to a large extent talking about “decorators” as you see them in Rails doesn’t make much sense. For me decorators imply attaching behaviour to data - this is in general not possible in Elixir. That’s a big advantage and disadvantage of the functional approach.
True, when I say decorators I am referring to conversion. Still shaking bad habits.
This is the path I have gone down, but these models might be rendered in different views and so require that same helper. To avoid duplication, I need it in a central place i can include in different views, and I’d rather not create a massive view helpers file and pollute that with random helpers covering responsibilities for different models.
This is the current structure I have:
# web.ex
def view do
# etc
use MyApp.Helpers
end
defmodule MyApp.Helpers do
defmacro __using__(_) do
quote do
import MyApp.LayoutHelpers
import MyApp.SpecificModelHelpers
end
end
defmodule MyApp.SpecificModelHelpers do
def foo_img(schema) do
end
end
Does that look sensible? The only issue I see is I’m including all of my helpers in all views – though I suppose Phoenix does this, anyway (form helpers, etc).
Imo just define a view called MyModelView put the method in there and from wherever you want to display this just call out to this module directly. It’s very explicit and easy to follow and related methods stick together.
There is this library called decoratex which provides an easy way of decorating structs by adding calculated fields. I think that it may suit your requirements.
The readme contains more information and some examples.
defmodule Post do
use Ecto.Schema
use Decoratex
decorations do
decorate_field :happy_comments_count, :integer, &PostHelper.count_happy_comments/1
decorate_field :troll_comments_count, :integer, &PostHelper.count_troll_comments/1
decorate_field :mention_comments_count, :integer, &PostHelper.count_mention_comments/2, ""
...
end
schema "posts" do
has_many :comments, Comment, on_delete: :delete_all
field :title, :string
field :body, :string
add_decorations
end
end
I don’t think it would be well-suited to view logic.
As this is a pet peeve of mine - this is not a decorator as defined in the original patterns. There a decorator sort of wraps one method and adds functionality to it retaining the name. It doesn’t define new methods for view or other purposes.
These things should be called presenters imo but that battle was lost for the majority quite long ago.
(it’s a different discussion I know but seeing that even elixir libraries carry this over is quite sad to me)
Edit:apart from nitpicking I don’t think it’s a good idea to use such a thing. Applying OO patterns to FP especially while simpler solutions exist is often not the best way to go imo. The above adds view related stuff to the core model definition.
I think michalmuskala mentioned that above. You’re right, it’s not a traditional decorator; it’s a presenter. I agree there.
What I can’t yet fully embrace is the part about not applying OO patterns to functional programming. I’ve only been FPing for 6 weeks. It’s natural to bring over patterns I’ve used in OO, and I’m sure it will take a lot longer than 6 weeks to uproot and relearn.
A “central place” for helpers that you can “include in different views” into different modules sounds like a Module to me. The module could be imported or “use”d into different view modules.