Skip to content

Commit

Permalink
index tips
Browse files Browse the repository at this point in the history
  • Loading branch information
mnishiguchi committed Jul 24, 2023
1 parent a5826fd commit be74a25
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 59 deletions.
12 changes: 6 additions & 6 deletions lib/nerves_motd.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ defmodule NervesMOTD do
{:logo, IO.ANSI.ansidata()}
| {:extra_rows, [row()]}
| {:show_tip, boolean()}
| {:extra_tips, [String.t()]}

@typedoc """
One row of information
Expand All @@ -53,7 +52,6 @@ defmodule NervesMOTD do
* `:extra_rows` - a list of custom rows or a callback for returning rows.
The callback can be a 0-arity function reference or MFArgs tuple.
* `:show_tip` - a boolean flag to show the tip of the day.
* `:extra_tips` - additional custom tips as a list of strings.
"""
@spec print([option()]) :: :ok
def print(opts \\ []) do
Expand Down Expand Up @@ -92,11 +90,13 @@ defmodule NervesMOTD do
@spec tips([option()]) :: IO.ANSI.ansidata()
defp tips(opts) do
if opts[:show_tip] do
"""
case Tips.random() do
{:ok, tip} ->
["\n=== Tip of the day ===\n", tip, "\n"]

=== Tip of the day ===
#{Tips.random(opts)}
"""
_error ->
[]
end
else
[]
end
Expand Down
54 changes: 37 additions & 17 deletions lib/nerves_motd/tips.ex
Original file line number Diff line number Diff line change
@@ -1,29 +1,49 @@
defmodule NervesMOTD.Tips do
@moduledoc false

@divider "\n\n"
@default_tips File.read!("config/default_tips.txt")
|> String.split(@divider, trim: true)
|> Enum.reject(&(&1 == ""))
# Returns a tuple list of locaton and byte size of each tip entry in the provided file.
index_file = fn filename, divider when is_binary(filename) and is_binary(divider) ->
divider_byte_size = byte_size(divider)

@type tip :: String.t()
{indices, _} =
File.read!(filename)
|> String.trim()
|> String.split(divider, trim: true)
|> Enum.reduce({[], 0}, fn tip, {indices, current_location} ->
how_many_bytes = byte_size(tip)
next_location = current_location + how_many_bytes + divider_byte_size
new_index = {current_location, how_many_bytes}

@doc """
List all tips
"""
@spec all(keyword) :: [tip]
def all(opts \\ []) do
extra_tips = Keyword.get(opts, :extra_tips, [])
@default_tips ++ extra_tips
{[new_index | indices], next_location}
end)

indices
end

@default_tips_file "priv/default_tips.txt"
@divider "%%%"
@indices Application.app_dir(:nerves_motd, [@default_tips_file]) |> index_file.(@divider)

def indices, do: @indices

@doc """
Pick one tip randomly
"""
@spec random(keyword) :: tip
def random(opts \\ []) do
all(opts)
|> Enum.reject(&(&1 == ""))
|> Enum.random()
@spec random() :: {:ok, String.t()} | {:error, any}
def random() do
{location, how_many_bytes} = Enum.random(@indices)
read_file_at_location(tips_file(), location, how_many_bytes)
end

defp tips_file do
# this is different from the one at compile time
Application.app_dir(:nerves_motd, [@default_tips_file])
end

defp read_file_at_location(filename, location, how_many_bytes) do
with {:ok, io_device} <- File.open(filename, [:read]),
{:ok, data} <- :file.pread(io_device, location, how_many_bytes),
:ok <- File.close(io_device),
do: {:ok, String.trim(data)}
end
end
40 changes: 20 additions & 20 deletions config/default_tips.txt → priv/default_tips.txt
Original file line number Diff line number Diff line change
@@ -1,75 +1,75 @@
Compiling systems on linux or via `mix nerves.system.shell`? Don't forgot about
`make help` and other tips from the Buildroot manual (thanks @ericr3r)
https://buildroot.org/downloads/manual/manual.html#make-tips

%%%
Configuring the Elixir Logger to show SASL reports can help debug unexpected
GenServer restarts.
https://hexdocs.pm/logger/Logger.html#module-erlang-otp-integration

