Midwire

Ruby, Elixir, Macintosh and Other Technical Maunder.

Elixir - Time Ago in Words

After reading the excellent book “Programming Elixir”, by Dave Thomas, and “Programming Phoenix”, by Chris McCord, Bruce Tate and José Valim, I decided to dive in.

As of this post, I am currently writing an Elixir app using the Phoenix framework. It is called Tunedrop and I am working on it for two reasons:

  1. To learn Elixir and Phoenix
  2. To share some of the music that I grew up on with my kids

You can see the code here.

It is definitely a work in progress and may look pretty raw at times. However, I wanted to share how easy it is to port code from Ruby to Elixir for your benefit, happiness and profit.

As I play music in iTunes, Tunedrop dynamically posts the song’s artist, name and year released, along with the time it was played. Originally, the time played was absolute and looked like this: 4/12 15:32. I wanted to look something like the Rails ActionView::Helpers.distance_of_time_in_words:

listings

Here’s how I did it:

Preparation

I’m using the timex Elixir library for DateTime manipulations. I followed the directions there to install it into my Phoenix project.

TimeHelpers

The Tests

In tunedrop/test/views/time_helpers_test.exs <- click the link to view the entire test module.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
defmodule Tunedrop.TimeHelpersTest do
  use Tunedrop.ViewHelperCase
  use Timex

  test "returns time in words for now" do
    time = DateTime.now
    words = Tunedrop.TimeHelpers.distance_of_time_in_words(time)
    assert words =~ ~r/Less than 1 minute/
  end

  test "returns time in words for 30 minutes" do
    time = DateTime.shift(DateTime.now, minutes: 30)
    words = Tunedrop.TimeHelpers.distance_of_time_in_words(time)
    assert words =~ ~r/30 minutes/
  end

  ...
end

The Module

In tunedrop/web/views/time_helpers.ex <- click the link to view the entire module.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
defmodule Tunedrop.TimeHelpers do
  use Timex
  alias Timex.DateTime

  @moduledoc """
  Helpers for DateTime display
  """

  @doc """
  Generates approximate distance of time between two times in words.
  """
  def distance_of_time_in_words(from_time, to_time \\ DateTime.now) do
    case from_time > to_time do
      true ->
        from = to_time
        to = from_time
      false ->
        from = from_time
        to = to_time
    end
    distance_in_seconds = abs(DateTime.to_seconds(to_time) - DateTime.to_seconds(from_time))
    distance_in_minutes = abs(round(distance_in_seconds / 60.0))

    words = case distance_in_minutes do
      n when n in 0..1 ->
        "Less than 1 minute"
      n when n in 2..45 ->
        "#{n} minutes"
      n when n in 45..90 ->
        "about 1 hour"
      n when n in 90..1440 ->
        "about #{round(n / 60.0)} hours"
      n when n in 1440..2520 ->
        "about 1 day"
      n when n in 2520..43200 ->
        "about #{round(n / 1440.0)} days"
      n when n in 43200..86400 ->
        "about #{round(n / 43200.0)} months"
      n when n in 86400..525600 ->
        "#{round(n / 43200.0)} months"
      _ -> "a long time"
    end
    words
  end
end

I’m sure this code could be better, but what I’m impressed with is how easy it is to port code from Ruby into the pattern-matching paradigm of Elixir, and how similar it looks to the original Ruby code.

Comments