Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Abandoning models - embracing data

Abandoning models - embracing data

A gentle introduction to Ecto

Michał Muskała

June 09, 2016
Tweet

More Decks by Michał Muskała

Other Decks in Programming

Transcript

  1. @michalmuskala Warszawa, 9.06.2016 Abandoning models - embracing data A gentle

    introduction to Ecto
  2. None
  3. None
  4. @michalmuskala Warszawa, 9.06.2016 Abandoning models - embracing data A gentle

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

  6. None
  7. None
  8. None
  9. None
  10. None
  11. @michalmuskala Warszawa, 9.06.2016 The Experiment

  12. @michalmuskala Warszawa, 9.06.2016 Who used Elixir?

  13. @michalmuskala Warszawa, 9.06.2016 Who used Ecto?

  14. @michalmuskala Warszawa, 9.06.2016 Who has Ruby background?

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

  16. @michalmuskala Warszawa, 9.06.2016 Inspirations LINQ - queries ActiveRecord - associations

    Other Elixir projects - Moebius - pipeline queries Other Ruby projects - rom, trailblazer - focus on data
  17. @michalmuskala Warszawa, 9.06.2016 Adapter-based architecture Database adapters Connection pool adapters

    db_connection library
  18. @michalmuskala Warszawa, 9.06.2016 Ecto Ecto.Schema Ecto.Query Ecto.Repo Ecto.Changeset

  19. @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
  20. @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
  21. @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)
  22. @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
  23. @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
  24. @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
  25. @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))
  26. @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
  27. @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
  28. @michalmuskala Warszawa, 9.06.2016 Ecto.Repo Isolates all impure operations Is an

    interface to the database Outside of repo - everything is plain data
  29. @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
  30. @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
  31. @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
  32. @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
  33. @michalmuskala Warszawa, 9.06.2016 Ecto.Multi Encodes transaction as a data structure

    Changesets for transactions You can test your transaction without database!
  34. @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)}
  35. @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
  36. @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
  37. @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(...)
  38. @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
  39. @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
  40. @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
  41. @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[…].”
  42. @michalmuskala Warszawa, 9.06.2016 Ecto is less opinionated

  43. @michalmuskala Warszawa, 9.06.2016 Ecto is more extensible

  44. @michalmuskala Warszawa, 9.06.2016 Ecto is data centric

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

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

  47. @michalmuskala Warszawa, 9.06.2016 Abandoning models - embracing data A gentle

    introduction to Ecto