Learning Phoenix / Elixir

Lessons learned

Elixir's API compatibility between eager and lazy is quite nice. For example:

|> Enum.each(fn id ->
  IO.puts "Getting #{id}"
|> Enum.take(5)

If item_ids has a lot of elements, then Enum.each would run for all of them (similar to JavaScript's, and Enum.take would grab the first 5 of those results.

If you want to do this lazily instead, you can just replace Enum.each with Stream.each, which creates a lazy list. Then, when Enum.take does its job, the lazy list only runs for the first 5 items.

Pretty cool.


I learned this one a few years ago, but in case it helps anyone else:

Strings in elixir always use double quotes ". Using single quotes will give you a charlist and throw strange errors!

More info here.


Ecto has a nice upsert feature. For example, assuming you have a unique index on a github_id id column in your users table:

  %User{github_id: github_id, name: name},
  on_conflict: :nothing

You can do more interesting things on conflict, such as update a few specific columns. More info here.


Figuring out ecto's upsert behavior was a major pain. Take the following example:

|> User.changeset(attrs)
|> Repo.insert!(
  on_conflict: :replace_all,
  conflict_target: :github_id

Here, :replace_all tells ecto to update the existing record in the db with your data from the changeset (when a conflict occurs).

This is great. However, the unexpected part is that :replace_all also includes the id column in its updates. In other words, when a conflict occurs, ecto updates the existing record with a new incremental id. What??

I don't know when you'd want this. But to stop it, you need to explicitly tell ecto to exclude id:

  on_conflict: {:replace_all_except, [:id]},
  conflict_target: :steam_appid


Welcome to DevLog

The open thought platform for developers.

Share your work as you work on it. Easier than a blog, handier than Twitter.

Sign in and write down valuable thoughts that would otherwise be forgotten.