{"id":4944,"date":"2015-12-15T11:20:48","date_gmt":"2015-12-15T13:20:48","guid":{"rendered":"http:\/\/blog.plataformatec.com.br\/?p=4944"},"modified":"2015-12-15T11:18:19","modified_gmt":"2015-12-15T13:18:19","slug":"ecto-v1-1-released-and-ecto-v2-0-plans","status":"publish","type":"post","link":"https:\/\/blog.plataformatec.com.br\/2015\/12\/ecto-v1-1-released-and-ecto-v2-0-plans\/","title":{"rendered":"Ecto v1.1 released and Ecto v2.0 plans"},"content":{"rendered":"

Ecto v1.1 has been released. Ecto v1.1 brings improvements and bug fixes to Ecto but, more importantly, paves the way for the upcoming Ecto v2.0 release by deprecating functionality that has been said to be error prone or confusing by developers. This article will highlight both improvements and what to expect by Ecto 2.0.<\/p>\n

For detailed information on the release, please check the CHANGELOG<\/a>.<\/p>\n

Let’s get started!<\/p>\n

Better pipelines<\/h2>\n

The pipeline operator in Elixir is a great way to express a series of computations on top of a data structure. Given Ecto queries are just data structures, they are a perfect fit to be modified as part of pipelines.<\/p>\n

Ecto has always supported both keyword and function queries<\/a>. Let’s start with a keyword query:<\/p>\n

from p in Post,\n  where: p.author == \"Jos\u00e9\",\n  order_by: [desc: p.published_at],\n  limit: 5\n<\/code><\/pre>\n

In Ecto v1.0, it could have be written using pipelines as follows:<\/p>\n

Post\n|> where([p], p.author == \"Jos\u00e9\")\n|> order_by([p], desc: p.published_at)\n|> limit(5)\n<\/code><\/pre>\n

Ecto v1.1 improve pipelines by making the binding argument required only when working with associations and by allowing dynamic data to be given on more places. In v1.1, we can rewrite the example above as:<\/p>\n

Post\n|> where(author: \"Jos\u00e9\")\n|> order_by(desc: :published_at)\n|> limit(5)\n<\/code><\/pre>\n

Most query operations like where<\/code>, distinct<\/code>, having<\/code> support the syntax above. The only exceptions are select<\/code> and group_by<\/code> which will be tackled on Ecto v2.0.<\/p>\n

No more models<\/h2>\n

Ecto.Model<\/code> is being deprecated on Ecto v1.1. This aims to solve both conceptual and practical issues. Let’s discuss them.<\/p>\n

What are models?<\/h3>\n

The big question imposed by Ecto.Model<\/code> is: what is a model?<\/p>\n

One thing is clear, Ecto did not provide models in the “traditional” sense. In OO languages, you would say a model can be instantiated and it would have methods that contain business logic. However, the data that comes from the database in Ecto is just data. It is an Elixir struct. It is not an Ecto model.<\/p>\n

Working closely on Phoenix applications and on the Programming Phoenix<\/a> book made it clear that, similar to controllers\u00a0and views, models are not an entity. A model, a controller or a view (from the MVC pattern) are just group of functions that share similar responsibilities. They are just guidelines on how to group code towards a common purpose.<\/p>\n

For those reasons, Ecto.Model<\/code> is being deprecated in Ecto. At first, this implies Ecto data structures are now defined directly with Ecto.Schema<\/code>. In Ecto v1.0:<\/p>\n

defmodule MyApp.Post do\n  use Ecto.Model\n\n  schema \"posts\" do\n    # ...\n  end\nend\n<\/code><\/pre>\n

From Ecto v1.1:<\/p>\n

defmodule MyApp.Post do\n  use Ecto.Schema\n\n  schema \"posts\" do\n    # ...\n  end\nend\n<\/code><\/pre>\n

Not only that, many of the functions in the Ecto.Model<\/code><\/a> module have been moved to Ecto<\/code><\/a>.<\/p>\n

However, the biggest change with the deprecation of models is that model callbacks are being removed<\/strong>. To understand why this matters, let’s look at one Ecto feature that relied on callbacks and was rewritten to be a simple function.<\/p>\n

Optimistic lock<\/h3>\n

Ecto provides optimistic locks on top of your schema. A simple implementation of optimistic lock uses an integer column, usually named lock_version<\/code>, to store the current version of a given row. On update, Ecto would do a “compare and increase” operation. If the entry being updated had the same lock_version<\/code> as in the database, the update operation succeeds and the lock_version<\/code> is incremented. Otherwise, the update fails because the entry is stale.<\/p>\n

On Ecto v1.0, optimistic_lock<\/code> was enabled for the whole model:<\/p>\n

defmodule MyApp.Post do\n  use Ecto.Model\n\n  schema \"posts\" do\n    # ...\n  end\n\n  optimistic_lock :lock_version\nend\n<\/code><\/pre>\n

This reveals the awkwardness behind callbacks. We are suddenly adding “behaviour” to our data structures. Not only that, because callbacks are enabled on all operations, we have no control over its use.<\/p>\n

For example, what if you also provide an admin interface. Do you want the admin to be under the same lock constraints as regular users? More importantly, what if you want to trigger the lock only if some fields are changing? The only way to add this functionality is by growing the complexity of the optimistic_lock<\/code> implementation by providing an ever growing set of complex options.<\/p>\n

It happens Ecto has the perfect solution to this problem: changesets<\/a>. For example, instead of defining validations in the model, you define per changeset:<\/p>\n

@required_params [:title, :body]\n@optional_params [:metadata]\n\ndef changeset(post, params \\\\ :empty) do\n  post\n  |> cast(params, @required_params, @optional_params)\n  |> validate_length(:title, min: 3)\n  |> validate_length(:metadata, min: 3)\nend\n<\/code><\/pre>\n

In other words, a changeset is a data structure that controls the changes being sent to the database. This means that, if you have different roles in your application that work on different facets of the same data, you define different changesets for every operation.<\/p>\n

Ecto v1.1 has replaced the optimistic_lock\/1<\/code> macro implementation by a simple function that works on the changeset. If you want to add optimistic locking, just pipe your changeset in the optimistic_lock\/2<\/code> with the lock column name:<\/p>\n

def changeset(post, params \\\\ :empty) do\n  post\n  |> cast(params, @required_params, @optional_params)\n  |> validate_length(:title, min: 3)\n  |> validate_length(:metadata, min: 3)\n  |> optimistic_lock(:lock_version)\nend\n<\/code><\/pre>\n

Because it is only a function call, you have control of exactly when and where you can apply the lock. And ultimately that’s the fundamental problem with callbacks: it makes developers write functionality that is hard to compose.<\/p>\n

Goodbye callbacks<\/h3>\n

After a quick search on GitHub, we quickly noticed that many developers relied on callbacks in many cases where changesets would suffice, introducing exactly the same problems we saw with optimistic_lock<\/code>.<\/p>\n

Furthermore, after_*<\/code> callbacks provide their own set of issues. Because after_insert<\/code> and after_update<\/code> callbacks would still run inside a transaction, there is no guarantee the transaction that wraps both insert<\/code> and update<\/code> would actually commit. So someone would rely on such callbacks to index data or write to the filesystem while the transaction could rollback afterwards. Those mistakes are always bound to happen with callbacks because the execution flow is hidden from developers.<\/p>\n

For all the reasons mentioned above, callbacks are deprecated in Ecto and will be removed by Ecto v2.0. Meanwhile we are working on solutions like Ecto.Multi<\/code><\/a> that will give developers a data-driven approach to work with transactions.<\/p>\n

Looking forward to 2.0<\/h2>\n

Besides the improvements already listed above, we are looking forward to many exciting new features on Ecto v2.0:<\/p>\n