Slide 1

Slide 1 text

@michalmuskala Warszawa, 9.06.2016 Abandoning models - embracing data A gentle introduction to Ecto

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

@michalmuskala Warszawa, 9.06.2016 Abandoning models - embracing data A gentle introduction to Ecto

Slide 5

Slide 5 text

@michalmuskala Warszawa, 9.06.2016 Michał Muskała http://michal.muskala.eu/ https://github.com/michalmuskala/ @michalmuskala

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

@michalmuskala Warszawa, 9.06.2016 The Experiment

Slide 12

Slide 12 text

@michalmuskala Warszawa, 9.06.2016 Who used Elixir?

Slide 13

Slide 13 text

@michalmuskala Warszawa, 9.06.2016 Who used Ecto?

Slide 14

Slide 14 text

@michalmuskala Warszawa, 9.06.2016 Who has Ruby background?

Slide 15

Slide 15 text

@michalmuskala Warszawa, 9.06.2016 Who has C# or .NET background?

Slide 16

Slide 16 text

@michalmuskala Warszawa, 9.06.2016 Inspirations LINQ - queries ActiveRecord - associations Other Elixir projects - Moebius - pipeline queries Other Ruby projects - rom, trailblazer - focus on data

Slide 17

Slide 17 text

@michalmuskala Warszawa, 9.06.2016 Adapter-based architecture Database adapters Connection pool adapters db_connection library

Slide 18

Slide 18 text

@michalmuskala Warszawa, 9.06.2016 Ecto Ecto.Schema Ecto.Query Ecto.Repo Ecto.Changeset

Slide 19

Slide 19 text

@michalmuskala Warszawa, 9.06.2016 Ecto.Schema defmodule Weather do use Ecto.Schema schema "weather" do belongs_to :city, City field :wdate, Ecto.Date field :temp_lo, :integer field :temp_hi, :integer field :prcp, :float, default: 0.0 timestamps end end

Slide 20

Slide 20 text

@michalmuskala Warszawa, 9.06.2016 Ecto.Schema declares fields - no SELECT * FROM foos allows for custom types thin wrapper around Elixir’s structs defines data - not behaviour

Slide 21

Slide 21 text

@michalmuskala Warszawa, 9.06.2016 Ecto.Query from w in Weather, where: w.prcp <= 0.0 or is_nil(w.prcp), select: w from s in Search, where: ilike(fragment("?::text", s.name), ^name_search_term) or fragment("to_tsvector(?) @@ to_tsquery(?)", s.description, ^term)

Slide 22

Slide 22 text

@michalmuskala Warszawa, 9.06.2016 Ecto.Query from p in Post, where: fragment(position: ["$geoWithin": [...]]) def all(filter) do filter.q |> sanitize |> search |> preload(:location) |> order_by([s] asc: s.date, desc: s.type) end

Slide 23

Slide 23 text

@michalmuskala Warszawa, 9.06.2016 Ecto.Query defmacrop geo_search(coordinates, geo_point, radius) do quote do fragment("ST_DWithin(?, ?, ?)", unquote(coordinates), unquote(geo_point), unquote(radius) ) end end

Slide 24

Slide 24 text

@michalmuskala Warszawa, 9.06.2016 Ecto.Query def by_location(query, %{lat: lat, lng: lng, radius: radius}) do geo_point = %Geo.Point{coordinates: {lat, lng}} from provider in query, left_join: location_provider in assoc(provider, :location), left_join: events in assoc(provider, :events), left_join: location_event in assoc(events, :location), where: geo_search(location_provider.coordinates, ^geo_point, ^radius) or geo_search(location_event.coordinates, ^geo_point, ^radius), distinct: true, select: provider end

Slide 25

Slide 25 text

@michalmuskala Warszawa, 9.06.2016 Ecto.Query query = from p in Post, select: [:visits], order_by: [desc: :visits], limit: 10 TestRepo.all(from p in subquery(query), select: avg(p.visits))

Slide 26

Slide 26 text

@michalmuskala Warszawa, 9.06.2016 Ecto.Query Two syntaxes - keyword and pipeline No lazy-loading, explicit preloads Easily extensible via fragments and macros Translated by adapter Composable

Slide 27

Slide 27 text

@michalmuskala Warszawa, 9.06.2016 Ecto.Repo defmodule Simple.Repo do use Ecto.Repo, otp_app: :simple end def start(_type, _args) do import Supervisor.Spec tree = [supervisor(Simple.Repo, [])] opts = [name: Simple.Sup, strategy: :one_for_one] Supervisor.start_link(tree, opts) end

Slide 28

Slide 28 text

@michalmuskala Warszawa, 9.06.2016 Ecto.Repo Isolates all impure operations Is an interface to the database Outside of repo - everything is plain data

Slide 29

Slide 29 text

