phoenixframework/phoenix — pull request #414 by josevalim (2014-10-12 16:42:01+00:00)
Title: Introducing format_encoders

I have started to refactor and clean up the views when I stumbled upon the HTML Safe code. The issue is that we are hard coding the html format when it comes to html safety. I have described this issue on #413 which was then classed because the proposed solutions were not good.

However, after a bit more of tinkering, I think I have come up with a pretty neat abstraction to solve this problem. The idea is: format encoders.

When we render an html template, it may return two values: `{:safe, string}` or `string`. During rendering, those values should be kept intact. However, once we need to return the value to the controller, we must convert it to a string. The idea is that the format_encoder will from now on have this responsibility: it will receive the content once rendering is done and encode it into a string. Of course, the format encoder for HTML will simply guarantee it is safe.

The exciting thing about this generalisation is that I realized we can also use it for other things like JSON! For example, if we specify Poison to be the format encoder for JSON, it means our no views no longer need to return JSON. All they need to do is to return something that can be encoded to JSON at the end of the day.

This means we get better composition because now `render("user.json", assigns)` can return data types instead of being forced to return strings!

## Configuration

I was originally thinking about configuring format encoders as we configure template engines:

config :phoenix, :format_engines, html: Phoenix.HTML.Safe, json: Poison

The issue with this though is that this configuration is global. For example, if two projects use Phoenix and they want to use different encoders, one of them will win only. I would like to propose to configure views as we configure routers:

config :phoenix, MyApp.View, format_engines: [html: Phoenix.HTML.Safe, json: Poison]

We don't need to have this on the config though. We could also do this configuration directly on the view when we call Phoenix.View. Thoughts?