tagged 'elixir'

How to use SASS/SCSS with Webpack in Phoenix 1.4

Phoenix 1.4 is on it’s way and one of the big changes is that webpack is replacing brunch. If you are a SASS fan then this is how to update the default Webpack configuration to use SASS (SCSS flavour).

Install NPM packages

The first step is to install the node-sass and sass-loader packages from NPM.

Using Yarn

$ yarn add node-sass sass-loader --dev

Using NPM

$ npm install node-sass sass-loader --save-dev

Update webpack.config.js

Update the assets/webpack.config.js file with a change to chain the sass-loader plugin after the css-loader.

diff --git a/assets/webpack.config.js b/assets/webpack.config.js
index 5225785..4c14948 100644
--- a/assets/webpack.config.js
+++ b/assets/webpack.config.js
@@ -26,8 +26,18 @@ module.exports = (env, options) => ({
         }
       },
       {
-      test: /\.css$/,
-      use: [MiniCssExtractPlugin.loader, 'css-loader']
+      test: /\.scss$/,
+      use: [
+        MiniCssExtractPlugin.loader,
+        {
+          loader: 'css-loader',
+          options: {}
+        },
+        {
+          loader: 'sass-loader',
+          options: {}
+        }
+      ]
       }
     ]
   },

Update app.css

Rename your assets/css/app.css to assets/css/app.scss.

$ mv assets/css/app.css assets/css/app.scss

Update app.js

Because the CSS files are loaded by Webpack through the javascript file, you need to update the css import path as well.

diff --git a/assets/js/app.js b/assets/js/app.js
index 8ee7177..0aa55a0 100644
--- a/assets/js/app.js
+++ b/assets/js/app.js
@@ -1,7 +1,7 @@
 // We need to import the CSS so that webpack will load it.
 // The MiniCssExtractPlugin is used to separate it out into
 // its own CSS file.
-import css from "../css/app.css"
+import css from "../css/app.scss"

 // webpack automatically bundles all modules in your
 // entry points. Those entry points can be configured

Build assets

Finally test your assets build.

$ node node_modules/webpack/bin/webpack.js --mode development
Jun 18, 2018 elixir, sass, scss, css, phoenix

Use Phoenix 1.4 Now