@michalmuskala Warszawa, 9.06.2016 Ecto.Changeset def changeset(schema, params) do schema |> cast(params, @params_required ++ @params_optional) |> validate_required(@params_required) |> validate_params |> cast_assoc(:location, required: true) |> cast_assoc(:age_group, required: true) |> cast_embed(:links) |> validate_length(:name, min: @min_length_name) |> validate_format(:image_url, @regexp_image) |> validate_inclusion(:category, Category.values) |> validate_email |> unique_constraint(:email) end

Slide 30

Slide 30 text

@michalmuskala Warszawa, 9.06.2016 Ecto.Changeset Encodes data changes as a data structure No validations are global Multiple changieret functions for different circumstances Easily extensible with functions

Slide 31

Slide 31 text

@michalmuskala Warszawa, 9.06.2016 Ecto.Multi def password_reset_multi(account, params) do account = account_password_reset(account, params) access_log = log_password_reset(account, params) Multi.new |> Multi.update(:account, account) |> Multi.insert(:log, access_log) |> Multi.delete_all(:sessions, assoc(account, :sessions)) end

Slide 32

Slide 32 text

@michalmuskala Warszawa, 9.06.2016 Ecto.Multi defmodule UserManager do def password_reset(params) do with {:ok, account} <- load_account(params), multi = password_reset_multi(account, params), {:ok, results} <- Repo.transaction(multi), :ok <- send_notification(results.account), do: {:ok, results.account} end # ... end

Slide 33

Slide 33 text

@michalmuskala Warszawa, 9.06.2016 Ecto.Multi Encodes transaction as a data structure Changesets for transactions You can test your transaction without database!

Slide 34

Slide 34 text

@michalmuskala Warszawa, 9.06.2016 Schema less queries from u in "users", join: a in "activities", on: a.user_id == u.id, where: a.start_at > type(^start_at, Ecto.DateTime) and a.end_at < type(^end_at, Ecto.DateTime), group_by: a.user_id, select: %{user_id: a.user_id, interval: a.start_at - a.end_at, count: count(u.id)}

Slide 35

Slide 35 text

@michalmuskala Warszawa, 9.06.2016 Schema less queries # insert data [%{id: id}] = MyApp.Repo.insert_all("posts", [[title: "hello"]], returning: [:id]) # use query for updates post = from p in "posts", where: p.id == ^id {1, _} = MyApp.Repo.update_all(post, set: [title: "new title"]) # and deletes {1, _} = MyApp.Repo.delete_all post

Slide 36

Slide 36 text

@michalmuskala Warszawa, 9.06.2016 Table less schemas defmoudule RegistrationSchema do use Ecto.Schema schema "" do field :foo, :string field :bar, :integer embeds_one :resource, Resource end # ... end

Slide 37

Slide 37 text

@michalmuskala Warszawa, 9.06.2016 Schema less changesets data = %{} types = %{first_name: :string, last_name: :string, email: :string} fields = Map.keys(types) changeset = {data, types} |> Ecto.Changeset.cast(params, fields) |> validate_required(...) |> validate_length(...)

Slide 38

Slide 38 text

@michalmuskala Warszawa, 9.06.2016 Concurrent, transactional tests In 1.1 - database tests sequential, 2.0 - concurrent Works with postgres, mysql has wired transaction semantics Works with Wallaby and Hound

Slide 39

Slide 39 text

@michalmuskala Warszawa, 9.06.2016 No models, no callbacks Ecto 1.1 deprecated models, embraced schemas, 2.0 removes models Ecto 1.1 deprecated callbacks, 2.0 removed them I’m forbidden by José to use the world “model” Phoenix will switch away from the model nomenclature

Slide 40

Slide 40 text

@michalmuskala Warszawa, 9.06.2016 KISS Plain CRUD interfaces are easy When you need more power - you have it We want not to force anything on the users, but keep the simplicity

Slide 41

Slide 41 text

@michalmuskala Warszawa, 9.06.2016 –José Valim “I am completely OK with taking the blame regarding Ecto 1.0. We did push folks to the wrong direction. But Ecto 2.0 no longer puts you in this place[…]. If you are using structs as your domain models, then it will be more on you than on me, since you have everything to stop doing that[…].”

Slide 42

Slide 42 text

@michalmuskala Warszawa, 9.06.2016 Ecto is less opinionated

Slide 43

Slide 43 text

@michalmuskala Warszawa, 9.06.2016 Ecto is more extensible

Slide 44

Slide 44 text

@michalmuskala Warszawa, 9.06.2016 Ecto is data centric

Slide 45

Slide 45 text

@michalmuskala Warszawa, 9.06.2016 Ecto is not a solution to the problem

Slide 46

Slide 46 text

@michalmuskala Warszawa, 9.06.2016 Ecto is a toolbox for building solutions

Slide 47

Slide 47 text

@michalmuskala Warszawa, 9.06.2016 Abandoning models - embracing data A gentle introduction to Ecto