Explicitness(Ecto) vs Implicitness(Active Record)

Recently I have started exploring Phoenix as part of my learning. Phoenix is a web framework just like Rails but it built on top of Elixir and Ecto is an ORM just like Active Record use by Phoenix to talk to DB.

Since the semantics of Phoenix is similar to rails makes me very comfortable with Phoenix. Let see some similarities between Ecto and Active Record. Prior to that, the important aspect about Ecto is it follows Explicitness whereas Active Record follows implicitness. Let check out these characteristics with examples:

  1. Model Generation

In Active Record

rails g model User first_name:string last_name:string 
# app/models/user.rb
class User < ApplicationRecord
end
# db/migrate/20170925112416_create_users.rb
class CreateUsers < ActiveRecord::Migration[5.1]
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
      t.timestamps
end
end
end

In Ecto

mix phx.gen.scheme User users first_name:string last_name:string
# lib/stc/user.ex
defmodule Stc.User do
use Ecto.Schema
import Ecto.Changeset
alias Stc.User
 schema “users” do
field :first_name, :string
field :last_name, :string
   timestamps()
end
end
# priv/repo/migrations/20170925112137_create_user.exs
defmodule Stc.Repo.Migrations.CreateUser do
use Ecto.Migration
 def change do
create table(:user) do
add :first_name, :string
add :last_name, :string
     timestamps()
end
end

Unlike Active Record, in Ecto, we have to mention schema to access necessary attributes from a table. Otherwise, Ecto returns only those fields that are defined in the schema. 
In Active Record when you mention User, implicitly it understands that it should talk with users table. Whereas in Ecto if you mention schema as the userfor User model then it talks to user table instead of users.

2. Querying:

In Active Record

# To insert single record
> User.create(first_name: ‘siva’, last_name: ‘Gollapalli’)
# To Fetch all records
> User.all

In Ecto

# To insert single record
> %Stc.User{first_name: “Siva”, last_name: “Gollapalli”} |> Stc.Repo.insert
# To Fetch all records
> Stc.User |> Stc.Repo.all

In Ecto, querying will be handled by a module called Repo which is an OTP app. By DB config, it knows which DB it should talk but to query a table we should explicitly pass the table name to Repo to get query result as above.

3. Associations:

In Active Record

class User < ApplicationRecord
has_many :posts
end
class Post < ApplicationRecord
belongs_to :user
end
> user = User.first
> post = Post.create(description: "associations", user: user)
# To Fetch all posts of a user
> user.posts

In Ecto

# lib/stc/user.ex
defmodule Stc.User do
use Ecto.Schema
import Ecto.Changeset
alias Stc.User
 schema "users" do
has_many :posts, Stc.Post
end
end
# lib/stc/post.ex
defmodule Stc.Post do
use Ecto.Schema
import Ecto.Changeset
alias Stc.Post
schema "posts" do
field :description, :string
   belongs_to :user, Stc.user
end
end
> user = Stc.Repo.get(Stc.User, 1)
> Ecto.build_assoc(user,
:posts,
%{description: "First post"}) |> Stc.Repo.insert
# To Fetch all posts of a user
> Stc.Repo.all assoc(user, :posts)

As we know that in Rails, depends upon the relationship, AR would find respective model unless and until if relationship defers from the model name. If it differs then we explicitly pass an option called class_name in relation.

In Ecto, it follows the same pattern but by default. As you see we are explicitly mentioning module name while defining a relation.

Though for the users coming from Rails background might face hiccups in their baby steps but as you progress it gives a better understanding.

Thanks for reading. Any suggestions would be welcome!!!.