With Phoenix 1.4 announced at ElixirConf EU (https://youtu.be/MTT1Jl4Fs-E) I was keen to try it out. I was specifically interested in seeing the new Webpack integration. Getting started with Phoenix 1.4 is really quite easy.

Uninstall the existing Phoenix 1.3 archive

From the README,

Remove any previously installed phx_new archives so that Mix will pick up the local source code. This can be done with mix archive.uninstall phx_new or by simply deleting the file, which is usually in ~/.mix/archives/.

Clone the Phoenix master repo

$ git clone https://github.com/phoenixframework/phoenix

Build and install the Phoenix archive

$ cd phoenix/installer
$ MIX_ENV=prod mix do archive.build, archive.install

Generate your new Phoenix 1.4 app

Run mix phx.new my_app

Your mix.exs deps will now look like this:

defp deps do
  [
    {:phoenix, github: "phoenixframework/phoenix", override: true},
    #…
  ]
end

When Phoenix 1.4 is released, you can just update this line to:

defp deps do
  [
    {:phoenix, "~> 1.4.0"},
    #…
  ]
end

Revert back to the Phoenix 1.3 installer

Reverting to the 1.3 installer is as easy as uninstalling and reinstalling the Phoenix archive.

Related

mix archive.uninstall phx_new-1.3.0
mix archive.install https://github.com/phoenixframework/archives/raw/master/phx_new.ez
Jun 17, 2018 elixir, phoenix

Repo.count in Ecto

I have often wanted to just do the following but Ecto’s Repo module doesn’t have a count method.

iex> MyApp.Repo.count(MyApp.Account)
42

It is not too difficult to create a count function that will allow you to count the results of any query.

defmodule MyApp.DBUtils do
  import Ecto.Query, only: [from: 2]

  @doc "Generate a select count(id) on any query"
  def count(query),
    do: from t in clean_query_for_count(query), select: count(t.id)

  # Remove the select field from the query if it exists
  defp clean_query_for_count(query),
    do: Ecto.Query.exclude(query, :select)
end

This will provide a shortcut for counting any query

MyApp.DBUtils.count(MyApp.Account) |> Repo.one!

Now, to enable Repo.count we can modify the repo module usually found in lib/my_app/repo.ex

defmodule MyApp.Repo do
  use Ecto.Repo, otp_app: :my_app

  def count(query),
    do: MyApp.DBUtils.count(query) |> __MODULE__.one!
end

That’s it. This will enable a count on any query including complicated queries and those that have a select expression set.

Sep 20, 2016 elixir, ecto, tip, tech

Benchmarking in Elixir

Appending to a list in Elixir ([1] ++ [2]) is slower than prepending and reversing [ 2 | [1] ] |> Enum.reverse but how bad is it?

Start by creating a new project, mix new benchmarking and add benchfella as a dependency in your mix.exs file

defp deps do
  [{:benchfella, "~> 0.3.2"}]
end

and run mix deps.get

Benchfella benchmarks work similarly to tests. Create a directory named bench and then create a file ending in _bench.exs. Benchfella will find these files and run them.

Create a file bench/list_append_bench.exs We will write our functions in the bench file but you can reference functions in another module to benchmark your project code.

This benchmark will test three different ways to build a list, (1) append each element to the list using ++, (2) build up the list using a recursive tail where the element is added to the head but the tail is built up recursively, and (3) prepending the element to a list accumulator and then reversing the list at the end.

defmodule ListAppendBench do
  use Benchfella

  @length 1_000

  # First bench mark
  bench "list1 ++ list2" do
    build_list_append(1, @length)
  end

  # Second bench mark
  bench "[head | recurse ]" do
    build_list_recursive_tail(1, @length)
  end

  # Third bench mark
  bench "[head | tail] + Enum.reverse" do
    build_list_prepend(1, @length)
  end

  @doc """
  Build a list of numbers from `num` to `total` by appending each item
  to the end of the list
  """
  def build_list_append(num, total, acc \\ [])
  def build_list_append(total, total, acc), do: acc
  def build_list_append(num, total, acc) do
    acc = acc ++ [num]
    next_num = num + 1
    build_list_append(next_num, total, acc)
  end

  @doc """
  Build a list of numbers from `num` to `total` by building
  the list with a recursive tail instead of using an accumulator
  """
  def build_list_recursive_tail(total, total), do: []
  def build_list_recursive_tail(num, total) do
    [ num | build_list_recursive_tail(num + 1, total) ]
  end

  @doc """
  Build a list of numbers from `num` to `total` by prepending each item
  and reversing the list at the end
  """
  def build_list_prepend(num, total, acc \\ [])
  def build_list_prepend(total, total, acc), do: Enum.reverse(acc)
  def build_list_prepend(num, total, acc) do
    acc = [num | acc]
    next_num = num + 1
    build_list_prepend(next_num, total, acc)
  end
end

Run the benchmark with mix bench and you see the results,

Settings:
  duration:      1.0 s

## ListAppendBench
[10:15:32] 1/3: list1 ++ list2
[10:15:34] 2/3: [head | tail] + Enum.reverse
[10:15:37] 3/3: [head | recurse ]

Finished in 6.66 seconds

## ListAppendBench
[head | tail] + Enum.reverse      100000   20.87 µs/op
[head | recurse ]                 100000   21.25 µs/op
list1 ++ list2                       500   3228.16 µs/op

The results: prepending to a list and reversing it is 200 times faster than appending and only fractionally faster than building the tail recursively.

For more complex benchmarks, Benchfella has various hooks for test setup and teardown. It also has ability to compare benchmark runs with mix bench.cmp and graph the results with mix bench.graph.

Mar 29, 2016 tech, elixir