Share
Embed
Transcript
47 slides
-
@michalmuskala Warszawa, 9.06.2016 Abandoning models - embracing data A gentle
introduction to Ecto -
@michalmuskala Warszawa, 9.06.2016 Abandoning models - embracing data A gentle
introduction to Ecto -
@michalmuskala Warszawa, 9.06.2016 Inspirations LINQ - queries ActiveRecord - associations
Other Elixir projects - Moebius - pipeline queries Other Ruby projects - rom, trailblazer - focus on data -
@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 -
@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 -
@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) -
@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 -
@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 -
@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 -
@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)) -
@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 -
@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 -
@michalmuskala Warszawa, 9.06.2016 Ecto.Repo Isolates all impure operations Is an
interface to the database Outside of repo - everything is plain data -
@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 -
@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 -
@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 -
@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 -
@michalmuskala Warszawa, 9.06.2016 Ecto.Multi Encodes transaction as a data structure
Changesets for transactions You can test your transaction without database! -
@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)} -
@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 -
@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 -
@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(...) -
@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 -
@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 -
@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 -
@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[…].” -
@michalmuskala Warszawa, 9.06.2016 Abandoning models - embracing data A gentle
introduction to Ecto