├── README.md ├── elixir ├── distributed-erlang-sync-nodes.md ├── distributed-erlang.md ├── dynamic-supervisor-start-children.md ├── ecto-repo-connection-process.md ├── ecto-stream-to-list.md ├── ecto-virtual-field.md ├── erlang-observer-on-macos.md ├── error-handling-in-libraries.md ├── ex_unit │ ├── assert-match.md │ ├── capture-log-level.md │ └── ignore-tests.md ├── examples │ └── graceful_shutdown │ │ ├── .formatter.exs │ │ ├── .gitignore │ │ ├── README.md │ │ ├── lib │ │ ├── application.ex │ │ └── server.ex │ │ └── mix.exs ├── exception-message.md ├── external-resource-module-attribute.md ├── gen_server_hibernate_after_start_option.md ├── genserver-name-registration.md ├── graceful_shutdown.md ├── install-elixir-with-asdf.md ├── monotonic-time.md ├── path-from-app-dir.md ├── pattern-match-struct-name.md ├── postgrex-copy-from-stdin.md ├── process-links.md ├── remote-observer-distillery-release.md ├── remove-unused-dependencies-from-mix-lock.md ├── revert-hex-package.md ├── rollbar-exception-reporting-for-phoenix.md ├── running-ecto-migrations-during-deployment.md ├── start-application-dependencies.md ├── testing-plugs.md ├── travis-ci-ecto-migrations.md └── use-ecto-utc-datetime.md ├── erlang └── erlang_version.md ├── git ├── rebase-root-commit.md ├── set-origin-url.md └── syncing-a-github-fork.md ├── javascript └── capture-escape-keypress.md ├── julia └── getting-started.md ├── linux └── systemd-service-configuration.md ├── mac ├── increase-open-file-limit.md └── mac-sleep-wake.md ├── phoenix-framework └── page-specific-titles.md ├── postgres ├── formatting-timestamps.md ├── gapless-sequence.md ├── install-timescaledb.md ├── list-tables-in-schema.md └── recreate-postgres-schema.md └── shell-scripting └── exit-codes.md /README.md: -------------------------------------------------------------------------------- 1 | # TIL 2 | 3 | > Today I Learned 4 | 5 | A collection of concise write-ups on small things I learn day to day across a 6 | variety of languages and technologies. 7 | 8 | Inspired by [Josh Branchaud's](https://github.com/jbranchaud) [til](https://github.com/jbranchaud/til). 9 | 10 | ## Elixir 11 | 12 | - [Distributed Erlang](elixir/distributed-erlang.md) 13 | - [Distributed Erlang sync nodes](elixir/distributed-erlang-sync-nodes.md) 14 | - [Dynamic supervisor start children](elixir/dynamic-supervisor-start-children.md) 15 | - [Ecto stream to list](elixir/ecto-stream-to-list.md) 16 | - [Ecto Repo's connection process](elixir/ecto-repo-connection-process.md) 17 | - [Ecto virtual field](elixir/ecto-virtual-field.md) 18 | - [Error handling in Elixir libraries](elixir/error-handling-in-libraries.md) 19 | - [Exception message](elixir/exception-message.md) 20 | - [External resource module attribute](elixir/external-resource-module-attribute.md) 21 | - [Graceful shutdown](elixir/graceful_shutdown.md) 22 | - [`GenServer` hibernate after start option](elixir/gen_server_hibernate_after_start_option.md) 23 | - [`GenServer` name registration using `:via` option](elixir/genserver-name-registration.md) 24 | - [Install Elixir and Erlang with `asdf` version manager](elixir/install-elixir-with-asdf.md) 25 | - [Pattern match struct name](elixir/pattern-match-struct-name.md) 26 | - [Postgrex copy from STDIN](elixir/postgrex-copy-from-stdin.md) 27 | - [Process links](elixir/process-links.md) 28 | - [Running Erlang's observer on macOS](elixir/erlang-observer-on-macos.md) 29 | - [Running Observer on a remote node deployed with Distillery](elixir/remote-observer-distillery-release.md) 30 | - [Read from the application's directory](elixir/path-from-app-dir.md) 31 | - [Remove unused dependencies from `mix.lock`](elixir/remove-unused-dependencies-from-mix-lock.md) 32 | - [Revert Hex package](elixir/revert-hex-package.md) 33 | - [Rollbar exception reporting for Phoenix](elixir/rollbar-exception-reporting-for-phoenix.md) 34 | - [Running Ecto database migrations during deployment](elixir/running-ecto-migrations-during-deployment.md) 35 | - [Start an Elixir application's dependencies](elixir/start-application-dependencies.md) 36 | - [Testing plugs](elixir/testing-plugs.md) 37 | - [Travis CI Ecto migrations](elixir/travis-ci-ecto-migrations.md) 38 | - [Use Ecto's UTC datetime type](elixir/use-ecto-utc-datetime.md) 39 | - [Use monotonic time for timing](elixir/monotonic-time.md) 40 | 41 | ### ExUnit 42 | 43 | - [ExUnit assert match](elixir/ex_unit/assert-match.md) 44 | - [ExUnit capture log level](elixir/ex_unit/capture-log-level.md) 45 | - [ExUnit ignore tests](elixir/ex_unit/ignore-tests.md) 46 | 47 | ### Phoenix framework 48 | 49 | - [Page specific titles](phoenix-framework/page-specific-titles.md) 50 | 51 | ## Erlang 52 | 53 | - [Erlang version](erlang/erlang_version.md) 54 | 55 | ## Julia 56 | 57 | - [Getting started with Julia](julia/getting-started.md) 58 | 59 | ## Git 60 | 61 | - [Rebase root git commit](git/rebase-root-commit.md) 62 | - [Set origin URL](git/set-origin-url.md) 63 | - [Syncing a GitHub fork](git/syncing-a-github-fork.md) 64 | 65 | ## Linux 66 | 67 | - [systemd service configuration](linux/systemd-service-configuration.md) 68 | 69 | ## macOS 70 | 71 | - [Increase open file limit](mac/increase-open-file-limit.md) 72 | - [macOS sleep and wake](mac/mac-sleep-wake.md) 73 | 74 | ## Postgres 75 | 76 | - [Formatting timestamps](postgres/formatting-timestamps.md) 77 | - [Gapless sequence in Postgres](postgres/gapless-sequence.md) 78 | - [Install TimescaleDB with Postgres.app](postgres/install-timescaledb.md) 79 | - [List tables in a Postgres schema](postgres/list-tables-in-schema.md) 80 | - [Recreate Postgres schema](postgres/recreate-postgres-schema.md) 81 | 82 | ## Shell scripting 83 | 84 | - [Exit codes](shell-scripting/exit-codes.md) 85 | -------------------------------------------------------------------------------- /elixir/distributed-erlang-sync-nodes.md: -------------------------------------------------------------------------------- 1 | # Distributed Erlang sync nodes 2 | 3 | In a [distributed Erlang application](http://erlang.org/doc/design_principles/distributed_applications.html) you can configure nodes to wait for at start up, during cluster formation, before starting your own application. 4 | 5 | You configure the `:kernel` application: 6 | 7 | ```elixir 8 | config :kernel, 9 | sync_nodes_optional: [:"node1@192.168.1.1", :"node2@192.168.1.2"], 10 | sync_nodes_timeout: 60_000 11 | ``` 12 | 13 | The `sync_nodes_optional` configuration specifies which nodes to attempt to connect to within the `sync_nodes_timeout` window, defined in milliseconds, before continuing with startup. 14 | 15 | There is also a `sync_nodes_mandatory` setting which can be used to enforce all nodes are connected within the timeout window or else the node terminates. 16 | 17 | ## `iex` usage 18 | 19 | Create a `sys.config` file per node: 20 | 21 | - `node1.sys.config` 22 | 23 | ``` 24 | [{kernel, 25 | [ 26 | {sync_nodes_optional, ['node2@127.0.0.1', 'node3@127.0.0.1']}, 27 | {sync_nodes_timeout, 30000} 28 | ]} 29 | ]. 30 | ``` 31 | 32 | - `node2.sys.config` 33 | 34 | ``` 35 | [{kernel, 36 | [ 37 | {sync_nodes_optional, ['node1@127.0.0.1', 'node3@127.0.0.1']}, 38 | {sync_nodes_timeout, 30000} 39 | ]} 40 | ]. 41 | ``` 42 | 43 | - `node3.sys.config` 44 | 45 | ``` 46 | [{kernel, 47 | [ 48 | {sync_nodes_optional, ['node1@127.0.0.1', 'node2@127.0.0.1']}, 49 | {sync_nodes_timeout, 30000} 50 | ]} 51 | ]. 52 | ``` 53 | 54 | Run an `iex` console, one for each node: 55 | 56 | ```console 57 | $ MIX_ENV=test iex --name node1@127.0.0.1 --erl "-config node1.sys.config" -S mix 58 | ``` 59 | 60 | ```console 61 | $ MIX_ENV=test iex --name node2@127.0.0.1 --erl "-config node2.sys.config" -S mix 62 | ``` 63 | 64 | ```console 65 | $ MIX_ENV=test iex --name node3@127.0.0.1 --erl "-config node3.sys.config" -S mix 66 | ``` 67 | 68 | Once the third `iex` console has started, within the 30 second timeout, the three nodes will connect and then start your application. 69 | 70 | You can verify the nodes are connected to each other: 71 | 72 | ``` 73 | iex> Node.list() 74 | [:"node2@127.0.0.1", :"node3@127.0.0.1"] 75 | ``` 76 | -------------------------------------------------------------------------------- /elixir/distributed-erlang.md: -------------------------------------------------------------------------------- 1 | # Distributed Erlang 2 | 3 | > Good for control plane, not so good for data plane. 4 | 5 | - Sending large data can cause busy distribution ports and head-of-line blocking. 6 | - Use TCP, UDP, etc. directly for data plane traffic. 7 | - Don't mix control plane and data plane traffic. 8 | 9 | See: [Implementing Riak in Erlang: Benefits and Challenges](http://gotocon.com/dl/goto-chicago-2014/Web/vinoski-impl-riak-erlang.pdf) 10 | -------------------------------------------------------------------------------- /elixir/dynamic-supervisor-start-children.md: -------------------------------------------------------------------------------- 1 | # Dynamic supervisor start children 2 | 3 | How do I start a dynamic supervisor's initial children when it starts? 4 | 5 | José Valim [provides a solution](https://elixirforum.com/t/understanding-dynamicsupervisor-no-initial-children/14938/2?u=slashdotdash): 6 | 7 | > Start a `DynamicSupervisor` with a Task under a `rest_for_one` supervisor. This way you can start children immediately after the supervisor boots. 8 | 9 | ### Example 10 | 11 | The following module based `DynamicSupervisor` provides a `start_children/0` function which starts one or more existing child processes. It uses a static list, but this could be populated from anywhere. 12 | 13 | ```elixir 14 | defmodule ExampleDynamicSupervisor do 15 | use DynamicSupervisor 16 | 17 | def start_link(init_arg) do 18 | DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__) 19 | end 20 | 21 | @impl DynamicSupervisor 22 | def init(_init_arg) do 23 | DynamicSupervisor.init(strategy: :one_for_one) 24 | end 25 | 26 | def start_children do 27 | for child <- [:child1, :child2, :child3] do 28 | {:ok, _pid} = start_child(child) 29 | end 30 | 31 | :ok 32 | end 33 | 34 | def start_child(child) do 35 | spec = %{ 36 | id: {ChildSupervisor, child}, 37 | start: {ChildSupervisor, :start_link, [child]}, 38 | type: :supervisor 39 | } 40 | 41 | DynamicSupervisor.start_child(__MODULE__, spec) 42 | end 43 | end 44 | ``` 45 | 46 | The above `DynamicSupervisor` can be started by a supervisor in addition to a task which calls the `start_children/0` function. This will asynchronously start all the children _after_ the dynamic supervisor has started. 47 | 48 | ```elixir 49 | children = [ 50 | ExampleDynamicSupervisor, 51 | Supervisor.child_spec({Task, &ExampleDynamicSupervisor.start_children/0}, restart: :transient) 52 | ] 53 | 54 | opts = [strategy: :rest_for_one] 55 | 56 | Supervisor.start_link(children, opts) 57 | ``` 58 | 59 | The `rest_for_one` supervisor strategy ensures that if the dynamic supervisor process crashes it will also terminate the task. On restart, both the dynamic supervisor and the task to start all children will be started. 60 | -------------------------------------------------------------------------------- /elixir/ecto-repo-connection-process.md: -------------------------------------------------------------------------------- 1 | # Ecto Repo's connection process 2 | 3 | Ecto's Repo doesn't expose the database connection process which is used to access the database. It is hidden internally which connection from the connection pool is being used. However, it is possible to lookup the connection process by using the PID of the Ecto Repo and getting the connection from the process dictionary when running within a checkout or transaction. 4 | 5 | 1. Use `Repo.checkout/1`: 6 | 7 | ```elixir 8 | Repo.checkout(fn -> 9 | %{pid: pool} = Ecto.Adapter.lookup_meta(Repo) 10 | 11 | conn = Process.get({Ecto.Adapters.SQL, pool}) 12 | 13 | Postgrex.query(conn, "SELECT NOW();", [], []) 14 | end) 15 | ``` 16 | 17 | 2. Use `Repo.transaction/1`: 18 | 19 | ```elixir 20 | Repo.transaction(fn -> 21 | %{pid: pool} = Ecto.Adapter.lookup_meta(Repo) 22 | 23 | conn = Process.get({Ecto.Adapters.SQL, pool}) 24 | 25 | Postgrex.query(conn, "SELECT NOW();", [], []) 26 | end) 27 | ``` 28 | 29 | 3. Use `Ecto.Multi.run/3`: 30 | 31 | ```elixir 32 | Ecto.Multi.new() 33 | |> Ecto.Multi.run(:query, fn repo, _changes -> 34 | %{pid: pool} = Ecto.Adapter.lookup_meta(repo) 35 | 36 | conn = Process.get({Ecto.Adapters.SQL, pool}) 37 | 38 | Postgrex.query(conn, "SELECT NOW();", [], []) 39 | end) 40 | |> Repo.transaction() 41 | ``` 42 | -------------------------------------------------------------------------------- /elixir/ecto-stream-to-list.md: -------------------------------------------------------------------------------- 1 | # Ecto stream to list 2 | 3 | ```elixir 4 | @spec stream_to_list(Ecto.Queryable.t(), Keyword.t()) :: {:ok, Enum.t()} | {:error, any} 5 | defp stream_to_list(queryable, opts) do 6 | stream = Repo.stream(queryable, opts) 7 | 8 | Repo.transaction(fn -> Enum.to_list(stream) end, opts) 9 | end 10 | ``` 11 | -------------------------------------------------------------------------------- /elixir/ecto-virtual-field.md: -------------------------------------------------------------------------------- 1 | # Ecto virtual field 2 | 3 | Populate an Ecto virtual field using a left-join query. 4 | 5 | The `favorited` field in the article schema is a virtual boolean, defaulted to false: 6 | 7 | ```elixir 8 | defmodule Blog.Article do 9 | use Ecto.Schema 10 | 11 | @primary_key {:uuid, :binary_id, autogenerate: false} 12 | 13 | schema "blog_articles" do 14 | field :slug, :string 15 | field :title, :string 16 | field :body, :string 17 | field :tags, {:array, :string} 18 | field :favorite_count, :integer, default: 0 19 | field :favorited, :boolean, virtual: true, default: false 20 | 21 | timestamps() 22 | end 23 | end 24 | ``` 25 | 26 | Favorited articles Ecto schema, using a composite primary key: 27 | 28 | ```elixir 29 | defmodule Blog.FavoritedArticle do 30 | use Ecto.Schema 31 | 32 | @primary_key false 33 | 34 | schema "blog_favorited_articles" do 35 | field :article_uuid, :binary_id, primary_key: true 36 | field :favorited_by_user_uuid, :binary_id, primary_key: true 37 | 38 | timestamps() 39 | end 40 | end 41 | ``` 42 | 43 | Query to set the `favorited` virtual field by joining between the two tables and detecting the presence of an entry in the favorited table for a given user: 44 | 45 | ```elixir 46 | import Ecto.Query 47 | 48 | from a in Article, 49 | left_join: f in FavoritedArticle, on: [article_uuid: a.uuid, favorited_by_user_uuid: ^user_uuid], 50 | select: %{a | favorited: not is_nil(f.article_uuid)} 51 | ``` 52 | -------------------------------------------------------------------------------- /elixir/erlang-observer-on-macos.md: -------------------------------------------------------------------------------- 1 | # Running Erlang's observer on macOS 2 | 3 | Install `wxwidgets` with Homebrew: 4 | 5 | ```shell 6 | brew install wxwidgets 7 | ``` 8 | 9 | Find the location of `wx-config`: 10 | 11 | ```shell 12 | $ which wx-config 13 | /opt/homebrew/bin/wx-config 14 | ``` 15 | 16 | Enable wx via `KERL_CONFIGURE_OPTIONS`: 17 | 18 | ```shell 19 | export KERL_CONFIGURE_OPTIONS="--enable-wx --with-wx-config=/opt/homebrew/bin/wx-config" 20 | ``` 21 | 22 | Install Erlang with asdf _after_ wxwidgets has been installed: 23 | 24 | ```shell 25 | asdf install erlang 26 | ``` 27 | 28 | Note if you already have the specific version of Erlang installed you wil need to remove and then reinstall it. 29 | 30 | ```shell 31 | asdf uninstall erlang 32 | asdf install erlang 33 | ``` 34 | 35 | Run observer via `iex` shell: 36 | 37 | ```shell 38 | iex> :observer.start() 39 | ``` -------------------------------------------------------------------------------- /elixir/error-handling-in-libraries.md: -------------------------------------------------------------------------------- 1 | # Error handling in Elixir libraries 2 | 3 | - Use `{:ok, value}` and `{:error, reason}` tagged tuples. 4 | - Optionally provide *bang* functions (e.g. `foo/1` and `foo!/1`) that raise exceptions. 5 | - Consider using a custom exception struct for the library. 6 | 7 | Example error handling for an Elixir library as [suggested by Michał Muskała](http://michal.muskala.eu/2017/02/10/error-handling-in-elixir-libraries.html): 8 | 9 | ```elixir 10 | defmodule YourLibrary do 11 | defmodule Error do 12 | defexception [:reason] 13 | 14 | def exception(reason), 15 | do: %__MODULE__{reason: reason} 16 | 17 | def message(%__MODULE__{reason: reason}), 18 | do: YourLibrary.format_error(reason) 19 | end 20 | 21 | def get_one(1), 22 | do: {:ok, "one"} 23 | 24 | def get_one(value), 25 | do: {:error, {:not_one, value}} 26 | 27 | def get_one!(arg) do 28 | case get_one(arg) do 29 | {:ok, value} -> value 30 | {:error, reason} -> raise Error, reason 31 | end 32 | end 33 | 34 | def format_error({:not_one, value}), 35 | do: "#{inspect value} is not one" 36 | end 37 | ``` 38 | 39 | Example pattern matching using the `with` special form: 40 | 41 | ```elixir 42 | with {:ok, tokens} <- Redix.command(:redix, ~w(SMEMBERS tokens)), 43 | {:ok, _} <- Postgrex.execute(:pg, "SELECT * FROM users", []) do 44 | :ok 45 | else 46 | {:error, %struct{} = exception} when struct in [Redix.Error, Redix.Connection.Error] -> 47 | Logger.error "Redis error: #{Exception.message(exception)}" 48 | :error 49 | {:error, %Postgrex.Error{} = exception} -> 50 | Logger.error "Postgres error: #{Exception.message(exception)}" 51 | :error 52 | end 53 | ``` 54 | -------------------------------------------------------------------------------- /elixir/ex_unit/assert-match.md: -------------------------------------------------------------------------------- 1 | # ExUnit assert match 2 | 3 | Use [`match?/2`](https://hexdocs.pm/elixir/Kernel.html#match?/2) to assert or refute whether a pattern matches an expression. 4 | 5 | ```elixir 6 | assert match?({:ok, _}, some_fun()) 7 | refute match?({:ok, _}, some_fun()) 8 | ``` 9 | 10 | Attempting to do `assert {:ok, _} = some_fun()` will return a Dialyzer error: 11 | 12 | ``` 13 | The guard clause: 14 | 15 | _ :: {:ok, _} 16 | 17 | === 18 | 19 | false 20 | 21 | can never succeed. 22 | ``` 23 | -------------------------------------------------------------------------------- /elixir/ex_unit/capture-log-level.md: -------------------------------------------------------------------------------- 1 | # ExUnit capture log level 2 | 3 | Configure the logging level when capturing log messages with ExUnit: 4 | 5 | ```elixir 6 | # config/test.exs 7 | config :ex_unit, capture_log: [level: :warn] 8 | ``` 9 | 10 | See [`ExUnit.configure/1`](https://hexdocs.pm/ex_unit/ExUnit.html#configure/1) 11 | -------------------------------------------------------------------------------- /elixir/ex_unit/ignore-tests.md: -------------------------------------------------------------------------------- 1 | # ExUnit ignore tests 2 | 3 | Configure ExUnit to ignore tests tagged with `:skip`, in `test/test_helper.ex`: 4 | 5 | ```elixir 6 | ExUnit.start(exclude: [:skip]) 7 | ``` 8 | 9 | Apply the `:skip` tag to ignore a test: 10 | 11 | ```elixir 12 | @tag :skip 13 | test "an ignored test" do 14 | # ... 15 | end 16 | ``` 17 | 18 | Run tests: 19 | 20 | ```console 21 | $ mix test 22 | Excluding tags: [:skip] 23 | 24 | . 25 | 26 | Finished in 0.1 seconds 27 | 2 tests, 0 failures, 1 skipped 28 | ``` 29 | -------------------------------------------------------------------------------- /elixir/examples/graceful_shutdown/.formatter.exs: -------------------------------------------------------------------------------- 1 | # Used by "mix format" 2 | [ 3 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] 4 | ] 5 | -------------------------------------------------------------------------------- /elixir/examples/graceful_shutdown/.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where third-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | 22 | # Ignore package tarball (built via "mix hex.build"). 23 | graceful_shutdown-*.tar 24 | 25 | 26 | # Temporary files for e.g. tests 27 | /tmp 28 | -------------------------------------------------------------------------------- /elixir/examples/graceful_shutdown/README.md: -------------------------------------------------------------------------------- 1 | # Graceful shutdown example 2 | -------------------------------------------------------------------------------- /elixir/examples/graceful_shutdown/lib/application.ex: -------------------------------------------------------------------------------- 1 | defmodule GracefulShutdown.Application do 2 | @moduledoc false 3 | 4 | use Application 5 | 6 | @impl true 7 | def start(_type, _args) do 8 | children = [ 9 | GracefulShutdown.Server 10 | ] 11 | 12 | opts = [strategy: :one_for_one, name: GracefulShutdown.Supervisor] 13 | Supervisor.start_link(children, opts) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /elixir/examples/graceful_shutdown/lib/server.ex: -------------------------------------------------------------------------------- 1 | defmodule GracefulShutdown.Server do 2 | use GenServer 3 | 4 | def start_link(opts \\ []) do 5 | GenServer.start_link(__MODULE__, [], opts) 6 | end 7 | 8 | @impl GenServer 9 | def init(init_arg) do 10 | Process.flag(:trap_exit, true) 11 | {:ok, init_arg} 12 | end 13 | 14 | @impl GenServer 15 | def terminate(reason, state) do 16 | IO.puts("GracefulShutdownServer is shutting down due to: " <> inspect(reason)) 17 | state 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /elixir/examples/graceful_shutdown/mix.exs: -------------------------------------------------------------------------------- 1 | defmodule GracefulShutdown.MixProject do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :graceful_shutdown, 7 | version: "0.1.0", 8 | elixir: "~> 1.11", 9 | start_permanent: Mix.env() == :prod, 10 | deps: deps() 11 | ] 12 | end 13 | 14 | # Run "mix help compile.app" to learn about applications. 15 | def application do 16 | [ 17 | extra_applications: [:logger], 18 | mod: {GracefulShutdown.Application, []} 19 | ] 20 | end 21 | 22 | # Run "mix help deps" to learn about dependencies. 23 | defp deps do 24 | [ 25 | # {:dep_from_hexpm, "~> 0.3.0"}, 26 | # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} 27 | ] 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /elixir/exception-message.md: -------------------------------------------------------------------------------- 1 | # Exception message 2 | 3 | Use [`Exception.message/1`](https://hexdocs.pm/elixir/Exception.html#message/1) to get a formatted error message. 4 | 5 | ### Example 6 | 7 | Given the following Postgrex error: 8 | 9 | ```elixir 10 | error = %Postgrex.Error{ 11 | connection_id: 58369, 12 | message: nil, 13 | postgres: %{ 14 | code: :duplicate_schema, 15 | file: "pg_namespace.c", 16 | line: "63", 17 | message: "schema \"example\" already exists", 18 | pg_code: "42P06", 19 | routine: "NamespaceCreate", 20 | severity: "ERROR", 21 | unknown: "ERROR" 22 | }, 23 | query: nil 24 | } 25 | ``` 26 | 27 | Will produce the message: 28 | 29 | ```elixir 30 | iex> Exception.message(error) 31 | "ERROR 42P06 (duplicate_schema) schema \"example\" already exists" 32 | ``` 33 | -------------------------------------------------------------------------------- /elixir/external-resource-module-attribute.md: -------------------------------------------------------------------------------- 1 | # External resource module attribute 2 | 3 | The [`@external_resource`](https://hexdocs.pm/elixir/Module.html#module-external_resource) module attribute is useful when a module depends on an external file. By setting this attribute any changes to the external file will cause the referencing module to be recompiled by Elixir. 4 | 5 | > Sometimes a module embeds information from an external file. This attribute allows the module to annotate which external resources have been used. 6 | > 7 | > Tools like Mix may use this information to ensure the module is recompiled in case any of the external resources change. 8 | 9 | An example usage might be if you do some compile-time processing on an external file. 10 | -------------------------------------------------------------------------------- /elixir/gen_server_hibernate_after_start_option.md: -------------------------------------------------------------------------------- 1 | # `GenServer` hibernate after start option 2 | 3 | `GenServer` allows the `:hibernate_after` option to be provided to [`GenServer.start_link3/`](https://hexdocs.pm/elixir/GenServer.html#start_link/3`) as described in the documentation: 4 | 5 | > `:hibernate_after` - if present, the GenServer process awaits any message for the given number of milliseconds and if no message is received, the process goes into hibernation automatically (by calling `:proc_lib.hibernate/3`). 6 | 7 | ### Example `GenServer` 8 | 9 | Here's a basic ping/pong `GenServer` process which can be used to configure the `hibernate_after` option. 10 | 11 | ```elixir 12 | defmodule HibernatingServer do 13 | use GenServer 14 | 15 | def start_link(opts) do 16 | GenServer.start_link(__MODULE__, [], opts) 17 | end 18 | 19 | def ping(server) do 20 | GenServer.call(server, :ping) 21 | end 22 | 23 | @impl GenServer 24 | def init(init_arg) do 25 | {:ok, init_arg} 26 | end 27 | 28 | @impl GenServer 29 | def handle_call(:ping, _from, state) do 30 | {:reply, :pong, state} 31 | end 32 | end 33 | ``` 34 | 35 | #### Usage 36 | 37 | Start the `GenServer` process with option to hibernate after 10 seconds: 38 | 39 | ```elixir 40 | iex> {:ok, pid} = HibernatingServer.start_link(hibernate_after: :timer.seconds(10)) 41 | {:ok, #PID<0.134.0>} 42 | ``` 43 | 44 | Initially the process is running the gen server loop: 45 | 46 | ```elixir 47 | iex> Process.info(pid) 48 | [ 49 | current_function: {:gen_server, :loop, 7}, 50 | initial_call: {:proc_lib, :init_p, 5}, 51 | status: :waiting, 52 | message_queue_len: 0, 53 | links: [#PID<0.108.0>], 54 | dictionary: [ 55 | "$initial_call": {HibernatingServer, :init, 1}, 56 | "$ancestors": [#PID<0.108.0>, #PID<0.81.0>] 57 | ], 58 | trap_exit: false, 59 | error_handler: :error_handler, 60 | priority: :normal, 61 | group_leader: #PID<0.64.0>, 62 | total_heap_size: 233, 63 | heap_size: 233, 64 | stack_size: 11, 65 | reductions: 31, 66 | garbage_collection: [ 67 | max_heap_size: %{error_logger: true, kill: true, size: 0}, 68 | min_bin_vheap_size: 46422, 69 | min_heap_size: 233, 70 | fullsweep_after: 65535, 71 | minor_gcs: 0 72 | ], 73 | suspending: [] 74 | ] 75 | ``` 76 | 77 | After waiting for more than 10 seconds the process should be hibernated: 78 | 79 | ```elixir 80 | iex> Process.info(pid) 81 | [ 82 | current_function: {:erlang, :hibernate, 3}, 83 | initial_call: {:proc_lib, :init_p, 5}, 84 | status: :waiting, 85 | message_queue_len: 0, 86 | links: [#PID<0.108.0>], 87 | dictionary: [ 88 | "$initial_call": {HibernatingServer, :init, 1}, 89 | "$ancestors": [#PID<0.108.0>, #PID<0.81.0>] 90 | ], 91 | trap_exit: false, 92 | error_handler: :error_handler, 93 | priority: :normal, 94 | group_leader: #PID<0.64.0>, 95 | total_heap_size: 32, 96 | heap_size: 32, 97 | stack_size: 0, 98 | reductions: 37, 99 | garbage_collection: [ 100 | max_heap_size: %{error_logger: true, kill: true, size: 0}, 101 | min_bin_vheap_size: 46422, 102 | min_heap_size: 233, 103 | fullsweep_after: 65535, 104 | minor_gcs: 0 105 | ], 106 | suspending: [] 107 | ] 108 | ``` 109 | 110 | Note the process info shows the `total_heap_size`, `heap_size`, and `stack_size` have all been reduced during the process hibernation as a garbage collection has taken place. 111 | 112 | Sending a message to the process will start it running again: 113 | 114 | ```elixir 115 | iex> HibernatingServer.ping(pid) 116 | :pong 117 | 118 | iex> Process.info(pid, :current_function) 119 | {:current_function, {:gen_server, :loop, 7}} 120 | ``` 121 | 122 | If no further messages are received within 10 seconds it will hibernate again: 123 | 124 | ```elixir 125 | iex> Process.info(pid, :current_function) 126 | {:current_function, {:erlang, :hibernate, 3}} 127 | ``` 128 | 129 | The process hibernation will happen again after any 10 second inactivity period when it is running. 130 | -------------------------------------------------------------------------------- /elixir/genserver-name-registration.md: -------------------------------------------------------------------------------- 1 | # GenServer name registration using `:via` 2 | 3 | The `:via` option expects a module that exports: 4 | 5 | - `register_name/2` 6 | - `unregister_name/1` 7 | - `whereis_name/1` 8 | - `send/2` 9 | 10 | One such example is the :global module which uses these functions for keeping the list of names of processes and their associated PIDs that are available globally for a network of Elixir nodes. Elixir also ships with a local, decentralized and scalable registry called [Registry](https://hexdocs.pm/elixir/Registry.html) for locally storing names that are generated dynamically. 11 | -------------------------------------------------------------------------------- /elixir/graceful_shutdown.md: -------------------------------------------------------------------------------- 1 | # Graceful shutdown 2 | 3 | Erlang versions 20 and newer have support for UNIX signal handling. This allows Elixir to support graceful shutdown by sending the `SIGTERM` signal to an Elixir operating system process. The default behaviour when receiving this signal is to call `init:stop/0` to stop the BEAM VM. 4 | 5 | Start an `iex` shell: 6 | 7 | ```shell 8 | $ iex 9 | ``` 10 | 11 | Send `SIGTERM` signal to the Elixir (`iex`) process: 12 | 13 | ```shell 14 | kill -s TERM 15 | ``` 16 | 17 | Note: You can use `ps aux | grep iex` to identify the process id. 18 | 19 | This will cause the Elixir shell to shutdown: 20 | 21 | ``` 22 | Interactive Elixir (1.11.3) - press Ctrl+C to exit (type h() ENTER for help) 23 | iex(1)> 24 | [info] SIGTERM received - shutting down 25 | ``` 26 | 27 | The `GenServer` behaviour includes a `terminate/2` callback function which can be used to gracefully shutdown the process when it has been started by an Elixir application's supervision tree. 28 | 29 | ```elixir 30 | defmodule GracefulShutdownServer do 31 | use GenServer 32 | 33 | def start_link(opts \\ []) do 34 | GenServer.start_link(__MODULE__, [], opts) 35 | end 36 | 37 | @impl GenServer 38 | def init(init_arg) do 39 | Process.flag(:trap_exit, true) 40 | {:ok, init_arg} 41 | end 42 | 43 | @impl GenServer 44 | def terminate(reason, state) do 45 | IO.puts("GracefulShutdownServer is shutting down due to: " <> inspect(reason)) 46 | state 47 | end 48 | end 49 | ``` 50 | 51 | Note the `GenServer` process _must_ trap exits for the `terminate/2` callback to be called. 52 | 53 | See the [graceful_shutdown example](elixir/examples/graceful_shutdown) 54 | -------------------------------------------------------------------------------- /elixir/install-elixir-with-asdf.md: -------------------------------------------------------------------------------- 1 | # Install Elixir and Erlang with `asdf` version manager 2 | 3 | 1. Install [asdf](https://github.com/asdf-vm/asdf). 4 | 5 | 2. Install plugins 6 | 7 | ```sh 8 | asdf plugin-add erlang https://github.com/asdf-vm/asdf-erlang.git 9 | asdf plugin-add elixir https://github.com/asdf-vm/asdf-elixir.git 10 | ``` 11 | 12 | 3. Install Erlang: 13 | 14 | ```sh 15 | asdf install erlang 21.2.3 16 | asdf global erlang 21.2.3 17 | ``` 18 | 19 | 4. Install Elixir: 20 | 21 | ```sh 22 | asdf install elixir 1.8.0-otp-21 23 | asdf global elixir 1.8.0-otp-21 24 | ``` 25 | -------------------------------------------------------------------------------- /elixir/monotonic-time.md: -------------------------------------------------------------------------------- 1 | # Use monotonic time for timing 2 | 3 | You should use Erlang's [monotonic_time](http://erlang.org/doc/man/erlang.html#monotonic_time-0) for timing since it is not affected by OS clock changes. 4 | 5 | This is [used internally](http://erlang.org/doc/apps/erts/time_correction.html#Erlang_Monotonic_Time) as the "time engine" for almost all Erlang's time based processing. 6 | 7 | > `System.monotonic_time/1` 8 | 9 | > Returns the current monotonic time in the given time unit. 10 | 11 | > This time is monotonically increasing and starts in an unspecified point in time. 12 | 13 | ```elixir 14 | start_time = System.monotonic_time(:microseconds) 15 | 16 | # ... do something you want to be timed 17 | 18 | end_time = System.monotonic_time(:microseconds) 19 | 20 | duration_in_microseconds = end_time - start_time 21 | ``` 22 | -------------------------------------------------------------------------------- /elixir/path-from-app-dir.md: -------------------------------------------------------------------------------- 1 | # Read from the application's directory 2 | 3 | Use [`Application.app_dir/2`](https://hexdocs.pm/elixir/Application.html#app_dir/2) to construct a path relative to the application's directory, such as `/priv`: 4 | 5 | ```elixir 6 | path = Application.app_dir(:myapp, "priv") 7 | ``` 8 | 9 | This will be `/priv` when run in the `myapp` project. It will also work when the project is referenced as a dependency in another project. 10 | 11 | Ensure you include `priv` in the project package inside `mix.exs`: 12 | 13 | ```elixir 14 | defp package do 15 | [ 16 | files: ["lib", "priv"], 17 | # ... 18 | ] 19 | end 20 | ``` 21 | -------------------------------------------------------------------------------- /elixir/pattern-match-struct-name.md: -------------------------------------------------------------------------------- 1 | # Pattern match struct name 2 | 3 | Define one or more Elixir structs: 4 | 5 | ```elixir 6 | defmodule Foo do 7 | defstruct [:name] 8 | 9 | def inspect(%Foo{name: name}), do: "Foo<#{name}>" 10 | end 11 | 12 | defmodule Bar do 13 | defstruct [:name] 14 | 15 | def inspect(%Bar{name: name}), do: "Bar<#{name}>" 16 | end 17 | ``` 18 | 19 | Pattern match on the struct module using `%module{}`: 20 | 21 | ```elixir 22 | defmodule Inspector do 23 | def inspect(%module{} = struct), do: module.inspect(struct) 24 | end 25 | ``` 26 | 27 | Usage: 28 | 29 | ```elixir 30 | Inspector.inspect(%Foo{name: "foo"}) # Foo 31 | Inspector.inspect(%Bar{name: "bar"}) # Bar 32 | ``` 33 | 34 | See also [Elixir protocols](https://elixir-lang.org/getting-started/protocols.html) for defining polymorphic behaviour. 35 | -------------------------------------------------------------------------------- /elixir/postgrex-copy-from-stdin.md: -------------------------------------------------------------------------------- 1 | # Postgrex copy from STDIN using `Postgrex.stream/4` 2 | 3 | [Postgrex.stream/4](https://hexdocs.pm/postgrex/Postgrex.html#stream/4) returns a stream for a query on a database connection. You can use the stream as a `Collectable` to copy data into a table. 4 | 5 | The example given in the Postgrex documentation copies data read from a file: 6 | 7 | ```elixir 8 | Postgrex.transaction(pid, fn(conn) -> 9 | stream = Postgrex.stream(conn, "COPY posts FROM STDIN", []) 10 | Enum.into(File.stream!("posts"), stream) 11 | end) 12 | ``` 13 | 14 | By default this uses the text format: fields are tab delimited and rows are separated by a newline character. 15 | 16 | ### PostgreSQL BINARY format 17 | 18 | > The binary format option causes all data to be stored/read as binary format rather than as text. It is somewhat faster than the text and CSV formats, but a binary-format file is less portable across machine architectures and PostgreSQL versions. 19 | 20 | To use PostgreSQL's [BINARY format for copying](https://www.postgresql.org/docs/current/static/sql-copy.html) you must implement their file format, and encode each field as binary data according to its exact datatype. 21 | 22 | I wanted to implement and benchmark this approach for the Elixir [EventStore](https://github.com/slashdotdash/eventstore) and compare its performance with the existing multirow insert SQL statement. 23 | 24 | Below is the `EventStore.Storage.Appender` module that encodes the events to their binary representation, prefixed with the header, and suffixed with the file trailer. 25 | 26 | ```elixir 27 | defmodule EventStore.Storage.Appender do 28 | @moduledoc """ 29 | Append-only storage of events to a stream 30 | """ 31 | 32 | require Logger 33 | 34 | @doc """ 35 | Append the given list of events to the given stream. 36 | 37 | Returns `{:ok, count}` on success, where count indicates the number of appended events. 38 | """ 39 | def append(conn, stream_id, events) do 40 | conn 41 | |> stream_events_into_table(events) 42 | |> handle_response(stream_id, events) 43 | end 44 | 45 | @header ["PGCOPY\n\xff\r\n\0\0\0\0\0\0\0\0\0\0"] 46 | @trailer [<<-1 :: signed-16>>] 47 | 48 | def stream_events_into_table(conn, events) do 49 | try do 50 | Postgrex.transaction(conn, fn (conn) -> 51 | stream = Postgrex.stream(conn, "COPY events(event_id, stream_id, stream_version, event_type, correlation_id, causation_id, data, metadata, created_at) FROM STDIN (FORMAT BINARY)", [], max_rows: 0) 52 | 53 | events 54 | |> pg_copy_stream() 55 | |> Enum.into(stream) 56 | end) 57 | rescue 58 | error -> {:error, error} 59 | end 60 | end 61 | 62 | def pg_copy_stream(events) do 63 | [@header, encode_events(events), @trailer] 64 | |> Stream.concat() 65 | end 66 | 67 | defp encode_events(events) do 68 | events 69 | |> Stream.flat_map(&encode/1) 70 | |> Stream.drop(-1) 71 | end 72 | 73 | defp encode(event) do 74 | [ 75 | <<9>>, 76 | <<8 :: signed-32, event.event_id :: signed-64>>, 77 | <<8 :: signed-32, event.stream_id :: signed-64>>, 78 | <<8 :: signed-32, event.stream_version :: signed-64>>, 79 | <>, event.event_type, 80 | <>, event.correlation_id, 81 | <>, event.causation_id, 82 | <>, event.data, 83 | <>, event.metadata, 84 | encode_date(event.created_at), 85 | "\0", 86 | ] 87 | end 88 | 89 | @gs_epoch :calendar.datetime_to_gregorian_seconds({{2000, 1, 1}, {0, 0, 0}}) 90 | 91 | defp encode_date(%NaiveDateTime{year: year, month: month, day: day, hour: hour, minute: min, second: sec, microsecond: {usec, _}}) do 92 | datetime = {{year, month, day}, {hour, min, sec}} 93 | secs = :calendar.datetime_to_gregorian_seconds(datetime) - @gs_epoch 94 | <<8 :: signed-32, secs * 1_000_000 + usec :: signed-64>> 95 | end 96 | 97 | defp handle_response({:ok, %Postgrex.Stream{}}, stream_id, events) do 98 | event_count = length(events) 99 | _ = Logger.info(fn -> "appended #{event_count} event(s) to stream id #{stream_id}" end) 100 | {:ok, event_count} 101 | end 102 | 103 | defp handle_response({:error, %Postgrex.Error{postgres: %{code: :foreign_key_violation, message: message}}}, stream_id, _events) do 104 | _ = Logger.warn(fn -> "failed to append events to stream id #{stream_id} due to: #{inspect message}" end) 105 | {:error, :stream_not_found} 106 | end 107 | 108 | defp handle_response({:error, %Postgrex.Error{postgres: %{code: :unique_violation, message: message}}}, stream_id, _events) do 109 | _ = Logger.warn(fn -> "failed to append events to stream id #{stream_id} due to: #{inspect message}" end) 110 | {:error, :wrong_expected_version} 111 | end 112 | 113 | defp handle_response({:error, reason}, stream_id, _events) do 114 | _ = Logger.warn(fn -> "failed to append events to stream id #{stream_id} due to: #{inspect reason}" end) 115 | {:error, reason} 116 | end 117 | end 118 | ``` 119 | 120 | For the typical use case of the EventStore, appending fewer than 100 events at a time, it was *twice as slow* as using a multirow SQL insert statement. 121 | 122 | Using `COPY FROM STDIN` should only be used for large data imports. 123 | -------------------------------------------------------------------------------- /elixir/process-links.md: -------------------------------------------------------------------------------- 1 | # Process links 2 | 3 | ```elixir 4 | p1 = 5 | spawn(fn -> 6 | receive do 7 | :exit -> exit(:exit) 8 | end 9 | end) 10 | 11 | p2 = 12 | spawn(fn -> 13 | true = Process.link(p1) 14 | 15 | receive do 16 | :exit -> exit(:exit) 17 | end 18 | end) 19 | 20 | p1_ref = Process.monitor(p1) 21 | p2_ref = Process.monitor(p2) 22 | 23 | send(p2, :exit) 24 | 25 | assert_receive {:DOWN, ^p1_ref, :process, _pid, _reason} 26 | assert_receive {:DOWN, ^p2_ref, :process, _pid, _reason} 27 | ``` 28 | -------------------------------------------------------------------------------- /elixir/remote-observer-distillery-release.md: -------------------------------------------------------------------------------- 1 | # Running Observer on a remote node deployed with Distillery 2 | 3 | Include `:runtime_tools` in the `extra_applications` section of `mix.exs`: 4 | 5 | ```elixir 6 | def application do 7 | [ 8 | mod: {Example.Web, []}, 9 | env: [environment_name: Mix.env], 10 | extra_applications: [ 11 | :logger, 12 | :runtime_tools, 13 | ], 14 | ] 15 | end 16 | ``` 17 | 18 | Or in the Distillery `rel/config.exs` configuration file: 19 | 20 | ```elixir 21 | release :example_app do 22 | set version: "1.2.3" 23 | set applications: [ 24 | example_web: :permanent, 25 | runtime_tools: :temporary, 26 | ] 27 | end 28 | ``` 29 | 30 | Discover the deployed app's local port number via `epmd`: 31 | 32 | ```console 33 | $ ssh remotehost "epmd -names" 34 | epmd: up and running on port 4369 with data: 35 | name example_app at port 39593 36 | ``` 37 | 38 | Replace `remotehost` with remote host name or IP address. 39 | 40 | Start an SSH connection and tunnel epmd and your remote app's ports locally: 41 | 42 | ```console 43 | ssh -L 4369:localhost:4369 -L 39593:localhost:39593 remotehost 44 | ``` 45 | 46 | Locate and copy the Erlang host cookie set by Distillery in `builds/_build/prod/example_app/releases/1.2.3/vm.args` 47 | 48 | Start an `iex` console using the cookie located above: 49 | 50 | ```console 51 | $ iex --name debug@127.0.0.1 --hidden --cookie cookie 52 | ``` 53 | 54 | Connect to the remote node and start an observer. 55 | 56 | ``` 57 | iex> Node.connect(:"example_app@127.0.0.1") 58 | iex> :observer.start() 59 | ``` 60 | 61 | You can then connect to the remote node from the Observer "Nodes" menu. 62 | -------------------------------------------------------------------------------- /elixir/remove-unused-dependencies-from-mix-lock.md: -------------------------------------------------------------------------------- 1 | # Remove unused dependencies from `mix.lock` 2 | 3 | Run this command to remove unused dependencies from both `/deps` folder and the `mix.lock` file: 4 | 5 | ```shell 6 | mix deps.clean --unlock --unused 7 | ``` 8 | -------------------------------------------------------------------------------- /elixir/revert-hex-package.md: -------------------------------------------------------------------------------- 1 | # Revert Hex package 2 | 3 | A package published to Hex may be [reverted](https://hex.pm/docs/tasks#hex_publish) up to one hour after its publication: 4 | 5 | ```console 6 | mix hex.publish --revert 0.10.0 7 | ``` 8 | 9 | Older packages can not be reverted. 10 | -------------------------------------------------------------------------------- /elixir/rollbar-exception-reporting-for-phoenix.md: -------------------------------------------------------------------------------- 1 | # Rollbar exception reporting for Phoenix 2 | 3 | Install the [rollbax](https://hex.pm/packages/rollbax) exception tracking and logging library. 4 | 5 | Configure Rollbar in `config/config.exs`: 6 | 7 | ```elixir 8 | config :rollbax, 9 | access_token: "<>", 10 | environment: "production" 11 | 12 | config :logger, 13 | backends: [:console, Rollbax.Logger] 14 | ``` 15 | 16 | Enable Rollbar error logging in production environment (`config/prod.exs`): 17 | 18 | ```elixir 19 | config :logger, Rollbax.Logger, 20 | level: :error 21 | ``` 22 | 23 | Use Rollbar console logging mode in development (`config/dev.exs`): 24 | 25 | ```elixir 26 | config :rollbax, enabled: :log 27 | ``` 28 | 29 | Disable Rollbar logging for tests (`config/test.exs`): 30 | 31 | ```elixir 32 | config :rollbax, enabled: false 33 | ``` 34 | 35 | Add `Plug.ErrorHandler` to your web router module and report errors to Rollbar: 36 | 37 | ```elixir 38 | defmodule ExampleWeb.Router do 39 | use ExampleWeb, :router 40 | use Plug.ErrorHandler 41 | 42 | # Ignore Phoenix routing errors 43 | defp handle_errors(_conn, %{reason: %Phoenix.Router.NoRouteError{}}), do: :ok 44 | 45 | # Log all other errors to Rollbar 46 | defp handle_errors(conn, %{kind: kind, reason: reason, stack: stacktrace}) do 47 | conn = 48 | conn 49 | |> Plug.Conn.fetch_cookies() 50 | |> Plug.Conn.fetch_query_params() 51 | 52 | conn_data = %{ 53 | "request" => %{ 54 | "cookies" => conn.req_cookies, 55 | "url" => "#{conn.scheme}://#{conn.host}:#{conn.port}#{conn.request_path}", 56 | "user_ip" => (conn.remote_ip |> Tuple.to_list() |> Enum.join(".")), 57 | "headers" => Enum.into(conn.req_headers, %{}), 58 | "params" => conn.params, 59 | "method" => conn.method, 60 | } 61 | } 62 | 63 | Rollbax.report(kind, reason, stacktrace, %{}, conn_data) 64 | end 65 | end 66 | ``` 67 | 68 | Note I've chosen to ignore Phoenix's no route errors. 69 | -------------------------------------------------------------------------------- /elixir/running-ecto-migrations-during-deployment.md: -------------------------------------------------------------------------------- 1 | # Running Ecto database migrations during deployment 2 | 3 | Using distillery to deploy Elixir apps, you can run Ecto database migrations as a custom command: 4 | 5 | - [Running Migrations, etc.](https://hexdocs.pm/distillery/running-migrations.html) 6 | 7 | Here's the example migration module: 8 | 9 | ```elixir 10 | defmodule MyApp.ReleaseTasks do 11 | @start_apps [ 12 | :crypto, 13 | :ssl, 14 | :postgrex, 15 | :ecto 16 | ] 17 | 18 | @myapps [ 19 | :myapp 20 | ] 21 | 22 | @repos [ 23 | MyApp.Repo 24 | ] 25 | 26 | def seed do 27 | IO.puts "Loading myapp.." 28 | # Load the code for myapp, but don't start it 29 | :ok = Application.load(:myapp) 30 | 31 | IO.puts "Starting dependencies.." 32 | # Start apps necessary for executing migrations 33 | Enum.each(@start_apps, &Application.ensure_all_started/1) 34 | 35 | # Start the Repo(s) for myapp 36 | IO.puts "Starting repos.." 37 | Enum.each(@repos, &(&1.start_link(pool_size: 1))) 38 | 39 | # Run migrations 40 | Enum.each(@myapps, &run_migrations_for/1) 41 | 42 | # Run the seed script if it exists 43 | seed_script = Path.join([priv_dir(:myapp), "repo", "seeds.exs"]) 44 | if File.exists?(seed_script) do 45 | IO.puts "Running seed script.." 46 | Code.eval_file(seed_script) 47 | end 48 | 49 | # Signal shutdown 50 | IO.puts "Success!" 51 | :init.stop() 52 | end 53 | 54 | def priv_dir(app), do: "#{:code.priv_dir(app)}" 55 | 56 | defp run_migrations_for(app) do 57 | IO.puts "Running migrations for #{app}" 58 | Ecto.Migrator.run(MyApp.Repo, migrations_path(app), :up, all: true) 59 | end 60 | 61 | defp migrations_path(app), do: Path.join([priv_dir(app), "repo", "migrations"]) 62 | defp seed_path(app), do: Path.join([priv_dir(app), "repo", "seeds.exs"]) 63 | 64 | end 65 | ``` 66 | -------------------------------------------------------------------------------- /elixir/start-application-dependencies.md: -------------------------------------------------------------------------------- 1 | # Start an Elixir application's dependencies 2 | 3 | Use the [`Application.spec/2` function](https://hexdocs.pm/elixir/Application.html#spec/2) to list the dependencies of an application. This can be useful if you run your tests _without_ starting the application using the `--no-start` ExUnit flag. In the test helper file you can then manually start your application's dependencies without it being started. 4 | 5 | ```elixir 6 | # test/test_helper.exs 7 | Application.load(:my_app) 8 | 9 | for app <- Application.spec(:my_app, :applications) do 10 | {:ok, _apps} = Application.ensure_all_started(app) 11 | end 12 | ``` 13 | -------------------------------------------------------------------------------- /elixir/testing-plugs.md: -------------------------------------------------------------------------------- 1 | # Testing plugs 2 | 3 | Use [`Plug.Test`](https://hexdocs.pm/plug/Plug.Test.html) as a convenience for testing plugs. 4 | 5 | ```elixir 6 | defmodule PlugTest do 7 | use ExUnit.Case, async: true 8 | use Plug.Test 9 | 10 | test "POST /foo" do 11 | conn = 12 | conn(:post, "/foo", "") 13 | |> put_req_header("authorization", "Basic YWxhZGRpbjpvcGVuc2VzYW1l") 14 | |> put_req_header("content-type", "application/json") 15 | |> MyPlug.call([]) 16 | 17 | assert conn.state == :sent 18 | assert conn.status == 200 19 | assert conn.resp_body == "" 20 | end 21 | end 22 | ``` 23 | -------------------------------------------------------------------------------- /elixir/travis-ci-ecto-migrations.md: -------------------------------------------------------------------------------- 1 | # Travis CI Ecto migrations 2 | 3 | To run unit tests on [Travis CI](https://travis-ci.org/) using Ecto and PostgreSQL you must: 4 | 5 | 1. Include the `postgresql` service and add-on. 6 | 2. Run `mix ecto.create` and `mix ecto.migrate` as a `before_script`. 7 | 8 | Here's an example `travis.yml` config file: 9 | 10 | ```yaml 11 | language: elixir 12 | 13 | elixir: 14 | - 1.4.5 15 | 16 | otp_release: 17 | - 20.0 18 | 19 | services: 20 | - postgresql 21 | 22 | before_script: 23 | - MIX_ENV=test mix do ecto.create, ecto.migrate 24 | 25 | sudo: required 26 | dist: trusty 27 | 28 | addons: 29 | postgresql: "9.6" 30 | ``` 31 | -------------------------------------------------------------------------------- /elixir/use-ecto-utc-datetime.md: -------------------------------------------------------------------------------- 1 | # Use Ecto's UTC datetime type 2 | 3 | Use Ecto's `utc_datetime` or `utc_datetime_usec` to store timestamps in UTC time as described in [Time zones in PostgreSQL, Elixir and Phoenix](https://www.amberbit.com/blog/2017/8/3/time-zones-in-postgresql-elixir-and-phoenix/). 4 | 5 | ```elixir 6 | schema "events" do 7 | field(:starts_at, :utc_datetime) 8 | field(:ends_at, :utc_datetime) 9 | 10 | timestamps(type: :utc_datetime_usec) 11 | end 12 | ``` 13 | 14 | Note the default type for `timestamps` is `:naive_datetime` unless the `type` option is provided, as shown above. 15 | -------------------------------------------------------------------------------- /erlang/erlang_version.md: -------------------------------------------------------------------------------- 1 | # Erlang version 2 | 3 | ```shell 4 | erl -eval '{ok, Version} = file:read_file(filename:join([code:root_dir(), "releases", erlang:system_info(otp_release), "OTP_VERSION"])), io:fwrit 5 | e(Version), halt().' -noshell 6 | ``` 7 | 8 | ### Example 9 | 10 | ```shell 11 | $ erl -eval '{ok, Version} = file:read_file(filename:join([code:root_dir(), "releases", erlang:system_info(otp_release), "OTP_VERSION"])), io:fwrit 12 | e(Version), halt().' -noshell 13 | 24.3.4.2 14 | ``` 15 | 16 | ### Reference 17 | 18 | * [How to get Erlang's release version number from a shell?](https://stackoverflow.com/questions/9560815/how-to-get-erlangs-release-version-number-from-a-shell) 19 | -------------------------------------------------------------------------------- /git/rebase-root-commit.md: -------------------------------------------------------------------------------- 1 | # Rebase root git commit 2 | 3 | To reapply commits from the first commit in a repo, use an interactive rebase from `--root`: 4 | 5 | ```console 6 | $ git rebase -i --root 7 | ``` 8 | 9 | This allows you to rebase the root commit on a branch. 10 | -------------------------------------------------------------------------------- /git/set-origin-url.md: -------------------------------------------------------------------------------- 1 | # Set origin URL 2 | 3 | Use git+ssh: 4 | 5 | ```console 6 | $ git remote set-url origin git@github.com:slashdotdash/til.git 7 | ``` 8 | 9 | Use https: 10 | 11 | ```console 12 | $ git remote set-url origin https://github.com/slashdotdash/til.git 13 | ``` 14 | -------------------------------------------------------------------------------- /git/syncing-a-github-fork.md: -------------------------------------------------------------------------------- 1 | # Syncing a GitHub fork 2 | 3 | Sync a fork of a repository to keep it up-to-date with the upstream repository. 4 | 5 | List tracked repositories to identify name of the upstream remote: 6 | 7 | ```console 8 | $ git remote show 9 | upstream 10 | origin 11 | ``` 12 | 13 | By default, GitHub will name the upstream remote after the forked repository's owner. 14 | 15 | Fetch upstream repository: 16 | 17 | ```console 18 | $ git fetch upstream 19 | ``` 20 | 21 | Rebase upstream changes into local branch: 22 | 23 | ```console 24 | $ git checkout master 25 | $ git rebase upstream/master 26 | ``` 27 | -------------------------------------------------------------------------------- /javascript/capture-escape-keypress.md: -------------------------------------------------------------------------------- 1 | # Capture "Escape" keypress 2 | 3 | ```typescript 4 | function captureEscapeKeypress(event: Event) { 5 | const keyboardEvent = event as KeyboardEvent; 6 | 7 | if (keyboardEvent && keyboardEvent.key == 'Escape') { 8 | event.preventDefault(); 9 | return false; 10 | } 11 | } 12 | ``` 13 | 14 | ```typescript 15 | element.addEventListener('keydown', captureEscapeKeypress); 16 | element.removeEventListener('keydown', captureEscapeKeypress); 17 | ``` -------------------------------------------------------------------------------- /julia/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting started with Julia 2 | 3 | Install Julia with asdf: 4 | 5 | ```shell 6 | asdf plugin-add julia https://github.com/rkyleg/asdf-julia.git 7 | asdf list-all julia 8 | asdf install julia 9 | asdf global julia 10 | ``` 11 | 12 | ## Jupyter notebook 13 | 14 | Install Jupyter lab and notebook with `pip`: 15 | 16 | ```shell 17 | pip install jupyterlab 18 | pip install notebook 19 | ``` 20 | 21 | ### Add Julia to Jupyter 22 | 23 | ``` 24 | $ julia 25 | julia> import Pkg 26 | 27 | julia> Pkg.add("IJulia") 28 | ``` 29 | 30 | Start Jupyter lab: 31 | 32 | ```shell 33 | jupyter-lab 34 | ``` 35 | 36 | Start Jupyter notebook: 37 | 38 | ```shell 39 | jupyter notebook 40 | ``` 41 | -------------------------------------------------------------------------------- /linux/systemd-service-configuration.md: -------------------------------------------------------------------------------- 1 | # systemd service configuration 2 | 3 | [systemd](https://github.com/systemd/systemd) is an init system used in Linux distributions, such as Ubuntu, to to bootstrap the user space. 4 | 5 | List available services: 6 | 7 | ```console 8 | $ systemctl list-unit-files 9 | ``` 10 | 11 | Show system status: 12 | 13 | ```console 14 | $ systemctl status 15 | ``` 16 | 17 | ### Create a new service 18 | 19 | Create the `foo` service config file in `/etc/systemd/system/foo.service`: 20 | 21 | ``` 22 | [Unit] 23 | Description=Job that runs the foo daemon 24 | Documentation=man:foo(1) 25 | 26 | [Service] 27 | Type=forking 28 | Environment=statedir=/var/cache/foo 29 | ExecStartPre=/usr/bin/mkdir -p ${statedir} 30 | ExecStart=/usr/bin/foo-daemon --arg1 "hello world" --statedir ${statedir} 31 | 32 | [Install] 33 | WantedBy=multi-user.target 34 | ``` 35 | 36 | Reload the systemd daemon: 37 | 38 | ```console 39 | $ sudo systemctl daemon-reload 40 | ``` 41 | 42 | Control the service (enable/start/stop/restart): 43 | 44 | ```console 45 | $ sudo systemctl enable foo 46 | $ sudo systemctl start foo 47 | $ sudo systemctl restart foo 48 | $ sudo systemctl stop foo 49 | $ sudo systemctl status foo 50 | ``` 51 | 52 | #### Reference 53 | 54 | - [SystemdForUpstartUsers](https://wiki.ubuntu.com/SystemdForUpstartUsers) 55 | -------------------------------------------------------------------------------- /mac/increase-open-file-limit.md: -------------------------------------------------------------------------------- 1 | # Increase open file limit 2 | 3 | By default, the maximum number of files that macOS can open is set to 12,288: 4 | 5 | ```console 6 | $ sysctl kern.maxfiles 7 | kern.maxfiles: 12288 8 | ``` 9 | 10 | The maximum number of files a given process can open is 10,240: 11 | 12 | ```console 13 | $ sysctl kern.maxfilesperproc 14 | kern.maxfilesperproc: 10240 15 | ``` 16 | 17 | Increase limits: 18 | 19 | ```console 20 | $ sudo launchctl limit maxfiles 1000000 1000000 21 | ``` 22 | 23 | Note the increased limits will reset to the defaults after a reboot. 24 | 25 | ## References 26 | 27 | - [Increase the maximum number of open file descriptors in Snow Leopard?](https://superuser.com/questions/302754/increase-the-maximum-number-of-open-file-descriptors-in-snow-leopard) 28 | - [Maximum Files in Mac OS X](http://krypted.com/mac-os-x/maximum-files-in-mac-os-x/) 29 | -------------------------------------------------------------------------------- /mac/mac-sleep-wake.md: -------------------------------------------------------------------------------- 1 | ## macOS sleep and wake 2 | 3 | The `pmset` command can be used to identify when macOS went to sleep or woke up: 4 | 5 | ```shell 6 | pmset -g log | grep -e " Sleep " -e " Wake " -e " DarkWake " 7 | ``` 8 | 9 | ## Example 10 | 11 | Get the most recent sleep or wake: 12 | 13 | ```shell 14 | pmset -g log | grep -e " Sleep " -e " Wake " -e " DarkWake " | tail -1 15 | 2020-09-23 08:46:04 +0100 Wake DarkWake to FullWake from Normal Sleep [CDNVA] due to Notification: Using AC (Charge:98%) 16 | ``` 17 | -------------------------------------------------------------------------------- /phoenix-framework/page-specific-titles.md: -------------------------------------------------------------------------------- 1 | # Page specific titles in Phoenix framework 2 | 3 | 1. Call the `title/2` function from the view module in the layout template: 4 | 5 | ```html 6 | 7 | 8 | 9 | <%= @view_module.title(@view_template, assigns) %> 10 | 11 | 12 | ``` 13 | 14 | 2. Add a `title/2` function for each specific page in its view module: 15 | 16 | ```elixir 17 | defmodule ExampleWeb.ExampleView do 18 | use ExampleWeb, :view 19 | 20 | def title("show.html", %{post: post}), do: post.title 21 | def title("new.html", _), do: "Create a post" 22 | end 23 | ``` 24 | 25 | 3. Add a fallback `title/2` function used when there is no matching page specific title in a view module: 26 | 27 | ```elixir 28 | defmodule ExampleWeb.Helpers.Defaults do 29 | defmacro __using__(_) do 30 | quote do 31 | @before_compile unquote(__MODULE__) 32 | end 33 | end 34 | 35 | defmacro __before_compile__(_env) do 36 | quote do 37 | def title(_view_template, _assigns), do: "Example" 38 | end 39 | end 40 | end 41 | ``` 42 | 43 | 4. Use the defaults module in the Phoenix web module: 44 | 45 | ```elixir 46 | defmodule ExampleWeb do 47 | def view do 48 | quote do 49 | use Phoenix.View, root: "lib/example_web/templates", 50 | namespace: ExampleWeb 51 | 52 | # Import convenience functions from controllers 53 | import Phoenix.Controller, only: [get_csrf_token: 0, get_flash: 2, view_module: 1] 54 | 55 | use ExampleWeb.Helpers.Defaults 56 | end 57 | end 58 | end 59 | ``` 60 | 61 | ### References 62 | 63 | - [SEO Tags In Phoenix](http://blog.danielberkompas.com/2016/01/28/seo-tags-in-phoenix.html) 64 | -------------------------------------------------------------------------------- /postgres/formatting-timestamps.md: -------------------------------------------------------------------------------- 1 | # Formatting timestamps 2 | 3 | Format a timestamp: 4 | 5 | ```sql 6 | SELECT to_char(now() - '2019-10-25 8:30'::timestamp, 'HH:MI'); 7 | ``` 8 | 9 | See [Data Type Formatting Functions](https://www.postgresql.org/docs/current/functions-formatting.html) for template patterns. 10 | 11 | Convert a timestamp to minutes: 12 | 13 | ```sql 14 | SELECT extract(epoch from (now() - '2019-10-25 8:30'::timestamp)) / 60; 15 | ``` 16 | -------------------------------------------------------------------------------- /postgres/gapless-sequence.md: -------------------------------------------------------------------------------- 1 | # Gapless sequence in Postgres 2 | 3 | You can use a separate counter table and either a Postgres function or a [Common Table Expression](https://www.postgresql.org/docs/current/queries-with.html) (CTE) to update the counter value during insert into your target table. 4 | 5 | ### Using a Postgres function 6 | 7 | 1. Create a "counter" table containing a single "last value" row: 8 | 9 | ```sql 10 | CREATE TABLE counter (last_value integer NOT NULL); 11 | INSERT INTO counter (last_value) VALUES (0); 12 | ``` 13 | 14 | 2. Create a `get_next_id` function: 15 | 16 | ```sql 17 | CREATE OR REPLACE FUNCTION get_next_id(countertable regclass, countercolumn text) RETURNS integer AS $$ 18 | DECLARE 19 | next_value integer; 20 | BEGIN 21 | EXECUTE format('UPDATE %s SET %I = %I + 1 RETURNING %I', countertable, countercolumn, countercolumn, countercolumn) INTO next_value; 22 | RETURN next_value; 23 | END; 24 | $$ LANGUAGE plpgsql; 25 | 26 | COMMENT ON get_next_id(countername regclass) IS 'Increment and return value from integer column $2 in table $1'; 27 | ``` 28 | 29 | 3. Create your desired table to use the gapless sequence: 30 | 31 | ```sql 32 | CREATE TABLE example (id integer NOT NULL, value text NOT NULL); 33 | CREATE UNIQUE INDEX events_pkey ON example(id); 34 | ``` 35 | 36 | Usage: 37 | 38 | ```sql 39 | INSERT INTO example(id, value) 40 | VALUES (get_next_id('counter','last_value'), 'example'); 41 | ``` 42 | 43 | - [PostgreSQL gapless sequences](https://stackoverflow.com/questions/9984196/postgresql-gapless-sequences) (using a function) 44 | 45 | ### Using a CTE 46 | 47 | Use a separate "counter" table and a [Common Table Expression](https://www.postgresql.org/docs/current/queries-with.html) (CTE) to update the counter value during insert into your target table. 48 | 49 | 1. Create a "counter" table containing a single "last value" row: 50 | 51 | ```sql 52 | CREATE TABLE counter (last_value integer NOT NULL); 53 | INSERT INTO counter (last_value) VALUES (0); 54 | ``` 55 | 56 | 2. Create your desired table to use the gapless sequence: 57 | 58 | ```sql 59 | CREATE TABLE example (id integer NOT NULL, value text NOT NULL); 60 | CREATE UNIQUE INDEX events_pkey ON example(id); 61 | ``` 62 | 63 | Usage: 64 | 65 | ```sql 66 | WITH 67 | counter AS ( 68 | UPDATE counter SET last_value = last_value + 1 69 | RETURNING last_value 70 | ) 71 | INSERT INTO example (id, value) 72 | SELECT counter.last_value, 'example'; 73 | ``` 74 | 75 | --- 76 | 77 | With both approaches the `UPDATE` ... `RETURNING` query will _block any other update_ to the `counter` table, thus guaranteeing a gapless sequence. You can test this by attempting to run the query concurrently using `BEGIN;` but not committing. You'll notice that the second query is blocked until the first commits (or aborts). 78 | 79 | This also means that if the first query's transaction is aborted (using `ROLLBACK`) the second query can continue and will be assigned the next value, no gaps. Unlike using a traditional Postgres sequence which is not transactional. 80 | 81 | A caveat is that if you delete a row from the table you will then have gaps. This can be prevented with a rule to prevent deletion from the table: 82 | 83 | ```sql 84 | CREATE RULE no_delete_example AS ON DELETE TO example DO INSTEAD NOTHING; 85 | ``` 86 | -------------------------------------------------------------------------------- /postgres/install-timescaledb.md: -------------------------------------------------------------------------------- 1 | # Install TimescaleDB with Postgres.app 2 | 3 | Following the [install from source](https://docs.timescale.com/latest/getting-started/installation/macos/installation-source) instructions. 4 | 5 | 1. Install `cmake` and `openssl` via Homebrew: 6 | 7 | ```shell 8 | brew install cmake 9 | brew install openssl 10 | ``` 11 | 12 | 2. Clone Timescale GitHub repo: 13 | 14 | ```shell 15 | git clone https://github.com/timescale/timescaledb.git 16 | cd timescaledb 17 | git checkout 2.3.1 18 | ``` 19 | 20 | 3. Build Timescale extension: 21 | 22 | ```shell 23 | OPENSSL_ROOT_DIR=/usr/local/opt/openssl ./bootstrap -DREGRESS_CHECKS=OFF 24 | cd build && make 25 | ``` 26 | 27 | 4. Install Timescale extension: 28 | 29 | ```shell 30 | make install 31 | ``` 32 | 33 | 5. Locate Postgres config file: 34 | 35 | ```shell 36 | psql -d postgres -c "SHOW config_file;" 37 | ``` 38 | 39 | 6. Modify `postgresql.conf` to add the TimescaleDB library: 40 | 41 | ``` 42 | shared_preload_libraries = 'timescaledb' 43 | ``` 44 | 45 | 7. Restart Postgres. 46 | 47 | 8. Create TimescaleDB extension in Postgres: 48 | 49 | ```sql 50 | CREATE EXTENSION IF NOT EXISTS "timescaledb" CASCADE; 51 | ``` 52 | 53 | ## Check which Timescale version is installed 54 | 55 | ```SQL 56 | SELECT default_version, installed_version 57 | FROM pg_available_extensions 58 | WHERE name = 'timescaledb'; 59 | ``` 60 | 61 | ## Upgrade an existing Timescale database 62 | 63 | 1. Follow the steps 2, 3, and 4 in the "Install TimescaleDB with Postgres.app" section above, but checkout the updated Timescale version: 64 | 65 | ```bash 66 | cd timescaledb 67 | git checkout 2.3.1 68 | ``` 69 | 70 | ... then run steps 3 and 4 above. 71 | 72 | 2. Connect to the Timescale database using `psql -X`: 73 | 74 | ```bash 75 | psql -X -d 76 | ``` 77 | 78 | 3. Upgrade Timescale extension: 79 | 80 | ```SQL 81 | ALTER EXTENSION timescaledb UPDATE; 82 | ``` 83 | -------------------------------------------------------------------------------- /postgres/list-tables-in-schema.md: -------------------------------------------------------------------------------- 1 | # List tables in a Postgres schema 2 | 3 | ```SQL 4 | SELECT table_name 5 | FROM information_schema.tables 6 | WHERE table_schema = '' AND table_type = 'BASE TABLE' 7 | ORDER BY table_name; 8 | ``` 9 | -------------------------------------------------------------------------------- /postgres/recreate-postgres-schema.md: -------------------------------------------------------------------------------- 1 | # Recreate Postgres schema 2 | 3 | Dropping and creating a schema, such as the default `public` schema, is useful for deleting all tables, data, functions, views, etc. 4 | 5 | ```sql 6 | DROP SCHEMA public CASCADE; 7 | CREATE SCHEMA public; 8 | 9 | GRANT ALL ON SCHEMA public TO postgres; 10 | GRANT ALL ON SCHEMA public TO public; 11 | ``` 12 | 13 | - [How can I drop all the tables in a PostgreSQL database?](https://stackoverflow.com/questions/3327312/how-can-i-drop-all-the-tables-in-a-postgresql-database) 14 | -------------------------------------------------------------------------------- /shell-scripting/exit-codes.md: -------------------------------------------------------------------------------- 1 | # Exit codes 2 | 3 | > Exit codes are a number between 0 and 255, which is returned by any Unix command when it returns control to its parent process. 4 | > 5 | > - _Success_ is traditionally represented with exit `0`. 6 | > - _Failure_ is normally indicated with a non-zero exit-code. This value can indicate different reasons for failure. 7 | 8 | To display the last command's exit code: 9 | 10 | ```sh 11 | echo $? 12 | ``` 13 | --------------------------------------------------------------------------------