Draw the moon the way it looks

36c4afc · Gabriel Morell · 2026-09-02 20:45

10 files +79 -10
Message
{commit_body(@commit)}

Files changed

modified apps/room_sanctum/lib/room_sanctum_web/live/live_preview.ex
+32 −10
@@ -27,19 +27,41 @@ use PhoenixHTMLHelpers
27 27 end
28 28 end
29 29
30 − defp moon_icon(phase) do
31 − case phase do
32 − :new_moon -> "🌕"
33 − :waxing_crescent -> "🌖"
34 − :first_quarter -> "🌗"
35 − :waxing_gibbous -> "🌘"
36 − :full_moon -> "🌑"
37 − :waning_gibbous -> "🌒"
38 − :third_quarter -> "🌓"
39 − :waning_crescent -> "🌔"
30 + @moon_phases [
31 + new_moon: "🌑",
32 + waxing_crescent: "🌒",
33 + first_quarter: "🌓",
34 + waxing_gibbous: "🌔",
35 + full_moon: "🌕",
36 + waning_gibbous: "🌖",
37 + third_quarter: "🌗",
38 + waning_crescent: "🌘"
39 + ]
40 +
41 + # The moon as a character.
42 + #
43 + # Northern-hemisphere shapes, waxing from the right: new is dark, full is
44 + # lit, and the quarters are lit on the side the light is coming from. The
45 + # mapping this replaces was rotated by four -- a new moon drew a full disc
46 + # and a full moon drew a dark one.
47 + #
48 + # Solarex hands back an atom; anything that has been through JSON is a
49 + # string, so both are read. A phase nobody recognises draws nothing rather
50 + # than raising: this is called from a board somebody is looking at, and a
51 + # missing glyph is a better outcome than a blank card.
52 + defp moon_icon(phase) when is_atom(phase) do
53 + Keyword.get(@moon_phases, phase, "")
54 + end
55 +
56 + defp moon_icon(phase) when is_binary(phase) do
57 + case Enum.find(@moon_phases, fn {name, _} -> Atom.to_string(name) == phase end) do
58 + {_name, glyph} -> glyph
59 + nil -> ""
40 60 end
41 61 end
42 62
63 + defp moon_icon(_phase), do: ""
64 +
43 65 def tsl(%Time{} = t), do: tsl(t |> Time.to_string)
44 66 def tsl(st), do: st |> String.slice(0,5)
45 67
added apps/room_sanctum/test/room_sanctum_web/live/moon_phase_test.exs
+47 −0
@@ -0,0 +1,47 @@
1 +defmodule RoomSanctumWeb.MoonPhaseTest do
2 + @moduledoc """
3 + The moon drawn as the moon actually looks.
4 +
5 + This existed as an off-by-four: the table ran from a full disc at new moon
6 + round to a dark one at full. Nobody notices a wrong moon for a fortnight, so
7 + it is worth a test rather than an eye.
8 + """
9 + use ExUnit.Case, async: true
10 +
11 + import Phoenix.LiveViewTest
12 +
13 + alias RoomSanctumWeb.LivePreview
14 +
15 + defp ephem(phase) do
16 + render_component(&LivePreview.p_ephem/1, entries: %{data: [%{name: "CPL", phase: phase}]})
17 + end
18 +
19 + test "a new moon is dark and a full moon is lit" do
20 + assert ephem(:new_moon) =~ "🌑"
21 + assert ephem(:full_moon) =~ "🌕"
22 + end
23 +
24 + test "waxing runs from dark towards full" do
25 + assert ephem(:waxing_crescent) =~ "🌒"
26 + assert ephem(:first_quarter) =~ "🌓"
27 + assert ephem(:waxing_gibbous) =~ "🌔"
28 + end
29 +
30 + test "waning runs from full back towards dark" do
31 + assert ephem(:waning_gibbous) =~ "🌖"
32 + assert ephem(:third_quarter) =~ "🌗"
33 + assert ephem(:waning_crescent) =~ "🌘"
34 + end
35 +
36 + test "a phase that has been through JSON reads the same as an atom" do
37 + # Anything arriving from a publisher rather than from Solarex is a string.
38 + assert ephem("waning_gibbous") =~ "🌖"
39 + end
40 +
41 + test "a phase nobody recognises draws nothing rather than raising" do
42 + # A board somebody is looking at should lose a glyph, not the card.
43 + html = ephem(:gibbous_but_sideways)
44 + assert html =~ "CPL"
45 + refute html =~ "🌕"
46 + end
47 +end

Parents: 23c5836