%%%
Erlinit is a small program that starts the Erlang VM on boot. It has many
options - especially for debugging startup issues.
https://hexdocs.pm/nerves/advanced-configuration.html#overriding-erlinit-config-from-mix-config

%%%
Get all of the default packages for starting a new Nerves project by depending
on `:nerves_pack` https://github.com/nerves-project/nerves_pack

%%%
Get some insights to Nerves internals with this high level overview of the
Nerves architecture and choice to use the BEAM VM https://youtu.be/VzOaSvTcvU4

%%%
Identify your Nerves devices using a unique ID that's already programmed into
the hardware using the boardid command. Details at
https://github.com/nerves-project/boardid/ and the boardid.config file.

%%%
Make small code changes to your running application by copy/pasting Elixir
source files at the IEx prompt.

%%%
Need more than Elixir logs? Run `dmesg` to see log messages from device drivers
and the Linux kernel. Nerves also routes kernel log messages to the Elixir
logger.

%%%
Nerves automatically reboots devices when the Erlang VM exits. This can make
some debugging harder, but you can easily disable it to let those sessions hang
for debugging

%%%
Nerves enables hardware watchdogs and connects them to Erlang's heart feature
to detect and recover from the Erlang VM hanging. See
https://embedded-elixir.com/post/2018-12-10-heart/ to learn more about this.

%%%
Nerves maintains a set of examples for common use-cases with devices. Things
like running a phoenix app, using SQLite, controlling GPIO, using Zig lang, and
more! https://github.com/nerves-project/nerves_examples

%%%
Nerves stores all BEAM files, the Erlang runtime and various support libraries
and apps in a compressed and read-only SquashFS filesystem. The writable
application partition is mounted at `/data`

%%%
Nerves stores all BEAM files, the Erlang runtime and various support
libraries/apps in a compressed and read-only SquashFS filesystem. Need to write
to disk? Use the application partition mounted R/W at `/data`

%%%
Run `Nerves.Runtime.revert` to go back to the previously loaded firmware.

%%%
See if someone has already implemented support for a sensor or other hardware
device that you have by checking https://elixir-circuits.github.io/.

%%%
Sometimes when compiling Nerves systems, order matters! Use `make
show-build-order` while in `mix nerves.system.shell` to see the compilation
order and make sure all your 🦆🦆 are in a row (thanks @ericr3r!)

%%%
Sometimes, you just need to see whats on your Nerves device's disk. Luckily,
there are a few ways to do that. You can even use VSCode!
https://embedded-elixir.com/post/2021-05-08-nerves-file-peeking/#sshfs

%%%
Use `RingLogger.next` to dump the current log buffer to screen. Use
`log_attach` from `Toolshed` to attach the current session to the Elixir logger
for live logs. https://hexdocs.pm/toolshed/Toolshed.Log.html#log_attach/1

%%%
Use `mix firmware.unpack` to decompress a local copy of your firmware on host
and inspect the files within before installing on device

%%%
Use the `RamoopsLogger` backend to store log messages in DRAM that can survive
unexpected reboots. https://github.com/smartrent/ramoops_logger

%%%
Want more traditional shell tools in your Nerves IEx? Use Toolshed! Its
included by default and has may familiar functions, such as `cat`, `ls`, or
even `weather` 🌤️ https://hexdocs.pm/toolshed
18 changes: 2 additions & 16 deletions test/nerves_motd/tips_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,8 @@ defmodule NervesMOTD.TipsTest do
alias NervesMOTD.Tips
doctest NervesMOTD.Tips

@extra_tip """
There are only two hard things in Computer Science: cache invalidation and naming things.
"""

test "all/1 returns a list of default tips" do
assert [tip | _] = Tips.all()
assert is_binary(tip)
end

test "all/1 with extra tips returns tips that contain extra tips" do
tips = Tips.all(extra_tips: [@extra_tip])
assert @extra_tip in tips
end

test "random/1 returns a string" do
tip = Tips.random(extra_tips: [@extra_tip])
test "random/0 returns a string" do
assert {:ok, tip} = Tips.random()
assert is_binary(tip)
end
end

0 comments on commit be74a25

Please sign in to comment.