<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Masatoshi Nishiguchi</title>
    <description>The latest articles on DEV Community by Masatoshi Nishiguchi (@mnishiguchi).</description>
    <link>https://dev.to/mnishiguchi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F48016%2F1d36b5c4-860d-498b-acec-e05823298511.jpg</url>
      <title>DEV Community: Masatoshi Nishiguchi</title>
      <link>https://dev.to/mnishiguchi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mnishiguchi"/>
    <language>en</language>
    <item>
      <title>Bringing Open JTalk to Elixir/Nerves: Make Your Pi Speak Japanese 🇯🇵</title>
      <dc:creator>Masatoshi Nishiguchi</dc:creator>
      <pubDate>Mon, 15 Sep 2025 10:52:11 +0000</pubDate>
      <link>https://dev.to/mnishiguchi/bringing-open-jtalk-to-elixirnerves-make-your-pi-speak-japanese-ml3</link>
      <guid>https://dev.to/mnishiguchi/bringing-open-jtalk-to-elixirnerves-make-your-pi-speak-japanese-ml3</guid>
      <description>&lt;p&gt;&lt;a href="https://qiita.com/mnishiguchi/items/e7c96c6caae15f16fbbf" rel="noopener noreferrer"&gt;日本語版&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;One day my friend &lt;a href="https://github.com/kurokouji" rel="noopener noreferrer"&gt;kurokouji&lt;/a&gt; asked, innocently opening a rabbit hole:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can my Nerves-powered Raspberry Pi talk in Japanese using Open JTalk?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Challenge accepted.&lt;/p&gt;

&lt;p&gt;I figured the answer was yes—I just didn’t know how yet. As &lt;a href="https://www.google.com/search?q=antonio+inoki" rel="noopener noreferrer"&gt;Antonio Inoki&lt;/a&gt; once said:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;元氣が有れば何でもできる (If you have spirit, you can do anything)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So there was no reason not to try. Worst case, I’d learn something. Best case, the Pi speaks Japanese. Thanks to modern AI tooling, we can now explore rabbit holes without getting totally lost.&lt;/p&gt;

&lt;p&gt;The result is &lt;a href="https://hex.pm/packages/open_jtalk_elixir" rel="noopener noreferrer"&gt;open_jtalk_elixir&lt;/a&gt;—a portable Elixir wrapper for &lt;a href="http://open-jtalk.sourceforge.net/" rel="noopener noreferrer"&gt;Open JTalk&lt;/a&gt;, Japan’s classic text-to-speech engine.&lt;/p&gt;

&lt;p&gt;This library:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Builds a native &lt;a href="https://open-jtalk.sourceforge.net/" rel="noopener noreferrer"&gt;Open JTalk CLI&lt;/a&gt; during compilation&lt;/li&gt;
&lt;li&gt;Bundles the required dictionary + voice assets by default&lt;/li&gt;
&lt;li&gt;Exposes a clean Elixir API like this:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/user-attachments/assets/69d2579c-2d6f-47e5-bcfc-16b955ee8df0" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff9bdkpv5fbp7mi2c7vlp.png" alt="Watch the demo" width="733" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even if the destination was clear, the path was full of lessons. This post documents the journey—the dead ends, the design trade-offs, and how the pieces came together.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is Open JTalk?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://open-jtalk.sourceforge.net/" rel="noopener noreferrer"&gt;Open JTalk&lt;/a&gt; is a well-established Japanese text-to-speech (TTS) engine widely used in research, embedded systems, and hobby projects.&lt;/p&gt;

&lt;p&gt;It’s built from these components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://taku910.github.io/mecab/" rel="noopener noreferrer"&gt;MeCab&lt;/a&gt; 0.996
— A morphological analyzer that parses Japanese text into words with readings and grammatical features.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hts-engine.sourceforge.net/" rel="noopener noreferrer"&gt;HTS Engine API&lt;/a&gt; 1.10
— A statistical parametric speech synthesis engine that generates the actual waveform from features.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://sourceforge.net/projects/open-jtalk/files/Dictionary/" rel="noopener noreferrer"&gt;Open JTalk Dictionary&lt;/a&gt; 1.11
— The UTF-8 dictionary package (&lt;code&gt;open_jtalk_dic_utf_8-1.11&lt;/code&gt;) distributed with Open JTalk. It provides the lexical data required by &lt;a href="https://taku910.github.io/mecab/" rel="noopener noreferrer"&gt;MeCab&lt;/a&gt; for morphological analysis and is maintained as part of the official Open JTalk project.&lt;/li&gt;
&lt;li&gt;HTS Voice “Mei” (&lt;a href="https://sourceforge.net/projects/mmdagent/files/MMDAgent_Example/" rel="noopener noreferrer"&gt;MMDAgent Example 1.8&lt;/a&gt;)
— A widely used Japanese voice model, clear and natural sounding.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://open-jtalk.sourceforge.net/" rel="noopener noreferrer"&gt;Open JTalk CLI&lt;/a&gt; 1.11
— A C-based command-line tool that ties all the components together and outputs audio.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Despite its age, Open JTalk remains relevant—especially when you want offline, no-internet Japanese TTS in embedded environments like Raspberry Pi or Nerves.&lt;/p&gt;




&lt;h2&gt;
  
  
  Challenges in embedded environments
&lt;/h2&gt;

&lt;p&gt;Making &lt;a href="https://open-jtalk.sourceforge.net/" rel="noopener noreferrer"&gt;Open JTalk&lt;/a&gt; work inside a Nerves-powered Elixir firmware image isn’t trivial.&lt;/p&gt;

&lt;p&gt;Here’s why:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Native Build Complexity
&lt;/h3&gt;

&lt;p&gt;Open JTalk uses &lt;a href="https://www.google.com/search?q=autotools" rel="noopener noreferrer"&gt;autotools&lt;/a&gt; to compile its C stack: &lt;code&gt;MeCab&lt;/code&gt;, &lt;code&gt;HTS Engine&lt;/code&gt;, and &lt;code&gt;Open JTalk&lt;/code&gt; itself. These aren't built for cross-compilation out of the box.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Required Assets Are Large
&lt;/h3&gt;

&lt;p&gt;Open JTalk doesn’t work without its dictionary and voice model files. You need to ship:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dictionary: ~100 MB&lt;/li&gt;
&lt;li&gt;Mei voice: ~2 MB&lt;/li&gt;
&lt;li&gt;CLI binary: ~1 MB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you skip bundling them, your app might compile just fine... but silently fail to synthesize speech unless the user manually downloads those assets post-install.&lt;/p&gt;

&lt;p&gt;That’s not the kind of "developer experience" we want.&lt;/p&gt;




&lt;h2&gt;
  
  
  The approach that worked: Vendor + Bundle + Shell Out
&lt;/h2&gt;

&lt;p&gt;After exploring various approaches, I found this simple one is effective:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vendor and compile all C dependencies at Elixir build time (&lt;code&gt;mix compile&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Bundle voice + dictionary files into the &lt;code&gt;priv/&lt;/code&gt; directory&lt;/li&gt;
&lt;li&gt;Use &lt;a href="https://hexdocs.pm/elixir/main/System.html#cmd/3" rel="noopener noreferrer"&gt;System.cmd/3&lt;/a&gt; to shell out from Elixir to the native CLI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That means you get a fully working setup out-of-the-box, even on embedded targets like Raspberry Pi running Nerves.&lt;/p&gt;

&lt;p&gt;If you're concerned about firmware size—such as for minimal Nerves deployments—you can disable bundled assets like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;OPENJTALK_BUNDLE_ASSETS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;OPENJTALK_DIC_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/data/jdic &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;OPENJTALK_VOICE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/data/mei.htsvoice &lt;span class="se"&gt;\&lt;/span&gt;
mix compile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can even skip the build entirely (e.g. if prebuilt in CI):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;OPENJTALK_BIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./bin/open_jtalk mix compile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All we’re really doing is letting Elixir orchestrate the CLI, while the heavy lifting stays in native code.It might not be fancy, but it’s reliable, portable, and easy to debug.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ideas That Didn’t Work
&lt;/h2&gt;

&lt;p&gt;Before settling on the current approach, I tried several others. Here's why they failed:&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom Nerves System
&lt;/h3&gt;

&lt;p&gt;The first idea was to build a custom Nerves system image with Open JTalk baked in. It sounds powerful—everything precompiled and baked into the firmware.&lt;/p&gt;

&lt;p&gt;But in practice, it's heavy for iteration and slow for development. Any time you want to tweak or test something, you're rebuilding the world.&lt;/p&gt;

&lt;p&gt;On top of that, even with Open JTalk included, you’d still need to wrap the CLI in Elixir and handle asset loading. &lt;/p&gt;

&lt;p&gt;I ruled this out early—it wasn’t worth the complexity for what I was trying to build.&lt;/p&gt;

&lt;h3&gt;
  
  
  Elixir Native Implemented Functions (NIFs)
&lt;/h3&gt;

&lt;p&gt;NIFs let you call C functions directly from Elixir—fast, no shelling out.&lt;/p&gt;

&lt;p&gt;But NIFs come with risk: if anything goes wrong, they can crash the entire BEAM VM. Portability across different CPU/OS combinations is also tricky.&lt;/p&gt;

&lt;p&gt;So I skipped this route too. Safer for me—and for your runtime.&lt;/p&gt;

&lt;h3&gt;
  
  
  Elixir Port
&lt;/h3&gt;

&lt;p&gt;I’ve used Ports before—for example, in my &lt;a href="https://github.com/elixir-sensors/sgp40" rel="noopener noreferrer"&gt;sgp40 sensor library&lt;/a&gt;. It works great when input/output is simple.&lt;/p&gt;

&lt;p&gt;Open JTalk isn’t that simple. Encoding issues, temp files, complex args... Port was more pain than gain.&lt;/p&gt;




&lt;h2&gt;
  
  
  Build System Details
&lt;/h2&gt;

&lt;p&gt;The native stack is built via a Makefile + shell scripts. The flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fetch Sources&lt;/strong&gt;
Downloads and unpacks:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mecab&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mecab-ipadic-utf8&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hts_engine_API&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;open_jtalk&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Configure &amp;amp; Patch&lt;/strong&gt;&lt;br&gt;
Applies platform-specific patches (e.g., macOS fixes, updated &lt;code&gt;config.guess&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compile C Stack&lt;/strong&gt;&lt;br&gt;
Each lib is compiled using autotools and linked statically when possible.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Bundle CLI + Assets&lt;/strong&gt;&lt;br&gt;
Places everything into &lt;code&gt;priv/&lt;/code&gt;, ready for Elixir to use.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Cross-Platform Portability
&lt;/h2&gt;

&lt;p&gt;Getting it to run on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🐧 Linux (x86_64 &amp;amp; ARM)&lt;/li&gt;
&lt;li&gt;🍎 macOS (Apple Silicon)&lt;/li&gt;
&lt;li&gt;🍓 Raspberry Pi via Nerves&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...was an adventure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Autotools Pain
&lt;/h3&gt;

&lt;p&gt;Autotools can’t detect modern platforms without help:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;configure: error: cannot guess build &lt;span class="nb"&gt;type&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So I inject fresh &lt;a href="https://git.savannah.gnu.org/cgit/config.git" rel="noopener noreferrer"&gt;&lt;code&gt;config.sub&lt;/code&gt;&lt;/a&gt; and &lt;code&gt;config.guess&lt;/code&gt; before each build.&lt;/p&gt;

&lt;h3&gt;
  
  
  macOS Quirks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;BSD &lt;code&gt;install&lt;/code&gt; doesn’t support &lt;code&gt;-D&lt;/code&gt; → shim script added.&lt;/li&gt;
&lt;li&gt;No static linking → disabled on Darwin.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Nerves Cross-Compiling
&lt;/h3&gt;

&lt;p&gt;Nerves handles toolchains if you respect &lt;code&gt;MIX_TARGET&lt;/code&gt; and pass the right env:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MIX_TARGET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;rpi4
mix deps.get
mix compile
mix firmware
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Worked like a charm!&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Use in Elixir
&lt;/h2&gt;

&lt;p&gt;After &lt;code&gt;mix compile&lt;/code&gt;, usage is dead simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Speak out loud using system audio&lt;/span&gt;
&lt;span class="no"&gt;OpenJTalk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"元氣が有れば、何でもできる"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Synthesize to WAV&lt;/span&gt;
&lt;span class="no"&gt;OpenJTalk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_wav&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"こんにちは"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;out:&lt;/span&gt; &lt;span class="s2"&gt;"/tmp/test.wav"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Get WAV as binary&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;binary&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;OpenJTalk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_binary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"おはようございます"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Options available
&lt;/h3&gt;

&lt;p&gt;All functions accept the same set of options, which map to Open JTalk’s CLI flags but with friendlier names:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;:pitch_shift&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Semitone shift&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;:rate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Speaking speed multiplier&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1.0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;:timbre&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Voice quality adjustment&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0.0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;:gain&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Output gain (in dB)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;:voice&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Path to &lt;code&gt;.htsvoice&lt;/code&gt; file&lt;/td&gt;
&lt;td&gt;&lt;em&gt;(bundled)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;:dictionary&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Path to dictionary folder&lt;/td&gt;
&lt;td&gt;&lt;em&gt;(bundled)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;:timeout&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Max runtime in milliseconds&lt;/td&gt;
&lt;td&gt;&lt;code&gt;20000&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Asset Resolution
&lt;/h3&gt;

&lt;p&gt;The library automatically finds the required pieces (binary, dictionary, voice) in this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Environment variables&lt;/strong&gt; (&lt;code&gt;OPENJTALK_CLI&lt;/code&gt;, &lt;code&gt;OPENJTALK_DIC_DIR&lt;/code&gt;, &lt;code&gt;OPENJTALK_VOICE&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bundled assets&lt;/strong&gt;: files placed in &lt;code&gt;priv/&lt;/code&gt; at compile time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System locations&lt;/strong&gt;: &lt;code&gt;/usr/share&lt;/code&gt;, &lt;code&gt;/usr/local/share&lt;/code&gt;, Homebrew, etc.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can inspect asset resolution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="no"&gt;OpenJTalk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you ever swap files or tweak environment variables at runtime, you can reset the cache with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="no"&gt;OpenJTalk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Assets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reset_cache&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Bundle assets by default — Users expect things to “just work.” Opt-out is fine, but opt-in creates surprises and frustration.&lt;/li&gt;
&lt;li&gt;Respect Nerves conventions — Cross-compilation works smoothly if you let Nerves set the toolchain. Fighting it only adds pain.&lt;/li&gt;
&lt;li&gt;Autotools lacks modern platform support — Legacy &lt;code&gt;config.guess&lt;/code&gt; and &lt;code&gt;config.sub&lt;/code&gt; frequently fail; replacing them is essential for portability.&lt;/li&gt;
&lt;li&gt;macOS quirks matter — Even if you don’t use macOS, someone else will. Handle BSD tool differences and static linking limits early.&lt;/li&gt;
&lt;li&gt;Elixir + shelling out is a solid choice — For complex native tools, it’s simpler and safer than managing Ports..&lt;/li&gt;
&lt;li&gt;Keep Makefiles boring — Avoid turning Makefiles into a programming language. Offload logic to scripts for clarity and maintainability.&lt;/li&gt;
&lt;li&gt;Test on ARM early — x86 isn’t the default world anymore. Raspberry Pi, Apple Silicon, and cloud ARM runners should be first-class citizens.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;I started with a goal: get my Raspberry Pi to talk Japanese using Elixir.&lt;/p&gt;

&lt;p&gt;It turned into a full-blown cross-platform build system for a decades-old C library—with clean UX and out-of-the-box Nerves support.&lt;/p&gt;

&lt;p&gt;But the end result?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/user-attachments/assets/69d2579c-2d6f-47e5-bcfc-16b955ee8df0" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff9bdkpv5fbp7mi2c7vlp.png" alt="Watch the demo" width="733" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s all we wanted.&lt;/p&gt;

&lt;p&gt;Give &lt;a href="https://hex.pm/packages/open_jtalk_elixir" rel="noopener noreferrer"&gt;open_jtalk_elixir&lt;/a&gt; a try, and let your Pi speak too.&lt;/p&gt;

</description>
      <category>openjtalk</category>
      <category>mecab</category>
      <category>elixir</category>
      <category>iot</category>
    </item>
    <item>
      <title>Getting Started with AtomVM: Setup with Prebuilt Firmware (ESP32-S3, September 2025)</title>
      <dc:creator>Masatoshi Nishiguchi</dc:creator>
      <pubDate>Tue, 02 Sep 2025 01:22:31 +0000</pubDate>
      <link>https://dev.to/mnishiguchi/getting-started-with-atomvm-setup-with-prebuilt-firmware-esp32-s3-september-2025-4lkj</link>
      <guid>https://dev.to/mnishiguchi/getting-started-with-atomvm-setup-with-prebuilt-firmware-esp32-s3-september-2025-4lkj</guid>
      <description>&lt;p&gt;I previously tried using &lt;a href="https://github.com/atomvm/AtomVM" rel="noopener noreferrer"&gt;AtomVM&lt;/a&gt;, a lightweight virtual machine that lets you run Elixir (and Erlang) applications directly on microcontrollers like the &lt;a href="https://www.espressif.com/ja-jp/products/socs/esp32-s3" rel="noopener noreferrer"&gt;ESP32-S3&lt;/a&gt;, but honestly, I rushed through the process without fully understanding the underlying details — especially around firmware setup.&lt;/p&gt;

&lt;p&gt;This time around, I wanted to go back and properly understand how to set things up, focusing specifically on getting started with AtomVM using a prebuilt firmware image. The goal is to simplify the onboarding experience without building anything from source.&lt;/p&gt;

&lt;p&gt;This post doesn’t dig into advanced features. Instead, it’s a hands-on guide to get your ESP32-S3 board up and running with AtomVM, so you can start running Elixir code on real hardware as quickly as possible.&lt;/p&gt;

&lt;p&gt;Originally published in Japanese: &lt;a href="https://qiita.com/mnishiguchi/items/86a821f853b504911cae" rel="noopener noreferrer"&gt;AtomVM 入門: ビルド済みイメージを活用した環境構築 (2025年9月)&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Target Environment and Devices
&lt;/h2&gt;

&lt;p&gt;Here's what I used for this setup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Target Board
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://wiki.seeedstudio.com/xiao_esp32s3_getting_started/" rel="noopener noreferrer"&gt;Seeed Studio XIAO ESP32-S3&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Onboard LED on GPIO 21 (active-low)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Host Machine
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Linux (Debian-based — LMDE6)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hexdocs.pm/elixir/introduction.html" rel="noopener noreferrer"&gt;Elixir&lt;/a&gt; 1.17 with Erlang/OTP 27&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/atomvm/AtomVM" rel="noopener noreferrer"&gt;AtomVM&lt;/a&gt; v0.6.6 (prebuilt firmware)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.espressif.com/projects/esptool/en/latest/esp32/" rel="noopener noreferrer"&gt;esptool&lt;/a&gt; 5.0 — official flashing tool for ESP32&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.google.com/search?q=picocom+linux" rel="noopener noreferrer"&gt;picocom&lt;/a&gt; 3.1 — for serial monitoring&lt;/li&gt;
&lt;li&gt;Python 3.13 — required by esptool&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Notes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;esptool is a Python tool and requires Python 3.x.&lt;/li&gt;
&lt;li&gt;Since we're using a prebuilt AtomVM image, you do not need to install &lt;a href="https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/get-started/" rel="noopener noreferrer"&gt;ESP-IDF&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;For compatibility info (e.g. which Elixir &amp;amp; OTP versions are supported by AtomVM), refer to the official &lt;a href="https://doc.atomvm.org/main/release-notes.html" rel="noopener noreferrer"&gt;release notes&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Installing esptool
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.espressif.com/projects/esptool/en/latest/esp32/" rel="noopener noreferrer"&gt;esptool&lt;/a&gt; is the official command-line utility for flashing firmware to ESP32 boards. &lt;/p&gt;

&lt;p&gt;We'll install it using &lt;code&gt;pip&lt;/code&gt;, which makes it easy to manage and update.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"esptool&amp;gt;=5,&amp;lt;6"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Version 5.x introduces hyphenated options instead of older underscore syntax.&lt;/li&gt;
&lt;li&gt;Make sure Python 3.x is installed on your system.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Checking the Serial Port
&lt;/h2&gt;

&lt;p&gt;When you connect your ESP32-S3 via USB, Linux will create a new serial device (e.g., &lt;code&gt;/dev/ttyACM0&lt;/code&gt;). You’ll need this port name when flashing firmware or uploading &lt;code&gt;.avm&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;On Linux, you can run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dmesg | &lt;span class="nb"&gt;grep tty&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[12345.678901] cdc_acm 1-1.4:1.0: ttyACM0: USB ACM device
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, use &lt;code&gt;/dev/ttyACM0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You’re typically looking for something like &lt;code&gt;/dev/ttyACM0&lt;/code&gt; or &lt;code&gt;/dev/ttyUSB0&lt;/code&gt;, depending on your board and USB-to-serial chip.&lt;/p&gt;




&lt;h2&gt;
  
  
  Verifying &lt;code&gt;esptool&lt;/code&gt; and Board Connection
&lt;/h2&gt;

&lt;p&gt;Once &lt;code&gt;esptool&lt;/code&gt; is installed, you can check that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It's correctly installed&lt;/li&gt;
&lt;li&gt;It can communicate with your ESP32-S3 board
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check version&lt;/span&gt;
esptool.py version

&lt;span class="c"&gt;# Query flash chip information&lt;/span&gt;
esptool.py &lt;span class="nt"&gt;-p&lt;/span&gt; /dev/ttyACM0 flash-id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this succeeds, you're ready to start flashing firmware.&lt;/p&gt;




&lt;h2&gt;
  
  
  Downloading and Flashing AtomVM Firmware
&lt;/h2&gt;

&lt;p&gt;We’ll be using a prebuilt binary from the &lt;a href="https://github.com/atomvm/AtomVM/releases" rel="noopener noreferrer"&gt;AtomVM GitHub Releases&lt;/a&gt;, so there’s no need to build anything from source.&lt;/p&gt;

&lt;p&gt;We’ll use the official prebuilt AtomVM image (v0.6.6) for ESP32-S3.&lt;/p&gt;

&lt;h3&gt;
  
  
  Download the image
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Destination folder is arbitrary&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/Projects/atomvm &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$_&lt;/span&gt;

curl &lt;span class="nt"&gt;-LO&lt;/span&gt; https://github.com/atomvm/AtomVM/releases/download/v0.6.6/AtomVM-esp32s3-elixir-v0.6.6.img
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Erase flash (recommended)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; auto &lt;span class="nt"&gt;--port&lt;/span&gt; /dev/ttyACM0 &lt;span class="nt"&gt;--baud&lt;/span&gt; 921600 erase-flash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Write AtomVM firmware
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; auto &lt;span class="nt"&gt;--port&lt;/span&gt; /dev/ttyACM0 &lt;span class="nt"&gt;--baud&lt;/span&gt; 921600   write_flash 0x0 &lt;span class="nv"&gt;$HOME&lt;/span&gt;/Projects/atomvm/AtomVM-esp32s3-elixir-v0.6.6.img
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;According to the &lt;a href="https://doc.atomvm.org/main/getting-started-guide.html#flashing-a-binary-image-to-esp32" rel="noopener noreferrer"&gt;AtomVM docs&lt;/a&gt;, the bootloader offset for ESP32-S3 is &lt;code&gt;0x0&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building and Flashing an Elixir Application
&lt;/h2&gt;

&lt;p&gt;With AtomVM running on your ESP32-S3, you can now deploy an actual Elixir application to it!&lt;/p&gt;

&lt;p&gt;In this section, we'll use the classic &lt;code&gt;Blinky&lt;/code&gt; sample to flash an &lt;code&gt;.avm&lt;/code&gt; (AtomVM bytecode) file that blinks the onboard LED.&lt;/p&gt;

&lt;h3&gt;
  
  
  Get the sample project
&lt;/h3&gt;

&lt;p&gt;Clone the official &lt;a href="https://github.com/atomvm/atomvm_examples" rel="noopener noreferrer"&gt;atomvm_examples&lt;/a&gt; repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/Projects/atomvm  &lt;span class="c"&gt;# Or any directory you like&lt;/span&gt;

git clone https://github.com/atomvm/atomvm_examples.git
&lt;span class="nb"&gt;cd &lt;/span&gt;atomvm_examples/elixir/Blinky
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install dependencies (mainly &lt;a href="https://github.com/atomvm/exatomvm" rel="noopener noreferrer"&gt;exatomvm&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix deps.get
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adjust GPIO pin for XIAO ESP32-S3
&lt;/h3&gt;

&lt;p&gt;By default, the sample blinks &lt;code&gt;GPIO 2&lt;/code&gt; — but XIAO’s onboard LED is wired to &lt;code&gt;GPIO 21&lt;/code&gt; (active low). Update &lt;code&gt;lib/blinky.ex&lt;/code&gt; accordingly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Blinky&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@pin&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="ss"&gt;:gpio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_pin_mode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;@pin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;@pin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:low&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# LOW = LED ON (active-low)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="k"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="ss"&gt;:io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sx"&gt;~c"Setting pin ~p ~p~n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="ss"&gt;:gpio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;digital_write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:high&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="ss"&gt;:low&lt;/span&gt;
  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:low&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="ss"&gt;:high&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configure &lt;code&gt;mix.exs&lt;/code&gt; with flash offset
&lt;/h3&gt;

&lt;p&gt;You must set the &lt;code&gt;.avm&lt;/code&gt; flash offset to &lt;code&gt;0x250000&lt;/code&gt;. Without this, the ESP32-S3 won’t recognize your app at boot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;atomvm&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="ss"&gt;start:&lt;/span&gt; &lt;span class="no"&gt;SampleApp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;flash_offset:&lt;/span&gt; &lt;span class="mh"&gt;0x250000&lt;/span&gt;  &lt;span class="c1"&gt;# important!&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Package the app
&lt;/h3&gt;

&lt;p&gt;Build the &lt;code&gt;.avm&lt;/code&gt; files using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix atomvm.packbeam
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generates &lt;code&gt;.avm&lt;/code&gt; files such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blinky.avm&lt;/li&gt;
&lt;li&gt;deps.avm&lt;/li&gt;
&lt;li&gt;priv.avm&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Flash the app to ESP32-S3
&lt;/h3&gt;

&lt;p&gt;Finally, push your app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix atomvm.esp32.flash &lt;span class="nt"&gt;--port&lt;/span&gt; /dev/ttyACM0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will write the &lt;code&gt;.avm&lt;/code&gt; files to the right spot, without touching the AtomVM firmware.&lt;/p&gt;

&lt;p&gt;You should now see the LED start blinking 🎉&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frf1cl96mz1c21rkdyeg0.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frf1cl96mz1c21rkdyeg0.gif" width="463" height="436"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Viewing Logs over Serial
&lt;/h2&gt;

&lt;p&gt;Your Blinky app doesn't just blink the LED — it also prints log messages via serial.&lt;/p&gt;

&lt;p&gt;To view these logs in real time, you'll need a serial monitor. We'll use &lt;a href="https://linux.die.net/man/1/picocom" rel="noopener noreferrer"&gt;picocom&lt;/a&gt; — a simple, lightweight tool that works great on Linux.&lt;/p&gt;

&lt;p&gt;To start monitoring logs from your ESP32-S3 board:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;picocom /dev/ttyACM0 &lt;span class="nt"&gt;--baud&lt;/span&gt; 115200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This opens the serial console. You should immediately start seeing output like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm94a498w6nnoo65gx021.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm94a498w6nnoo65gx021.gif" width="463" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These messages come from your Elixir code (&lt;code&gt;:io.format&lt;/code&gt;), showing the pin toggling every second.&lt;/p&gt;

&lt;p&gt;To exit picocom, press: &lt;code&gt;Ctrl+A&lt;/code&gt; then &lt;code&gt;Ctrl+X&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this post, we walked through the full process of setting up an &lt;a href="https://www.espressif.com/ja-jp/products/socs/esp32-s3" rel="noopener noreferrer"&gt;ESP32-S3&lt;/a&gt; board to run Elixir code using &lt;a href="https://github.com/atomvm/AtomVM" rel="noopener noreferrer"&gt;AtomVM&lt;/a&gt; — no custom firmware builds required.&lt;/p&gt;

&lt;p&gt;This setup makes it easy to start tinkering with embedded Elixir, even without diving deep into ESP-IDF or AtomVM internals.&lt;/p&gt;

&lt;p&gt;Next steps might include integrating with sensors, displaying data, or connecting to the cloud via MQTT.  &lt;/p&gt;

&lt;p&gt;Hopefully, this gave you a smooth start with AtomVM on ESP32-S3!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F82804%2F3d6ebcb0-ef5c-40e1-84f1-4af14b8cd9a3.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F82804%2F3d6ebcb0-ef5c-40e1-84f1-4af14b8cd9a3.jpeg" alt="480439290-851da792-aef1-41b9-8931-4449079e4f6e.jpg" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mnishiguchi/hello_atomvm_tft_spi" rel="noopener noreferrer"&gt;https://github.com/mnishiguchi/hello_atomvm_tft_spi&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/atomvm/AtomVM" rel="noopener noreferrer"&gt;AtomVM GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://doc.atomvm.org/latest/getting-started-guide.html#getting-started-on-the-esp32-platform" rel="noopener noreferrer"&gt;AtomVM Getting Started Guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://doc.atomvm.org/main/release-notes.html" rel="noopener noreferrer"&gt;AtomVM Release Notes&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/atomvm/atomvm_examples/tree/master/elixir/Blinky" rel="noopener noreferrer"&gt;Blinky Example&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://wiki.seeedstudio.com/xiao_esp32s3_getting_started/" rel="noopener noreferrer"&gt;Seeed Studio XIAO ESP32-S3&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hexdocs.pm/elixir/introduction.html" rel="noopener noreferrer"&gt;Elixir Docs&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>atomvm</category>
      <category>elixir</category>
      <category>esp32</category>
      <category>iot</category>
    </item>
    <item>
      <title>Building a Local AI-Friendly Code Viewer Inspired by Uithub</title>
      <dc:creator>Masatoshi Nishiguchi</dc:creator>
      <pubDate>Fri, 28 Mar 2025 04:52:29 +0000</pubDate>
      <link>https://dev.to/mnishiguchi/building-a-local-ai-friendly-code-viewer-inspired-by-uithub-24ll</link>
      <guid>https://dev.to/mnishiguchi/building-a-local-ai-friendly-code-viewer-inspired-by-uithub-24ll</guid>
      <description>&lt;p&gt;I've been using AI tools daily at work—thanks to my employer’s support for AI-assisted development. At the same time, I like keeping my text editor simple and sometimes prefer writing code without real-time AI suggestions.&lt;/p&gt;

&lt;p&gt;Recently, I found &lt;a href="https://uithub.com/" rel="noopener noreferrer"&gt;Uithub&lt;/a&gt;, a helpful web service introduced by a coworker. It renders Git repository contents (directory trees and file contents) in a format that works well for AI conversations.&lt;/p&gt;

&lt;p&gt;Here’s a related blog post from a teammate:&lt;br&gt;&lt;br&gt;
&lt;a href="https://qiita.com/haw_ohnuma/items/3c4f0a764b1cf66bae7b" rel="noopener noreferrer"&gt;Qiita: Uithubを使ってAIとコードを会話する&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I liked the idea so much that I wanted something similar locally—without needing to upload code anywhere.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/mnishiguchi/items/58406b1cd34b3dc0ae9e" rel="noopener noreferrer"&gt;ニホンゴ版&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  First Attempt: Shell Script
&lt;/h2&gt;

&lt;p&gt;My first attempt was a Bash script that mimicked Uithub’s output. I wrote about it here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/mnishiguchi/items/af13b34ac636c03d97c5" rel="noopener noreferrer"&gt;Local version of Uithub using shell script&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While it worked, it was hard to maintain or extend. So I decided to rewrite it in a more suitable language.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rewriting in Go
&lt;/h2&gt;

&lt;p&gt;Around the same time, I noticed that &lt;a href="https://github.com/asdf-vm/asdf" rel="noopener noreferrer"&gt;asdf&lt;/a&gt; was rewritten in Go. I had also been practicing Go recently, so I rewrote my local Uithub-inspired tool as a Go CLI app.&lt;/p&gt;

&lt;p&gt;For the CLI framework, I picked &lt;a href="https://github.com/urfave/cli" rel="noopener noreferrer"&gt;&lt;code&gt;urfave/cli/v2&lt;/code&gt;&lt;/a&gt;, which is also used by &lt;code&gt;asdf&lt;/code&gt;. It’s lightweight and works well for small tools.&lt;/p&gt;

&lt;p&gt;I named it &lt;code&gt;uit&lt;/code&gt;, short for &lt;strong&gt;Uithub-Inspired Tool&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here’s the GitHub repo: &lt;a href="https://github.com/mnishiguchi/uit" rel="noopener noreferrer"&gt;https://github.com/mnishiguchi/uit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can download prebuilt binaries for macOS and Linux from the &lt;a href="https://github.com/mnishiguchi/uit/releases" rel="noopener noreferrer"&gt;Releases&lt;/a&gt; page.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Just a small tool I built for myself, but if it helps others too — even better.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>github</category>
      <category>go</category>
    </item>
    <item>
      <title>Boost Your Terminal Navigation with the y z Workflow</title>
      <dc:creator>Masatoshi Nishiguchi</dc:creator>
      <pubDate>Tue, 18 Mar 2025 23:36:30 +0000</pubDate>
      <link>https://dev.to/mnishiguchi/boost-your-terminal-navigation-with-the-y-z-workflow-291c</link>
      <guid>https://dev.to/mnishiguchi/boost-your-terminal-navigation-with-the-y-z-workflow-291c</guid>
      <description>&lt;p&gt;Navigating directories efficiently in the terminal is essential for&lt;br&gt;
productivity. While &lt;code&gt;cd&lt;/code&gt; has been the standard for decades, there are smarter&lt;br&gt;
and faster alternatives.&lt;/p&gt;

&lt;p&gt;Meet the &lt;code&gt;y&lt;/code&gt; → &lt;code&gt;z&lt;/code&gt; workflow—a powerful way to jump between directories and&lt;br&gt;
manage files effortlessly.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is &lt;code&gt;y&lt;/code&gt; → &lt;code&gt;z&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The letters &lt;code&gt;y&lt;/code&gt; and &lt;code&gt;z&lt;/code&gt; don’t have an inherent meaning, but their alphabetical&lt;br&gt;
order mirrors &lt;code&gt;c&lt;/code&gt; and &lt;code&gt;d&lt;/code&gt;—making them easy to remember as a &lt;code&gt;cd&lt;/code&gt; alternative.&lt;/p&gt;

&lt;p&gt;This workflow leverages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/ajeetdsouza/zoxide" rel="noopener noreferrer"&gt;&lt;code&gt;zoxide&lt;/code&gt;&lt;/a&gt; – A smarter &lt;code&gt;cd&lt;/code&gt;
replacement that remembers frequently visited directories.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://yazi-rs.github.io/docs/quick-start/" rel="noopener noreferrer"&gt;&lt;code&gt;yazi&lt;/code&gt;&lt;/a&gt; – A blazing-fast
terminal file manager with &lt;code&gt;zoxide&lt;/code&gt; integration.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/junegunn/fzf" rel="noopener noreferrer"&gt;&lt;code&gt;fzf&lt;/code&gt;&lt;/a&gt; – A fuzzy finder that enhances
interactive navigation.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Power of &lt;code&gt;z&lt;/code&gt; (zoxide)
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;zoxide&lt;/code&gt; enhances terminal navigation by remembering frequently visited&lt;br&gt;
directories. The &lt;code&gt;z&lt;/code&gt; command allows you to quickly jump to a known directory&lt;br&gt;
based on name matching. For even more flexibility, &lt;code&gt;zi&lt;/code&gt; integrates &lt;code&gt;zoxide&lt;/code&gt;&lt;br&gt;
with &lt;code&gt;fzf&lt;/code&gt;, enabling an interactive fuzzy search through your directory&lt;br&gt;
history, letting you select a destination effortlessly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic Commands
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;z project    &lt;span class="c"&gt;# Jump to a frequently visited "project" directory &lt;/span&gt;
zi           &lt;span class="c"&gt;# Use fzf to interactively search recent directories &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Power of &lt;code&gt;y&lt;/code&gt; (yazi)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://yazi-rs.github.io/docs/quick-start/" rel="noopener noreferrer"&gt;&lt;code&gt;yazi&lt;/code&gt;&lt;/a&gt; is a powerful&lt;br&gt;
terminal-based file manager designed for fast and efficient navigation. One of&lt;br&gt;
its standout features is its seamless integration with &lt;code&gt;zoxide&lt;/code&gt;. While using&lt;br&gt;
&lt;code&gt;yazi&lt;/code&gt;, you can press &lt;code&gt;z&lt;/code&gt; to instantly jump to any frequently visited directory&lt;br&gt;
tracked by &lt;code&gt;zoxide&lt;/code&gt;. This allows you to combine fuzzy directory switching with&lt;br&gt;
visual file management, creating a smooth and efficient workflow for handling&lt;br&gt;
files and navigating your system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic Commands
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;y            &lt;span class="c"&gt;# Open yazi &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once inside &lt;code&gt;yazi&lt;/code&gt;, press &lt;code&gt;z&lt;/code&gt; to instantly jump to a directory tracked by&lt;br&gt;
&lt;code&gt;zoxide&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;However, when a full TUI file explorer isn't necessary, you might prefer using&lt;br&gt;
&lt;code&gt;zi&lt;/code&gt; directly. Instead of opening &lt;code&gt;yazi&lt;/code&gt;, the &lt;code&gt;zi&lt;/code&gt; command allows you to&lt;br&gt;
instantly select and jump to a directory using fuzzy search—making it the&lt;br&gt;
fastest option for quick directory switching.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;If you're looking for a faster way to move around your file system, the &lt;code&gt;y&lt;/code&gt; →&lt;br&gt;
&lt;code&gt;z&lt;/code&gt; workflow is a game-changer. &lt;/p&gt;

</description>
      <category>cli</category>
      <category>tui</category>
      <category>fzf</category>
      <category>yazi</category>
    </item>
    <item>
      <title>Elixir/Nerves: blinking LED</title>
      <dc:creator>Masatoshi Nishiguchi</dc:creator>
      <pubDate>Mon, 24 Jun 2024 01:48:42 +0000</pubDate>
      <link>https://dev.to/mnishiguchi/elixirnerves-blinking-led-5gf1</link>
      <guid>https://dev.to/mnishiguchi/elixirnerves-blinking-led-5gf1</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When we first learn &lt;a href="https://nerves-project.org/" rel="noopener noreferrer"&gt;Nerves&lt;/a&gt;, one of the things we come up with is blinking LEDs on a breadboard.&lt;/p&gt;

&lt;p&gt;Today I'd like to try one implementation using GenServer.&lt;/p&gt;

&lt;p&gt;This is actually my outcome at this event I participated in last weekend "&lt;a href="https://piyopiyoex.connpass.com/event/317734/" rel="noopener noreferrer"&gt;Shop for parts in Akihabara and get started on Nerves right away!&lt;/a&gt;". It was so much fun hanging out in &lt;a href="https://en.wikipedia.org/wiki/Akihabara" rel="noopener noreferrer"&gt;Akihabara, Tokyo&lt;/a&gt; with &lt;a href="https://nerves-project.org/" rel="noopener noreferrer"&gt;Nerves&lt;/a&gt; enthusiasts.&lt;/p&gt;

&lt;p&gt;Thank you very much to the organizers &lt;a href="https://twitter.com/etcinitd" rel="noopener noreferrer"&gt;myasu&lt;/a&gt; and &lt;a href="https://twitter.com/nako_sleep_9h" rel="noopener noreferrer"&gt;nako_sleep_9h&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/etcinitd" rel="noopener noreferrer"&gt;myasu&lt;/a&gt; is very knowledgeable about electric and electronic stuff. I learned tons of pro tips from him, which I might write up as my future posts. He is the author of this book about Nerves.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nextpublishing.jp/book/17353.html" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2uzjz5387jogezkpnjy4.jpg" width="800" height="1130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/nako_sleep_9h" rel="noopener noreferrer"&gt;nako_sleep_9h&lt;/a&gt; is awesome at organizing and mc'ing fun-driven engineering events across Japan. She wrote a &lt;a href="https://qiita.com/nako_sleep_9h/items/8956a061b014f11cc65c" rel="noopener noreferrer"&gt;report on Day 1&lt;/a&gt; for this event.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/nako_sleep_9h/status/1804441299258359890" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpbs.twimg.com%2Fmedia%2FGQqpzB3acAASr-t%3Fformat%3Djpg%26name%3Dlarge" width="1536" height="2048"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;blockquote&gt;
&lt;p&gt;めでたい、光った光った〜&lt;a href="https://twitter.com/hashtag/piyopiyoex?src=hash&amp;amp;ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;#piyopiyoex&lt;/a&gt; &lt;a href="https://t.co/5WBqnFftQU" rel="noopener noreferrer"&gt;pic.twitter.com/5WBqnFftQU&lt;/a&gt;&lt;/p&gt;— nako@9時間睡眠 (@nako_sleep_9h) &lt;a href="https://twitter.com/nako_sleep_9h/status/1804441299258359890?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;June 22, 2024&lt;/a&gt;
&lt;/blockquote&gt; 
&lt;h2&gt;
  
  
  Nerves Livebook
&lt;/h2&gt;

&lt;p&gt;There are more than a few ways to get started with &lt;a href="https://nerves-project.org/" rel="noopener noreferrer"&gt;Nerves&lt;/a&gt; IoT projects but one of the easiest is to use prebuild &lt;a href="https://github.com/nerves-livebook/nerves_livebook" rel="noopener noreferrer"&gt;Nerves Livebook&lt;/a&gt; firmware. &lt;a href="https://github.com/nerves-livebook/nerves_livebook" rel="noopener noreferrer"&gt;Nerves Livebook&lt;/a&gt; allows us to control our target devices such as Raspberry Pi, doing Elixir coding in a web browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/nerves-livebook/nerves_livebook/blob/main/README.md" rel="noopener noreferrer"&gt;https://github.com/nerves-livebook/nerves_livebook/blob/main/README.md&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Blink using Circuits GPIO
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/nerves-livebook/nerves_livebook" rel="noopener noreferrer"&gt;Nerves Livebook&lt;/a&gt; has a nice tutorial notebook about how to use the &lt;a href="https://hexdocs.pm/circuits_gpio/readme.html" rel="noopener noreferrer"&gt;circuits_gpio&lt;/a&gt; package to control a &lt;a href="https://en.wikipedia.org/wiki/General-purpose_input/output" rel="noopener noreferrer"&gt;GPIO&lt;/a&gt; as an output, and blink an LED. It shows us what electric parts we need minimally and how we wire them before getting down to business.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/nerves-livebook/nerves_livebook/blob/main/priv/samples/basics/blink.livemd" rel="noopener noreferrer"&gt;https://github.com/nerves-livebook/nerves_livebook/blob/main/priv/samples/basics/blink.livemd&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Wrapping our LED blinking logic in GenServer
&lt;/h2&gt;

&lt;p&gt;When we want to repeat the blinking forever but want to stop at some stage without restarting the device, it is nice to prepare a &lt;a href="https://hexdocs.pm/elixir/GenServer.html" rel="noopener noreferrer"&gt;GenServer&lt;/a&gt; that manages the LED blinking. There can be many ways to achieve similar effect but here is one implementation using GenServer.&lt;/p&gt;

&lt;p&gt;First, let's write a simple module that wraps &lt;a href="https://en.wikipedia.org/wiki/General-purpose_input/output" rel="noopener noreferrer"&gt;GPIO&lt;/a&gt;-related logic within so that we can forget about how exactly we need to communicate with our LEDs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;LedBlink&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;led_pin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Circuits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;GPIO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;led_pin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;led_pin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Circuits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;GPIO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;led_pin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gpio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gpio&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gpio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gpio&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gpio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gpio&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gpio&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="ss"&gt;:ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Circuits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;GPIO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gpio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gpio&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="ss"&gt;:ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Circuits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;GPIO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gpio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then next, we make a &lt;a href="https://hexdocs.pm/elixir/GenServer.html" rel="noopener noreferrer"&gt;GenServer&lt;/a&gt; that lets an LED blink forever.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;BlinkServer&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;GenServer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;restart:&lt;/span&gt; &lt;span class="ss"&gt;:temporary&lt;/span&gt;

  &lt;span class="kn"&gt;require&lt;/span&gt; &lt;span class="no"&gt;Logger&lt;/span&gt;

  &lt;span class="nv"&gt;@run_interval_ms&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;

  &lt;span class="c1"&gt;## Client&lt;/span&gt;

  &lt;span class="nv"&gt;@doc&lt;/span&gt; &lt;span class="sd"&gt;"""
  Start a BlinkServer for the provided GPIO pin. It lets the LED blink forever.

  ### Examples

      iex&amp;gt;  BlinkServer.start_link(led_pin: "GPIO17")

  """&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;GenServer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;GenServer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;## Callbacks&lt;/span&gt;

  &lt;span class="nv"&gt;@impl&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;led_pin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Access&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetch!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:led_pin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;initial_state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;
      &lt;span class="ss"&gt;gpio_ref:&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;led_state:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;led_pin:&lt;/span&gt; &lt;span class="n"&gt;led_pin&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;initial_state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:continue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:init_gpio&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nv"&gt;@impl&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_continue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:init_gpio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;LedBlink&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;led_pin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gpio_ref&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;new_state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="ss"&gt;gpio_ref:&lt;/span&gt; &lt;span class="n"&gt;gpio_ref&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="ss"&gt;:toggle_led_state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:noreply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_state&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:stop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nv"&gt;@impl&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:toggle_led_state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_led_state&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;LedBlink&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gpio_ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;led_state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;new_state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="ss"&gt;led_state:&lt;/span&gt; &lt;span class="n"&gt;new_led_state&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="no"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"toggled LED: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;new_state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;led_state&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_after&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="ss"&gt;:toggle_led_state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;@run_interval_ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:noreply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_state&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nv"&gt;@impl&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;terminate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;LedBlink&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gpio_ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"terminated: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="ss"&gt;:ok&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is how to use our BlinkServer.&lt;/p&gt;

&lt;p&gt;When starting BlinkServer, we specify the GPIO pin we use for our LED.&lt;/p&gt;

&lt;p&gt;When we are done experimenting, we can stop our running BlinkServer worker.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="no"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;level:&lt;/span&gt; &lt;span class="ss"&gt;:debug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="no"&gt;BlinkServer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;led_pin:&lt;/span&gt; &lt;span class="s2"&gt;"GPIO17"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="no"&gt;BlinkServer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This implementation of BlinkServer is a singleton &lt;a href="https://hexdocs.pm/elixir/GenServer.html" rel="noopener noreferrer"&gt;GenServer&lt;/a&gt;, meaning we are not allowed to start more than one BlinkServer workers. So if we change our mind on the &lt;a href="https://en.wikipedia.org/wiki/General-purpose_input/output" rel="noopener noreferrer"&gt;GPIO&lt;/a&gt; pin we use, for example, we would need to stop the worker and start a new worker with different options.&lt;/p&gt;

&lt;h2&gt;
  
  
  circuits_gpio package
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://hexdocs.pm/circuits_gpio/readme.html" rel="noopener noreferrer"&gt;circuits_gpio&lt;/a&gt; is the one that lets us control &lt;a href="https://en.wikipedia.org/wiki/General-purpose_input/output" rel="noopener noreferrer"&gt;GPIO&lt;/a&gt; in our Elixir code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;We wrote a simple &lt;a href="https://hexdocs.pm/elixir/GenServer.html" rel="noopener noreferrer"&gt;GenServer&lt;/a&gt; that lets our LED blink.&lt;/p&gt;

&lt;p&gt;Once you get a feel of it, I would like to see your LED blinking logic. Please share!&lt;/p&gt;



&lt;blockquote&gt;
&lt;br&gt;
&lt;p&gt;今日は初Nerves！&lt;br&gt;8月末のSWESTに向けた知識を付けていこう～ &lt;a href="https://t.co/SrGEUJjzkS" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://t.co/SrGEUJjzkS" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://t.co/SrGEUJjzkS" rel="noopener noreferrer"&gt;https://t.co/SrGEUJjzkS&lt;/a&gt;&lt;/p&gt;— FRICK (@TewiEwi_no96) &lt;a href="https://twitter.com/TewiEwi_no96/status/1804302628907814965?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;June 21, 2024&lt;/a&gt;&lt;br&gt;
&lt;/blockquote&gt; 



&lt;blockquote&gt;
&lt;br&gt;
&lt;p&gt;Nerves勉強会2日目！&lt;br&gt;開始〜 &lt;a href="https://t.co/zgMKDEGrH8" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://t.co/zgMKDEGrH8" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://t.co/zgMKDEGrH8" rel="noopener noreferrer"&gt;https://t.co/zgMKDEGrH8&lt;/a&gt; &lt;a href="https://t.co/rT7QiHslAa" rel="noopener noreferrer"&gt;pic.twitter.com/rT7QiHslAa&lt;/a&gt;&lt;/p&gt;— FRICK (@TewiEwi_no96) &lt;a href="https://twitter.com/TewiEwi_no96/status/1804692392622305757?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;June 23, 2024&lt;/a&gt;&lt;br&gt;
&lt;/blockquote&gt; 



&lt;blockquote&gt;
&lt;br&gt;
&lt;p&gt;&lt;a href="https://twitter.com/hashtag/piyopiyoex?src=hash&amp;amp;ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;#piyopiyoex&lt;/a&gt;&lt;br&gt;【オフライン】秋葉原でパーツお買い物＆そのままNerves入門！ &lt;br&gt;はじまたー &lt;a href="https://t.co/BPiPBxZMTq" rel="noopener noreferrer"&gt;pic.twitter.com/BPiPBxZMTq&lt;/a&gt;&lt;/p&gt;— ysaito (&lt;a class="mentioned-user" href="https://dev.to/ysaito8015"&gt;@ysaito8015&lt;/a&gt;) &lt;a href="https://twitter.com/ysaito8015/status/1804695245550157840?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;June 23, 2024&lt;/a&gt;&lt;br&gt;
&lt;/blockquote&gt; 



&lt;blockquote&gt;
&lt;br&gt;
&lt;p&gt;神田明神きたよ&lt;a href="https://twitter.com/hashtag/piyopiyoex?src=hash&amp;amp;ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;#piyopiyoex&lt;/a&gt; &lt;a href="https://t.co/xuxuiWcl2F" rel="noopener noreferrer"&gt;pic.twitter.com/xuxuiWcl2F&lt;/a&gt;&lt;/p&gt;— nako@9時間睡眠 (@nako_sleep_9h) &lt;a href="https://twitter.com/nako_sleep_9h/status/1804374303284302326?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;June 22, 2024&lt;/a&gt;&lt;br&gt;
&lt;/blockquote&gt; 



&lt;blockquote&gt;
&lt;br&gt;
&lt;p&gt;今日はこちらへ参加！ &lt;a href="https://t.co/L28N3O6nIF" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://t.co/L28N3O6nIF" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://t.co/L28N3O6nIF" rel="noopener noreferrer"&gt;https://t.co/L28N3O6nIF&lt;/a&gt;&lt;/p&gt;— myasu (@etcinitd) &lt;a href="https://twitter.com/etcinitd/status/1804313482428584446?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;June 22, 2024&lt;/a&gt;&lt;br&gt;
&lt;/blockquote&gt; 



&lt;blockquote&gt;
&lt;br&gt;
&lt;p&gt;今日はDay2!!&lt;a href="https://t.co/hgJwwqGO9W" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://t.co/hgJwwqGO9W" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://t.co/hgJwwqGO9W" rel="noopener noreferrer"&gt;https://t.co/hgJwwqGO9W&lt;/a&gt;&lt;/p&gt;— myasu (@etcinitd) &lt;a href="https://twitter.com/etcinitd/status/1804676385459536130?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;June 23, 2024&lt;/a&gt;&lt;br&gt;
&lt;/blockquote&gt; 

</description>
      <category>nerves</category>
      <category>elixir</category>
      <category>raspberrypi</category>
      <category>iot</category>
    </item>
    <item>
      <title>Where Nerves-related Mix tasks are defined?</title>
      <dc:creator>Masatoshi Nishiguchi</dc:creator>
      <pubDate>Mon, 15 Jan 2024 20:03:19 +0000</pubDate>
      <link>https://dev.to/mnishiguchi/where-nerves-related-mix-tasks-are-defined-1pm7</link>
      <guid>https://dev.to/mnishiguchi/where-nerves-related-mix-tasks-are-defined-1pm7</guid>
      <description>&lt;p&gt;Nerves comes with a number of powerful and convenient Mix tasks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nerves-project.org/" rel="noopener noreferrer"&gt;https://nerves-project.org/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since I have not used many of those Mix tasks, first I want to learn what Mix tasks are available for my Nerves projects.&lt;/p&gt;

&lt;p&gt;The Nerves project is made up of separate per-scope sub-projects. So Mix tasks are maintained in different repositories. It is great for maintainability, but finding source code can be a bit hard without prior knowledge. For that reason, it is nice to familiarize ourselves with Nerves sub-projects by casually visiting them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mix
&lt;/h2&gt;

&lt;p&gt;In case, somebody is new to Mix, here is some info.&lt;/p&gt;

&lt;p&gt;Mix is a build tool that provides tasks such as creating, compiling, testing, and managing dependencies for Elixir projects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hexdocs.pm/mix/Mix.html" rel="noopener noreferrer"&gt;https://hexdocs.pm/mix/Mix.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://elixirschool.com/en/lessons/basics/mix" rel="noopener noreferrer"&gt;https://elixirschool.com/en/lessons/basics/mix&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Enumerating Mix tasks in Elixir project
&lt;/h2&gt;

&lt;p&gt;First of let's try and enumerate Mix tasks available for your Nerves project. We can achieve that just by running &lt;code&gt;mix help&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As an example, let's try it under the &lt;a href="https://github.com/nerves-livebook/nerves_livebook/blob/main/README.md" rel="noopener noreferrer"&gt;nerves_livebook&lt;/a&gt; project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd path/to/nerves_livebook

mix help
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, the results include non-Nerves Mix tasks. As I counted the number of lines, it was a total of 65. It might vary depending on projects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mix help | wc -l
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Filtering down to only Nerves-related Mix tasks
&lt;/h2&gt;

&lt;p&gt;I simply ran &lt;code&gt;grep&lt;/code&gt; and was able to narrow it down to only Nerves related Mix tasks!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mix help | grep 'mix' | grep -iE 'nerves|firmware'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The results are as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix burn                    &lt;span class="c"&gt;# Write a firmware image to an SDCard&lt;/span&gt;
mix compile.nerves_package  &lt;span class="c"&gt;# Nerves Package Compiler&lt;/span&gt;
mix firmware                &lt;span class="c"&gt;# Build a firmware bundle&lt;/span&gt;
mix firmware.burn           &lt;span class="c"&gt;# Build a firmware bundle and write it to an SDCard&lt;/span&gt;
mix firmware.gen.gdb        &lt;span class="c"&gt;# Generates a helper shell script for using gdb to analyze core dumps&lt;/span&gt;
mix firmware.gen.script     &lt;span class="c"&gt;# Generates a shell script for pushing firmware updates&lt;/span&gt;
mix firmware.image          &lt;span class="c"&gt;# Create a firmware image file&lt;/span&gt;
mix firmware.metadata       &lt;span class="c"&gt;# Print out metadata for the current firmware&lt;/span&gt;
mix firmware.patch          &lt;span class="c"&gt;# Build a firmware patch&lt;/span&gt;
mix firmware.unpack         &lt;span class="c"&gt;# Unpack a firmware bundle for inspection&lt;/span&gt;
mix local.nerves            &lt;span class="c"&gt;# Checks for updates to nerves_bootstrap&lt;/span&gt;
mix nerves.artifact         &lt;span class="c"&gt;# Creates system and toolchain artifacts for Nerves&lt;/span&gt;
mix nerves.artifact.details &lt;span class="c"&gt;# Prints Nerves artifact details&lt;/span&gt;
mix nerves.clean            &lt;span class="c"&gt;# Cleans dependencies and build artifacts&lt;/span&gt;
mix nerves.info             &lt;span class="c"&gt;# Prints Nerves information&lt;/span&gt;
mix nerves.new              &lt;span class="c"&gt;# Creates a new Nerves application&lt;/span&gt;
mix nerves.system.shell     &lt;span class="c"&gt;# Enter a shell to configure a custom system&lt;/span&gt;
mix nerves_key.device       &lt;span class="c"&gt;# Simulate NervesKey device key creation&lt;/span&gt;
mix nerves_key.signer       &lt;span class="c"&gt;# Manages NervesKey signing keys&lt;/span&gt;
mix upload                  &lt;span class="c"&gt;# Uploads firmware to a Nerves device over SSH&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In case we want to learn the innerworkings of the commands later, it is nice to know where their souce code is located.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Nerves-related Mix tasks are defined
&lt;/h2&gt;

&lt;p&gt;This was more difficult than I had imagined. Nerves projects are packaged by concern. Therefore, various features are managed in different repositories.&lt;/p&gt;

&lt;p&gt;The nerves package's &lt;a href="https://github.com/nerves-project/nerves/blob/main/README.md" rel="noopener noreferrer"&gt;README.md&lt;/a&gt; explains what each repository is responsible for with a comprehensive listing.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Our project is spread over many repositories in order to focus on a limited scope per repository.&lt;/p&gt;

&lt;p&gt;This repository (nerves-project/nerves) is an entrance to Nerves and provides the core tooling and documentation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here are the packages that define Nerves-related Mix tasks.&lt;/p&gt;

&lt;h3&gt;
  
  
  nerves package
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://hexdocs.pm/nerves" rel="noopener noreferrer"&gt;https://hexdocs.pm/nerves&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix burn                    &lt;span class="c"&gt;# Write a firmware image to an SDCard&lt;/span&gt;
mix compile.nerves_package  &lt;span class="c"&gt;# Nerves Package Compiler&lt;/span&gt;
mix firmware                &lt;span class="c"&gt;# Build a firmware bundle&lt;/span&gt;
mix firmware.burn           &lt;span class="c"&gt;# Build a firmware bundle and write it to an SDCard&lt;/span&gt;
mix firmware.gen.gdb        &lt;span class="c"&gt;# Generates a helper shell script for using gdb to analyze core dumps&lt;/span&gt;
mix firmware.image          &lt;span class="c"&gt;# Create a firmware image file&lt;/span&gt;
mix firmware.metadata       &lt;span class="c"&gt;# Print out metadata for the current firmware&lt;/span&gt;
mix firmware.patch          &lt;span class="c"&gt;# Build a firmware patch&lt;/span&gt;
mix firmware.unpack         &lt;span class="c"&gt;# Unpack a firmware bundle for inspection&lt;/span&gt;
mix nerves.artifact         &lt;span class="c"&gt;# Creates system and toolchain artifacts for Nerves&lt;/span&gt;
mix nerves.artifact.details &lt;span class="c"&gt;# Prints Nerves artifact details&lt;/span&gt;
mix nerves.clean            &lt;span class="c"&gt;# Cleans dependencies and build artifacts&lt;/span&gt;
mix nerves.info             &lt;span class="c"&gt;# Prints Nerves information&lt;/span&gt;
mix nerves.system.shell     &lt;span class="c"&gt;# Enter a shell to configure a custom system&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  nerves_bootstrap package
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://hexdocs.pm/nerves_bootstrap" rel="noopener noreferrer"&gt;https://hexdocs.pm/nerves_bootstrap&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix local.nerves            &lt;span class="c"&gt;# Checks for updates to nerves_bootstrap&lt;/span&gt;
mix nerves.new              &lt;span class="c"&gt;# Creates a new Nerves application&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  nerves_key package
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://hexdocs.pm/nerves_key" rel="noopener noreferrer"&gt;https://hexdocs.pm/nerves_key&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix nerves_key.device       &lt;span class="c"&gt;# Simulate NervesKey device key creation&lt;/span&gt;
mix nerves_key.signer       &lt;span class="c"&gt;# Manages NervesKey signing keys&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ssh_subsystem_fwup package
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://hexdocs.pm/ssh_subsystem_fwup" rel="noopener noreferrer"&gt;https://hexdocs.pm/ssh_subsystem_fwup&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix firmware.gen.script     &lt;span class="c"&gt;# Generates a shell script for pushing firmware updates&lt;/span&gt;
mix upload                  &lt;span class="c"&gt;# Uploads firmware to a Nerves device over SSH&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🎉🎉🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;To be honest, I have never used most of the Mix tasks I saw today yet. If you have any cool techniques using them, please share!&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>nerves</category>
      <category>raspberrypi</category>
      <category>iot</category>
    </item>
    <item>
      <title>Build Phoenix Docker Compose development environment easily</title>
      <dc:creator>Masatoshi Nishiguchi</dc:creator>
      <pubDate>Tue, 28 Nov 2023 23:25:12 +0000</pubDate>
      <link>https://dev.to/mnishiguchi/build-phoenix-docker-compose-development-environment-using-phx-docker-compose-new-instead-of-mix-phxnew-20n2</link>
      <guid>https://dev.to/mnishiguchi/build-phoenix-docker-compose-development-environment-using-phx-docker-compose-new-instead-of-mix-phxnew-20n2</guid>
      <description>&lt;p&gt;I wrote a script called &lt;a href="https://github.com/mnishiguchi/phx-docker-compose-new" rel="noopener noreferrer"&gt;phx-docker-compose-new&lt;/a&gt; that can be used instead of &lt;a href="https://hexdocs.pm/phoenix/Mix.Tasks.Phx.New.html" rel="noopener noreferrer"&gt;mix phx.new&lt;/a&gt; to generate a new &lt;a href="https://www.phoenixframework.org/" rel="noopener noreferrer"&gt;Phoenix&lt;/a&gt; application.&lt;/p&gt;

&lt;p&gt;Building the development environment for a &lt;a href="https://www.phoenixframework.org/" rel="noopener noreferrer"&gt;Phoenix&lt;/a&gt; application using &lt;a href="https://docs.docker.com/compose/" rel="noopener noreferrer"&gt;Docker Compose&lt;/a&gt; is convenient because you can set up not only the application but also the &lt;a href="https://www.postgresql.org/" rel="noopener noreferrer"&gt;PostgreSQL&lt;/a&gt; database, &lt;a href="https://livebook.dev/" rel="noopener noreferrer"&gt;Livebook&lt;/a&gt;, etc. all at once.&lt;/p&gt;

&lt;p&gt;However, the reality is that it is not always easy and requires some know-how.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/koyo-miyamura/items/a609de2e9fadaf198243" rel="noopener noreferrer"&gt;https://qiita.com/koyo-miyamura/items/a609de2e9fadaf198243&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://zenn.dev/koga1020/articles/d260bc1bde8267" rel="noopener noreferrer"&gt;https://zenn.dev/koga1020/articles/d260bc1bde8267&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/mnishiguchi/items/e367743bca3520e2a387" rel="noopener noreferrer"&gt;https://qiita.com/mnishiguchi/items/e367743bca3520e2a387&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I just thought it would be nice to have a script that allows us to easily build a &lt;a href="https://www.phoenixframework.org/" rel="noopener noreferrer"&gt;Phoenix&lt;/a&gt; development environment using &lt;a href="https://docs.docker.com/compose/" rel="noopener noreferrer"&gt;Docker Compose&lt;/a&gt;. Of course, just thinking about it won't change anything, so I got down to business straight away.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/mnishiguchi/items/425a7e55f05a7ab6359b" rel="noopener noreferrer"&gt;日本語版&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;Make sure that &lt;a href="https://git-scm.com/" rel="noopener noreferrer"&gt;Git&lt;/a&gt;, &lt;a href="https://docs.docker.com/get-started/overview/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;, and &lt;a href="https://docs.docker.com/compose/" rel="noopener noreferrer"&gt;Docker Compose&lt;/a&gt; are installed in your system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git version
docker &lt;span class="nt"&gt;--version&lt;/span&gt;
docker compose version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Download the source code for the &lt;a href="https://github.com/mnishiguchi/phx-docker-compose-new" rel="noopener noreferrer"&gt;phx-docker-compose-new&lt;/a&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/mnishiguchi/phx-docker-compose-new.git ~/.phx-docker-compose-new
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Define an &lt;code&gt;alias&lt;/code&gt; so you can use the &lt;a href="https://github.com/mnishiguchi/phx-docker-compose-new" rel="noopener noreferrer"&gt;phx-docker-compose-new&lt;/a&gt; command in your terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;phx-docker-compose-new&lt;span class="o"&gt;=&lt;/span&gt;~/.phx-docker-compose-new/phx-docker-compose-new.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generate a &lt;a href="https://www.phoenixframework.org/" rel="noopener noreferrer"&gt;Phoenix&lt;/a&gt; sample app using the &lt;a href="https://github.com/mnishiguchi/phx-docker-compose-new" rel="noopener noreferrer"&gt;phx-docker-compose-new&lt;/a&gt; command. For available options, refer to &lt;a href="https://hexdocs.pm/phoenix/Mix.Tasks.Phx.New.html" rel="noopener noreferrer"&gt;Phoenix official documentation&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;phx-docker-compose-new sample_phx_app &lt;span class="nt"&gt;--no-assets&lt;/span&gt; &lt;span class="nt"&gt;--no-gettext&lt;/span&gt; &lt;span class="nt"&gt;--no-mailer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go into the generated app directory and launch the app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;sample_phx_app

bin/start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can access the URLs below and start developing your &lt;a href="https://www.phoenixframework.org/" rel="noopener noreferrer"&gt;Phoenix&lt;/a&gt; app right now!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://localhost:4000/" rel="noopener noreferrer"&gt;http://localhost:4000/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://localhost:4000/dev/dashboard/" rel="noopener noreferrer"&gt;http://localhost:4000/dev/dashboard/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://localhost:4001/" rel="noopener noreferrer"&gt;http://localhost:4001/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fna26tg28n71sq4c08zyf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fna26tg28n71sq4c08zyf.png" alt="docker-compose-demo 2023-11-23 09-44-06.png" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can look at the log with the command below. Press &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;C&lt;/code&gt; to close the log.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bin/logs &lt;span class="nt"&gt;--follow&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://elixir-lang.org/" rel="noopener noreferrer"&gt;Elixir&lt;/a&gt;'s interactive console (&lt;a href="https://elixir-lang.org/getting-started/introduction.html#interactive-mode" rel="noopener noreferrer"&gt;IEx&lt;/a&gt;) can be started with the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bin/console
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since &lt;a href="https://elixir-lang.org/getting-started/introduction.html#interactive-mode" rel="noopener noreferrer"&gt;IEx&lt;/a&gt; is open, we might as well do something. Let's display a list of processes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="no"&gt;IEx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;configure&lt;/span&gt; &lt;span class="ss"&gt;inspect:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;limit:&lt;/span&gt; &lt;span class="ss"&gt;:infinity&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;for&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:registered_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;elem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To stop the app, use the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bin/stop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🎉🎉🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Congratulations! Now you can build a &lt;a href="https://www.phoenixframework.org/" rel="noopener noreferrer"&gt;Phoenix&lt;/a&gt; application development environment easily any time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxcy4zlyxreas5958l705.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxcy4zlyxreas5958l705.jpeg" alt="toukon-qiita-macbook_20230912_091808.jpg" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>erlang</category>
      <category>phoenix</category>
      <category>docker</category>
    </item>
    <item>
      <title>Elixirでリストの最初の要素を取得</title>
      <dc:creator>Masatoshi Nishiguchi</dc:creator>
      <pubDate>Thu, 17 Aug 2023 00:24:08 +0000</pubDate>
      <link>https://dev.to/mnishiguchi/elixirderisutonozui-chu-noyao-su-woqu-de-mll</link>
      <guid>https://dev.to/mnishiguchi/elixirderisutonozui-chu-noyao-su-woqu-de-mll</guid>
      <description>&lt;p&gt;&lt;a href="https://elixir-lang.org/" rel="noopener noreferrer"&gt;Elixir&lt;/a&gt;でListの最初の要素を取得する方法についていくつか考えてみました。一見初歩中の初歩のように見えますが、実は複数のやり方があり、その使い分けがエンジニアとしての腕の見せどころにもなりえます。&lt;/p&gt;

&lt;p&gt;これから&lt;a href="https://elixir-lang.org/" rel="noopener noreferrer"&gt;Elixir&lt;/a&gt;を始める方にはこのサイトがおすすめです。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://elixir-lang.info/" rel="noopener noreferrer"&gt;https://elixir-lang.info/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://elixir-lang.org/" rel="noopener noreferrer"&gt;Elixir&lt;/a&gt;とコミュニティの雰囲気をゆるく味わいたい方は「先端ピアちゃん」さんの動画が超オススメです。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/@piacerex" rel="noopener noreferrer"&gt;https://www.youtube.com/@piacerex&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ElixirのList
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://hexdocs.pm/elixir/List.html" rel="noopener noreferrer"&gt;List&lt;/a&gt;は値の集合です。複数の異なるタイプを含むことができます。一意でない値を含むこともできます。&lt;/p&gt;

&lt;p&gt;ひとつ注意点は、&lt;a href="https://elixir-lang.org/" rel="noopener noreferrer"&gt;Elixir&lt;/a&gt;の&lt;a href="https://hexdocs.pm/elixir/List.html" rel="noopener noreferrer"&gt;List&lt;/a&gt;は&lt;a href="https://ja.wikipedia.org/wiki/%E9%80%A3%E7%B5%90%E3%83%AA%E3%82%B9%E3%83%88" rel="noopener noreferrer"&gt;連結リスト&lt;/a&gt;として実装されていることです。今回の実験では特に問題がないですが、他の主要プログラミング言語の配列とは異なるので思い込みは禁物です。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://elixirschool.com/ja/lessons/basics/collections" rel="noopener noreferrer"&gt;https://elixirschool.com/ja/lessons/basics/collections&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/search?q=Elixir+List" rel="noopener noreferrer"&gt;https://qiita.com/search?q=Elixir+List&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  実験の準備
&lt;/h2&gt;

&lt;h3&gt;
  
  
  実験用List
&lt;/h3&gt;

&lt;p&gt;要素数の異なるパターンのリストを用意します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;lists&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  実験を実行する関数
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;前項で作成したリストを何度も使用するので、共通部分を切り出し関数化します。&lt;/li&gt;
&lt;li&gt;万一、例外が発生した場合にもテストを止めず、エラー内容を値として処理します。
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test_fun = fn do_test -&amp;gt;
  for list &amp;lt;- lists do
    try do
      do_test.(list)
    rescue
      e -&amp;gt; inspect(e)
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Kernel.hd/1 (hd/1)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://hexdocs.pm/elixir/Kernel.html#hd/1" rel="noopener noreferrer"&gt;hd/1&lt;/a&gt;は&lt;a href="https://hexdocs.pm/elixir/List.html" rel="noopener noreferrer"&gt;List&lt;/a&gt;が空の時にはエラーになります。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test_fun.(fn list -&amp;gt;
  hd(list)
end)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;["%ArgumentError{message: \"errors were found at the given arguments:\\n\\n  * 1st argument: not a nonempty list\\n\"}",
 nil,
 1,
 1,
 1]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://hexdocs.pm/elixir/Kernel.html#hd/1" rel="noopener noreferrer"&gt;https://hexdocs.pm/elixir/Kernel.html#hd/1&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  List.first/1
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://hexdocs.pm/elixir/List.html#first/2" rel="noopener noreferrer"&gt;List.first/2&lt;/a&gt;のユニークなところは、空リストの最初の要素は&lt;code&gt;nil&lt;/code&gt;と解釈する点です。この特性が便利な場面があります。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test_fun.(fn list -&amp;gt;
  List.first(list)
end)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[nil,
 nil,
 1,
 1,
 1]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://hexdocs.pm/elixir/List.html#first/2" rel="noopener noreferrer"&gt;https://hexdocs.pm/elixir/List.html#first/2&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Enum.at/2
&lt;/h2&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/torifukukaiou"&gt;@torifukukaiou&lt;/a&gt; さんからお便りをいただき、これを見落としていたのに気がつきました。ありがとうございます。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hexdocs.pm/elixir/Enum.html#at/2" rel="noopener noreferrer"&gt;Enum.at/2&lt;/a&gt;は&lt;a href="https://hexdocs.pm/elixir/List.html#first/2" rel="noopener noreferrer"&gt;List.first/2&lt;/a&gt;と同様に空リストの最初の要素を&lt;code&gt;nil&lt;/code&gt;とします。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test_fun.(fn list -&amp;gt;
  Enum.at(list, 0)
end)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[nil,
 nil,
 1,
 1,
 1]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Enum.fetch/2
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://hexdocs.pm/elixir/Enum.html#fetch/2" rel="noopener noreferrer"&gt;Enum.fetch/2&lt;/a&gt;は空リストの最初の要素を探すことを異常とみなしますが、プログラマーが例外処理しやすいように&lt;code&gt;{:ok, any} | :error&lt;/code&gt;を出力してくれます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test_fun.(fn list -&amp;gt;
  Enum.fetch(list, 0)
end)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[:error,
 {:ok, nil},
 {:ok, 1},
 {:ok, 1},
 {:ok, 1}]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://hexdocs.pm/elixir/Enum.html#fetch/2" rel="noopener noreferrer"&gt;https://hexdocs.pm/elixir/Enum.html#fetch/2&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Enum.fetch!/2
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://hexdocs.pm/elixir/Enum.html#fetch!/2" rel="noopener noreferrer"&gt;Enum.fetch!/2&lt;/a&gt;は空リストの最初の要素を探すことを異常とみなし、エラーを起こします。&lt;a href="https://hexdocs.pm/elixir/Kernel.html#hd/1" rel="noopener noreferrer"&gt;hd/1&lt;/a&gt;の時と結果が同じです。エラーメッセージは&lt;a href="https://hexdocs.pm/elixir/Kernel.html#hd/1" rel="noopener noreferrer"&gt;hd/1&lt;/a&gt;の方が具体的でわかりやすような気がしますが、こっちのエラーの方がスッキリ簡潔です。ここは好みが別れるところかもしれません。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test_fun.(fn list -&amp;gt;
  Enum.fetch!(list, 0)
end)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;["%Enum.OutOfBoundsError{message: \"out of bounds error\"}",
 nil
 1,
 1,
 1]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://hexdocs.pm/elixir/Enum.html#fetch!/2" rel="noopener noreferrer"&gt;https://hexdocs.pm/elixir/Enum.html#fetch!/2&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  [first | _rest]でパターンマッチ
&lt;/h2&gt;

&lt;p&gt;このパターンは最初の要素と残り全部の2つに分割されます。&lt;a href="https://hexdocs.pm/elixir/List.html" rel="noopener noreferrer"&gt;List&lt;/a&gt;が空の時はエラーになります。要素が一個の時は大丈夫のようです。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test_fun.(fn list -&amp;gt;
  [first | _rest] = list
  first
end)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;["%MatchError{term: []}",
 nil,
 1,
 1,
 1]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://elixirschool.com/ja/lessons/basics/pattern_matching" rel="noopener noreferrer"&gt;https://elixirschool.com/ja/lessons/basics/pattern_matching&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  [first, _, _]でパターンマッチ
&lt;/h2&gt;

&lt;p&gt;このパターンの場合は、要素数がピッタリ一致する場合のみヨシとされます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test_fun.(fn list -&amp;gt;
  [first, _, _] = list
  first
end)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;["%MatchError{term: []}",
 "%MatchError{term: [nil]}",
 "%MatchError{term: [1]}",
 "%MatchError{term: [1, 2]}",
 1]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ElixirのEnum技
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://hexdocs.pm/elixir/List.html" rel="noopener noreferrer"&gt;List&lt;/a&gt;等の集合を操作する時に使う&lt;a href="https://hexdocs.pm/elixir/Enum.html" rel="noopener noreferrer"&gt;Enum&lt;/a&gt;には無数の興味深い関数やテクニックがありますが、ここではいくつか特に&lt;strong&gt;イー&lt;/strong&gt;やつをご紹介させて頂こうと思います。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/torifukukaiou/items/e07ed758d1259d14a2b7" rel="noopener noreferrer"&gt;https://qiita.com/torifukukaiou/items/e07ed758d1259d14a2b7&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/torifukukaiou/items/3b65e5c04fa8c55f526e" rel="noopener noreferrer"&gt;https://qiita.com/torifukukaiou/items/3b65e5c04fa8c55f526e&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/torifukukaiou/items/4481f7884a20ab4b1bea" rel="noopener noreferrer"&gt;https://qiita.com/torifukukaiou/items/4481f7884a20ab4b1bea&lt;/a&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>list</category>
      <category>enum</category>
      <category>stream</category>
    </item>
    <item>
      <title>Elixir Phoenix 1.7 breadcrumb component</title>
      <dc:creator>Masatoshi Nishiguchi</dc:creator>
      <pubDate>Sat, 12 Aug 2023 19:39:15 +0000</pubDate>
      <link>https://dev.to/mnishiguchi/elixir-phoenix-17-breadcrumb-component-85c</link>
      <guid>https://dev.to/mnishiguchi/elixir-phoenix-17-breadcrumb-component-85c</guid>
      <description>&lt;p&gt;I like the &lt;a href="https://en.wikipedia.org/wiki/Breadcrumb_navigation" rel="noopener noreferrer"&gt;breadcrumb&lt;/a&gt; navigation in a web app so I wanted to make one what works in &lt;a href="https://www.phoenixframework.org/" rel="noopener noreferrer"&gt;Phoenix LiveView&lt;/a&gt; apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  what breadcrumb navigation is
&lt;/h2&gt;

&lt;p&gt;I think I first learned about the &lt;a href="https://en.wikipedia.org/wiki/Breadcrumb_navigation" rel="noopener noreferrer"&gt;breadcrumb&lt;/a&gt; from &lt;a href="https://getbootstrap.com/docs/5.3/components/breadcrumb" rel="noopener noreferrer"&gt;Bootstrap&lt;/a&gt; a long time ago, but I do not know the exact definition of it. Let's wikipedia.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A breadcrumb or breadcrumb trail is a graphical control element used as a navigational aid in user interfaces and on web pages. It allows users to keep track and maintain awareness of their locations within programs, documents, or websites.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Breadcrumb_navigation" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/Breadcrumb_navigation&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  my custom breadcrumb component
&lt;/h2&gt;

&lt;p&gt;I do not need anything smart. Simple is best. My breadcrumb component simply accepts a list of navigation items. It might be used like below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;.&lt;/span&gt;&lt;span class="n"&gt;breadcrumb&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{[&lt;/span&gt;
  &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;text:&lt;/span&gt; &lt;span class="s2"&gt;"Home"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;navigate:&lt;/span&gt; &lt;span class="sx"&gt;~p"/"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;text:&lt;/span&gt; &lt;span class="s2"&gt;"Examples"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;navigate:&lt;/span&gt; &lt;span class="sx"&gt;~p"/examples"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;text:&lt;/span&gt; &lt;span class="s2"&gt;"Light"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]}&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2wfpoi5jogoq8po9na9z.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2wfpoi5jogoq8po9na9z.gif" alt="liveview-breadcrumb 2023-08-11 at 22.40.14.gif" width="690" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By the way, the LiveView pages in the screen recording above are from &lt;a href="https://pragmaticstudio.com/courses/phoenix-liveview" rel="noopener noreferrer"&gt;Pragmatic Studio Phoenix LiveView Course&lt;/a&gt;. The online course is amazing. I added my custom &lt;a href="https://en.wikipedia.org/wiki/Breadcrumb_navigation" rel="noopener noreferrer"&gt;breadcrumb&lt;/a&gt; component onto them for my extra learning.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTML and Tailwind classes
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.phoenixframework.org/" rel="noopener noreferrer"&gt;Phoenix&lt;/a&gt; 1.7 comes with &lt;a href="https://tailwindcss.com" rel="noopener noreferrer"&gt;Tailwind CSS&lt;/a&gt; by default. &lt;a href="https://tailwindcss.com" rel="noopener noreferrer"&gt;Tailwind CSS&lt;/a&gt; just works without any configuration changes.&lt;/p&gt;

&lt;p&gt;First of all, I did the Internet search for an example &lt;a href="https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/HTML_basics" rel="noopener noreferrer"&gt;HTML&lt;/a&gt; snippet with &lt;a href="https://tailwind.build/classes" rel="noopener noreferrer"&gt;Tailwind CSS classes&lt;/a&gt; because my primary focus is to enjoy programming with &lt;a href="https://elixir-lang.org" rel="noopener noreferrer"&gt;Elixir&lt;/a&gt; and &lt;a href="https://www.phoenixframework.org/" rel="noopener noreferrer"&gt;Phoenix&lt;/a&gt;, not &lt;a href="https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/HTML_basics" rel="noopener noreferrer"&gt;HTML&lt;/a&gt;, not &lt;a href="https://tailwindcss.com" rel="noopener noreferrer"&gt;Tailwind CSS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I started by using this &lt;a href="https://flowbite.com/docs/components/breadcrumb" rel="noopener noreferrer"&gt;example snippet from Flowbite&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://flowbite.com/docs/components/breadcrumb" rel="noopener noreferrer"&gt;https://flowbite.com/docs/components/breadcrumb&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Phoenix.Component.link/1
&lt;/h2&gt;

&lt;p&gt;For links, I took advangate of &lt;a href="https://hexdocs.pm/phoenix_live_view/Phoenix.Component.html#link/1" rel="noopener noreferrer"&gt;Phoenix.Component.link/1&lt;/a&gt; and its &lt;code&gt;:navaigate&lt;/code&gt; attribute so I can navigate between LiveView pages smoothely.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hexdocs.pm/phoenix_live_view/Phoenix.Component.html#link/1" rel="noopener noreferrer"&gt;https://hexdocs.pm/phoenix_live_view/Phoenix.Component.html#link/1&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  icons
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.phoenixframework.org/" rel="noopener noreferrer"&gt;Phoenix&lt;/a&gt; 1.7 ships with &lt;a href="https://en.wikipedia.org/wiki/SVG" rel="noopener noreferrer"&gt;SVG&lt;/a&gt; icons from &lt;a href="https://heroicons.com" rel="noopener noreferrer"&gt;heroicons&lt;/a&gt; and those &lt;a href="https://en.wikipedia.org/wiki/SVG" rel="noopener noreferrer"&gt;SVG&lt;/a&gt; icons can be used easily via &lt;a href="https://github.com/phoenixframework/phoenix/blob/e095f23ff62efb2f8c1205b0cc67bfacd80b11ed/installer/templates/phx_web/components/core_components.ex#L569-L594" rel="noopener noreferrer"&gt;MyAppWeb.CoreComponents.icon&lt;/a&gt; component.&lt;/p&gt;

&lt;p&gt;Since all the functions in &lt;code&gt;MyAppWeb.CoreComponents&lt;/code&gt; module are &lt;a href="https://github.com/phoenixframework/phoenix/blob/e095f23ff62efb2f8c1205b0cc67bfacd80b11ed/installer/templates/phx_single/lib/app_name_web.ex#L87" rel="noopener noreferrer"&gt;imported by default&lt;/a&gt;, the module name can be omitted when we use the icon component.&lt;/p&gt;

&lt;p&gt;Thanks to the icon component, we no longer need to wrap our head around in terms of setting up icons, as long as we are happy with what &lt;a href="https://heroicons.com" rel="noopener noreferrer"&gt;heroicons&lt;/a&gt; library provides. It is very nice.&lt;/p&gt;

&lt;h2&gt;
  
  
  building breadcrumb component
&lt;/h2&gt;

&lt;p&gt;I created a new module named &lt;code&gt;MyAppWeb.Breadcrumb&lt;/code&gt; that is dedicated for the breadcrumb &lt;a href="https://hexdocs.pm/phoenix_live_view/Phoenix.Component.html" rel="noopener noreferrer"&gt;function component&lt;/a&gt;. Alternatively it could have been in &lt;code&gt;MyAppWeb.CoreComponents&lt;/code&gt; along with the icon component but I chose to create a new module so that I can focus on the breadcrumb concern. I might change my mind in the future.&lt;/p&gt;

&lt;p&gt;Here is the entirety of the module I created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Breadcrumb&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Phoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Component&lt;/span&gt;
  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;CoreComponents&lt;/span&gt;

  &lt;span class="n"&gt;attr&lt;/span&gt; &lt;span class="ss"&gt;:items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;required:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;breadcrumb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;assigns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;assigns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;assigns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;assigns&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="sx"&gt;~H""&lt;/span&gt;&lt;span class="s2"&gt;"
    &amp;lt;nav class="&lt;/span&gt;&lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="s2"&gt;" aria-label="&lt;/span&gt;&lt;span class="n"&gt;breadcrumb&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;
      &amp;lt;ol class="&lt;/span&gt;&lt;span class="n"&gt;inline&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;flex&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;center&lt;/span&gt; &lt;span class="n"&gt;space&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="ss"&gt;md:&lt;/span&gt;&lt;span class="n"&gt;space&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;
        &amp;lt;.breadcrumb_item
          :for={{item, index} &amp;lt;- Enum.with_index(@items)}
          type={index_to_item_type(index, @size)}
          navigate={item[:navigate]}
          text={item[:text]}
        /&amp;gt;
      &amp;lt;/ol&amp;gt;
    &amp;lt;/nav&amp;gt;
    """&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;index_to_item_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_size&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"first"&lt;/span&gt;
  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;index_to_item_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;when&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"last"&lt;/span&gt;
  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;index_to_item_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_size&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"middle"&lt;/span&gt;

  &lt;span class="n"&gt;attr&lt;/span&gt; &lt;span class="ss"&gt;:type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default:&lt;/span&gt; &lt;span class="s2"&gt;"middle"&lt;/span&gt;
  &lt;span class="n"&gt;attr&lt;/span&gt; &lt;span class="ss"&gt;:navigate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default:&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt;
  &lt;span class="n"&gt;attr&lt;/span&gt; &lt;span class="ss"&gt;:text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;required:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;breadcrumb_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;assigns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;when&lt;/span&gt; &lt;span class="n"&gt;assigns&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"first"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="sx"&gt;~H""&lt;/span&gt;&lt;span class="s2"&gt;"
    &amp;lt;li class="&lt;/span&gt;&lt;span class="n"&gt;inline&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;flex&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;center&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;
      &amp;lt;.link navigate={@navigate} class="&lt;/span&gt;&lt;span class="n"&gt;inline&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;flex&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;center&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sm&lt;/span&gt; &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;medium&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;
        &amp;lt;.icon name="&lt;/span&gt;&lt;span class="n"&gt;hero&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="s2"&gt;" class="&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="s2"&gt;" /&amp;gt;
      &amp;lt;/.link&amp;gt;
    &amp;lt;/li&amp;gt;
    """&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;breadcrumb_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;assigns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;when&lt;/span&gt; &lt;span class="n"&gt;assigns&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"last"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="sx"&gt;~H""&lt;/span&gt;&lt;span class="s2"&gt;"
    &amp;lt;li aria-current="&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;
      &amp;lt;div class="&lt;/span&gt;&lt;span class="n"&gt;flex&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;center&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;
        &amp;lt;.icon name="&lt;/span&gt;&lt;span class="n"&gt;hero&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;chevron&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="s2"&gt;" class="&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="s2"&gt;" /&amp;gt;
        &amp;lt;span class="&lt;/span&gt;&lt;span class="n"&gt;ml&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sm&lt;/span&gt; &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;medium&lt;/span&gt; &lt;span class="ss"&gt;md:&lt;/span&gt;&lt;span class="n"&gt;ml&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;
          &amp;lt;%= @text %&amp;gt;
        &amp;lt;/span&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/li&amp;gt;
    """&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;breadcrumb_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;assigns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="sx"&gt;~H""&lt;/span&gt;&lt;span class="s2"&gt;"
    &amp;lt;li&amp;gt;
      &amp;lt;div class="&lt;/span&gt;&lt;span class="n"&gt;flex&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;center&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;
        &amp;lt;.icon name="&lt;/span&gt;&lt;span class="n"&gt;hero&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;chevron&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="s2"&gt;" class="&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="s2"&gt;" /&amp;gt;
        &amp;lt;.link navigate={@navigate} class="&lt;/span&gt;&lt;span class="n"&gt;ml&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sm&lt;/span&gt; &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;medium&lt;/span&gt; &lt;span class="ss"&gt;md:&lt;/span&gt;&lt;span class="n"&gt;ml&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;
          &amp;lt;%= @text %&amp;gt;
        &amp;lt;/.link&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/li&amp;gt;
    """&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  only one public function
&lt;/h3&gt;

&lt;p&gt;The module has only one public function called &lt;code&gt;breadcrumb&lt;/code&gt;, which is a function component. For readability, I factored out a sub-component &lt;code&gt;breadcrumb_item&lt;/code&gt; that maps provided info to the breadcrumb item markup according to the breadcrumb item type.&lt;/p&gt;

&lt;h3&gt;
  
  
  three types of breadcrumb items
&lt;/h3&gt;

&lt;p&gt;Typically the breadcrumb navigation has three types of items and they have different looks and behaviors.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the leftmost

&lt;ul&gt;
&lt;li&gt;the home&lt;/li&gt;
&lt;li&gt;link enabled&lt;/li&gt;
&lt;li&gt;can look special with home icon&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;the rightmost

&lt;ul&gt;
&lt;li&gt;the current&lt;/li&gt;
&lt;li&gt;link disabled&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;the in-between

&lt;ul&gt;
&lt;li&gt;all other paths between the home and the current&lt;/li&gt;
&lt;li&gt;link enabled&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For each type, the sub-component &lt;code&gt;breadcrumb_item&lt;/code&gt; renders differently. We can easily determine the appropriate breadcrumb item type by the index and length of the list. The private function &lt;code&gt;index_to_item_type&lt;/code&gt; does the job.&lt;/p&gt;

&lt;p&gt;We need one preparation before rendering the component. The length of the list varies so we need to find out the list length beforehand.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hexdocs.pm/elixir/Enum.html#with_index/2" rel="noopener noreferrer"&gt;Enum.with_index/2&lt;/a&gt; gives an index to each element of the list. With that index and the pre-calculated list length, we can determine the item type.&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>phoenix</category>
      <category>tailwindcss</category>
      <category>breadcrumb</category>
    </item>
    <item>
      <title>tmux の コピペ と クリップボード</title>
      <dc:creator>Masatoshi Nishiguchi</dc:creator>
      <pubDate>Sun, 28 May 2023 12:56:06 +0000</pubDate>
      <link>https://dev.to/mnishiguchi/tmux-no-kopipe-to-kuritupubodo-2o82</link>
      <guid>https://dev.to/mnishiguchi/tmux-no-kopipe-to-kuritupubodo-2o82</guid>
      <description>&lt;h2&gt;
  
  
  はじめに
&lt;/h2&gt;

&lt;p&gt;古い macbook に &lt;a href="https://ja.wikipedia.org/wiki/Linux_Mint" rel="noopener noreferrer"&gt;Linux Mint&lt;/a&gt; 21 をインストールして復活させました。普段は仕事で macOS を使っているので、&lt;a href="https://ja.wikipedia.org/wiki/Linux_Mint" rel="noopener noreferrer"&gt;Linux Mint&lt;/a&gt; に行ったりきたりしやすいように工夫を試みています。その一環として可能な限り &lt;a href="https://ja.wikipedia.org/wiki/%E3%82%AA%E3%83%9A%E3%83%AC%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0" rel="noopener noreferrer"&gt;OS&lt;/a&gt; を問わずに共通の設定で &lt;a href="https://neovim.io" rel="noopener noreferrer"&gt;neovim&lt;/a&gt; と &lt;a href="https://github.com/tmux/tmux/wiki" rel="noopener noreferrer"&gt;tmux&lt;/a&gt; を使えるように取り組んでいます。今回は、「&lt;a href="https://github.com/tmux/tmux/wiki" rel="noopener noreferrer"&gt;tmux&lt;/a&gt; の コピペ と &lt;a href="https://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%AA%E3%83%83%E3%83%97%E3%83%9C%E3%83%BC%E3%83%89" rel="noopener noreferrer"&gt;クリップボード&lt;/a&gt;」について調べてわかったことをメモします。&lt;/p&gt;

&lt;p&gt;世の中にはいろんな &lt;a href="https://ja.wikipedia.org/wiki/%E3%82%AA%E3%83%9A%E3%83%AC%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0" rel="noopener noreferrer"&gt;OS&lt;/a&gt; が存在しますが、ここでは macOS と &lt;a href="https://ja.wikipedia.org/wiki/Linux_Mint" rel="noopener noreferrer"&gt;Linux Mint&lt;/a&gt; しか登場しません。&lt;a href="https://ja.wikipedia.org/wiki/Linux_Mint" rel="noopener noreferrer"&gt;Linux Mint&lt;/a&gt; の中身は &lt;a href="https://ja.wikipedia.org/wiki/Ubuntu" rel="noopener noreferrer"&gt;Ubuntu&lt;/a&gt; らしいので、&lt;a href="https://ja.wikipedia.org/wiki/Linux_Mint" rel="noopener noreferrer"&gt;Linux Mint&lt;/a&gt; は 多分 &lt;a href="https://ja.wikipedia.org/wiki/Ubuntu" rel="noopener noreferrer"&gt;Ubuntu&lt;/a&gt; と読み替えてもいいと思います。しらんけど。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft8biypnqsf3gjtwgtgfc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft8biypnqsf3gjtwgtgfc.jpg" alt="linux_mint_on_mac" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  クリップボード
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;クリップボード（英: clipboard）は、コンピュータ上で、一時的にデータを保存できる共有のメモリ領域のことである。複数の異なるプログラムからアクセス可能であり、単一のアプリケーションだけでなく異なったアプリケーション間のデータの受け渡しにも使用される。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%AA%E3%83%83%E3%83%97%E3%83%9C%E3%83%BC%E3%83%89" rel="noopener noreferrer"&gt;https://ja.wikipedia.org/wiki/クリップボード&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;日頃何も考えずに使っている&lt;a href="https://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%AA%E3%83%83%E3%83%97%E3%83%9C%E3%83%BC%E3%83%89" rel="noopener noreferrer"&gt;クリップボード&lt;/a&gt;ですが、実はこれは&lt;a href="https://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%AA%E3%83%83%E3%83%97%E3%83%9C%E3%83%BC%E3%83%89" rel="noopener noreferrer"&gt;クリップボード&lt;/a&gt;を操作するためのプログラムとアプリとがうまく連携していないとスムーズなコピペができないようです。&lt;/p&gt;

&lt;h2&gt;
  
  
  コピペのショートカット
&lt;/h2&gt;

&lt;p&gt;キーボードでコピペする技です。macOS でも Linux Mint でもアルファベットのキーは同じですが、一緒に押すキーが異なります。キーを変更することもできるのでしょうが、慣れた方が早い気がします。&lt;/p&gt;

&lt;h3&gt;
  
  
  macOS
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;やりたいこと&lt;/th&gt;
&lt;th&gt;ショートカット&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;切取&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;X&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;複写&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;C&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;貼付&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;⌘&lt;/code&gt; + &lt;code&gt;V&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://support.apple.com/ja-jp/HT209651" rel="noopener noreferrer"&gt;https://support.apple.com/ja-jp/HT209651&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Linux Mint
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://ja.wikipedia.org/wiki/Linux_Mint" rel="noopener noreferrer"&gt;Linux Mint&lt;/a&gt; の&lt;a href="https://ja.wikipedia.org/wiki/%E7%AB%AF%E6%9C%AB%E3%82%A8%E3%83%9F%E3%83%A5%E3%83%AC%E3%83%BC%E3%82%BF" rel="noopener noreferrer"&gt;ターミナル&lt;/a&gt;では &lt;code&gt;Shift&lt;/code&gt; を押さないといけないので要注意。最初は戸惑いますが、慣れれば大丈夫。郷に入れば郷に従います。&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;やりたいこと&lt;/th&gt;
&lt;th&gt;ショートカット&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;切取&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;X&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;複写&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;C&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;貼付&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;V&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ターミナルで 切取&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;Shift&lt;/code&gt; + &lt;code&gt;X&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ターミナルで 複写&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;Shift&lt;/code&gt; + &lt;code&gt;C&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ターミナルで 貼付&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;Shift&lt;/code&gt; + &lt;code&gt;V&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://qiita.com/mnishiguchi/items/94d3ab813fca051a0e54" rel="noopener noreferrer"&gt;https://qiita.com/mnishiguchi/items/94d3ab813fca051a0e54&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  コピペのコマンド
&lt;/h2&gt;

&lt;p&gt;プログラムからクリップボードを操作するためのコマンドが存在します。いい感じにまとまっている記事がありました。ありがとうございます。OS によって使用可能なプログラムが異なります。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/sasaplus1/items/137a70e8f51f97a6636f" rel="noopener noreferrer"&gt;https://qiita.com/sasaplus1/items/137a70e8f51f97a6636f&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  macOS
&lt;/h3&gt;

&lt;p&gt;macOS では コマンドが備えついているので特に悩むことはありません。&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;コマンド&lt;/th&gt;
&lt;th&gt;機能&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pbcopy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;標準出力を受け取ってクリップボードにコピー&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pbpaste&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;クリップボードのデータをターミナルに出力&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"闘魂"&lt;/span&gt; | pbcopy

pbpaste
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Linux Mint
&lt;/h3&gt;

&lt;p&gt;Linux Mint にはクリップボードを操作するコマンドを自分でインストールする必要があります。また、いくつか選択肢があります。中でも &lt;code&gt;xsel&lt;/code&gt; と &lt;code&gt;xclip&lt;/code&gt; がよく利用されている様子です。ネット検索をしていて、「両方入れた方がいいよ！」との話があったので、深く考えず両方インストールしました。アプリにより、相性があるのではないかと想像しています。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;xclip xsel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;両方とも機能は似ています。&lt;/p&gt;

&lt;h4&gt;
  
  
  コピー先
&lt;/h4&gt;

&lt;p&gt;ここで注意が必要なのがコピー先です。macOS のコマンドと異なり３種類あるので、クリップボードを操作するときにはコピー先がクリップボードと明示しておかないとどこにコピーしたのかわからなくなります。省略できるオプションもありますが、後々わかりやすいように明示しておくのが得策かもしれません。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clipboard&lt;/li&gt;
&lt;li&gt;primary&lt;/li&gt;
&lt;li&gt;secondary&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://wiki.archlinux.jp/index.php/%E3%82%AF%E3%83%AA%E3%83%83%E3%83%97%E3%83%9C%E3%83%BC%E3%83%89" rel="noopener noreferrer"&gt;https://wiki.archlinux.jp/index.php/クリップボード&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  xsel でコピペ
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"闘魂"&lt;/span&gt; | xsel &lt;span class="nt"&gt;--clipboard&lt;/span&gt; &lt;span class="nt"&gt;--input&lt;/span&gt;

xsel &lt;span class="nt"&gt;--clipboard&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;詳しくはドキュメントをご参照ください。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;man xsel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  xclip でコピペ
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"闘魂"&lt;/span&gt; | xclip &lt;span class="nt"&gt;-selection&lt;/span&gt; clipboard &lt;span class="nt"&gt;-in&lt;/span&gt;

xclip &lt;span class="nt"&gt;-selection&lt;/span&gt; clipboard &lt;span class="nt"&gt;-out&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;詳しくはドキュメントをご参照ください。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;man xclip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  neovim でクリップボードにヤンク（コピー）
&lt;/h2&gt;

&lt;p&gt;vim 用語で copy のことを yank と言います。neovim でコピペするためにはいくつか条件を満たす必要があるようです。以下の記事にヒントがあります。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/gotchane/items/0e7e6e0d5c7fa9f55c1a" rel="noopener noreferrer"&gt;https://qiita.com/gotchane/items/0e7e6e0d5c7fa9f55c1a&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                                  *clipboard-tool*
The presence of a working clipboard tool implicitly enables the '+' and '*'
registers. Nvim looks for these clipboard tools, in order of priority:

  - |g:clipboard|
  - pbcopy, pbpaste (macOS)
  - wl-copy, wl-paste (if $WAYLAND_DISPLAY is set)
  - xclip (if $DISPLAY is set)
  - xsel (if $DISPLAY is set)
  - lemonade (for SSH) https://github.com/pocke/lemonade
  - doitclient (for SSH) http://www.chiark.greenend.org.uk/~sgtatham/doit/
  - win32yank (Windows)
  - tmux (if $TMUX is set)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;neovim はリストの上から順にクリップボード用のプログラムを探すようです。環境変数に依存する部分があるのでそこも注意が必要です。&lt;/p&gt;

&lt;h2&gt;
  
  
  tmux のコピーモード
&lt;/h2&gt;

&lt;p&gt;tmux にはターミナル内で vim 風の操作をできるようにするコピーモードがあります。コピペできるだけでなくターミナル内を高速に移動できるようになり便利です。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/tmux/tmux/wiki/Getting-Started#copy-and-paste" rel="noopener noreferrer"&gt;https://github.com/tmux/tmux/wiki/Getting-Started#copy-and-paste&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;しかしながら、クリップボードとの連携に若干の癖がありました。しっかり準備をしないとイゴきません。&lt;/p&gt;

&lt;p&gt;いくつか参考になる記事がありましたが、当時の tmux から設定方法が一部変わっているので注意が必要です。アイデアをありがたく頂きつつ、最後は原典に当たるのが一番です。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;man tmux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;現時点（2023 年 5 月）で最新は tmux 3.3a です。tmux 3.2 から &lt;code&gt;copy-command&lt;/code&gt; オプションが導入されました。他にも書き方が変わっている部分がいくつかあります。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/shimmer22/items/67ba93060ae456aadd1b" rel="noopener noreferrer"&gt;https://qiita.com/shimmer22/items/67ba93060ae456aadd1b&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/iggredible/the-easy-way-to-copy-text-in-tmux-319g"&gt;https://dev.to/iggredible/the-easy-way-to-copy-text-in-tmux-319g&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/tmux/tmux/wiki/Clipboard" rel="noopener noreferrer"&gt;https://github.com/tmux/tmux/wiki/Clipboard&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;色々試行錯誤した結果、&lt;code&gt;~/tmux.conf&lt;/code&gt; の設定は以下の通りに落ち着きました。関連する部分を抜粋します。Linux Mint と macOS とでクリップボードのコマンドが違うので、条件分岐します。OS の種類の確認が厳密ではありませんが、自分用の設定なので問題ありません。&lt;/p&gt;

&lt;p&gt;Linux 用クリップボードのプログラムとして &lt;code&gt;xsel&lt;/code&gt; を使います。&lt;code&gt;xclip&lt;/code&gt; より &lt;code&gt;xsel&lt;/code&gt; の方が tmux との相性がいいようです。もちろん &lt;code&gt;xsel&lt;/code&gt; コマンドがインストールされていることが前提です。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;set-clipboard&lt;/code&gt; と &lt;code&gt;copy-pipe&lt;/code&gt; が競合する場合があるそうです。&lt;a href="https://github.com/tmux/tmux/wiki/Clipboard#set-clipboard-and-copy-pipe" rel="noopener noreferrer"&gt;ドキュメントの指示&lt;/a&gt;に従い &lt;code&gt;set-clipboard&lt;/code&gt; を無効にします。&lt;/p&gt;

&lt;p&gt;tmux のコピーモードの &lt;code&gt;mode-keys&lt;/code&gt; は &lt;code&gt;emacs&lt;/code&gt; か &lt;code&gt;vi&lt;/code&gt; か選択できます。ここでは &lt;code&gt;vi&lt;/code&gt; を使います。&lt;/p&gt;

&lt;p&gt;コピーモード（vi）の初期設定ではコピーは &lt;code&gt;Enter&lt;/code&gt; キーで実行するようになっています。Vimmer にとっては &lt;code&gt;y&lt;/code&gt; でヤンクする方が自然なのでそれができるようにカスタマイズします。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# マウス有効化
set -g mouse on

# コピーモード（vi）を有効化
set-window-option -g mode-keys vi

# OS が Linux の時は xsel を使う
if-shell -b '[ "$(uname)" = "Linux" ]' {
  set -s copy-command "xsel --clipboard --input"
  display "using xsel as copy-command"
}

# OS が Darwin の時は pbcopy を使う
if-shell -b '[ "$(uname)" = "Darwin" ]' {
  set -s copy-command "pbcopy"
  display "using pbcopy as copy-command"
}

# copy-pipe と競合する場合があるので無効化
set -s set-clipboard off

# コピーモード中に Vim 風に v で選択範囲を定める
bind -Tcopy-mode-vi v send -X begin-selection

# コピーモード中に Vim 風に y で選択範囲をヤンクしてコピーモードを終了する
bind -Tcopy-mode-vi y send -X copy-pipe-and-cancel

# マウスをドラッグして選択範囲を定め、それをヤンクしてコピーモードを終了する
bind -Tcopy-mode-vi MouseDragEnd1Pane send -X copy-pipe-and-cancel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;copy-command&lt;/code&gt; などの tmux の設定値を確認したい時は　&lt;code&gt;tmux show&lt;/code&gt; が便利です。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tmux show copy-command
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;コピーモードのショートカットの初期設定は &lt;code&gt;man tmux&lt;/code&gt; を打てば出てきます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The following commands are supported in copy mode:

           Command                                      vi              emacs
           append-selection
           append-selection-and-cancel                  A
           back-to-indentation                          ^               M-m
           begin-selection                              Space           C-Space
           bottom-line                                  L
           cancel                                       q               Escape
           clear-selection                              Escape          C-g
           copy-end-of-line [&amp;lt;prefix&amp;gt;]
           copy-end-of-line-and-cancel [&amp;lt;prefix&amp;gt;]
           copy-pipe-end-of-line [&amp;lt;command&amp;gt;] [&amp;lt;prefix&amp;gt;]
           copy-pipe-end-of-line-and-cancel [&amp;lt;command&amp;gt;] [&amp;lt;prefix&amp;gt;]
                                                        D               C-k
           copy-line [&amp;lt;prefix&amp;gt;]
           copy-line-and-cancel [&amp;lt;prefix&amp;gt;]
           copy-pipe-line [&amp;lt;command&amp;gt;] [&amp;lt;prefix&amp;gt;]
           copy-pipe-line-and-cancel [&amp;lt;command&amp;gt;] [&amp;lt;prefix&amp;gt;]

           copy-pipe [&amp;lt;command&amp;gt;] [&amp;lt;prefix&amp;gt;]
           copy-pipe-no-clear [&amp;lt;command&amp;gt;] [&amp;lt;prefix&amp;gt;]
           copy-pipe-and-cancel [&amp;lt;command&amp;gt;] [&amp;lt;prefix&amp;gt;]
           copy-selection [&amp;lt;prefix&amp;gt;]
           copy-selection-no-clear [&amp;lt;prefix&amp;gt;]
           copy-selection-and-cancel [&amp;lt;prefix&amp;gt;]         Enter           M-w
           cursor-down                                  j               Down
           cursor-down-and-cancel
           cursor-left                                  h               Left
           cursor-right                                 l               Right
           cursor-up                                    k               Up
           end-of-line                                  $               C-e
           goto-line &amp;lt;line&amp;gt;                             :               g
           halfpage-down                                C-d             M-Down
           halfpage-down-and-cancel
           halfpage-up                                  C-u             M-Up
           history-bottom                               G               M-&amp;gt;
           history-top                                  g               M-&amp;lt;
           jump-again                                   ;               ;
           jump-backward &amp;lt;to&amp;gt;                           F               F
           jump-forward &amp;lt;to&amp;gt;                            f               f
           jump-reverse                                 ,               ,
           jump-to-backward &amp;lt;to&amp;gt;                        T
           jump-to-forward &amp;lt;to&amp;gt;                         t
           jump-to-mark                                 M-x             M-x
           middle-line                                  M               M-r
           next-matching-bracket                        %               M-C-f
           next-paragraph                               }               M-}
           next-space                                   W
           next-space-end                               E
           next-word                                    w
           next-word-end                                e               M-f
           other-end                                    o
           page-down                                    C-f             PageDown
           page-down-and-cancel
           page-up                                      C-b             PageUp
           pipe [&amp;lt;command&amp;gt;] [&amp;lt;prefix&amp;gt;]
           pipe-no-clear [&amp;lt;command&amp;gt;] [&amp;lt;prefix&amp;gt;]
           pipe-and-cancel [&amp;lt;command&amp;gt;] [&amp;lt;prefix&amp;gt;]
           previous-matching-bracket                                    M-C-b
           previous-paragraph                           {               M-{
           previous-space                               B
           previous-word                                b               M-b
           rectangle-on
           rectangle-off
           rectangle-toggle                             v               R
           refresh-from-pane                            r               r
           scroll-down                                  C-e             C-Down
           scroll-down-and-cancel
           scroll-up                                    C-y             C-Up
           search-again                                 n               n
           search-backward &amp;lt;for&amp;gt;                        ?
           search-backward-incremental &amp;lt;for&amp;gt;                            C-r
           search-backward-text &amp;lt;for&amp;gt;
           search-forward &amp;lt;for&amp;gt;                         /
           search-forward-incremental &amp;lt;for&amp;gt;                             C-s
           search-forward-text &amp;lt;for&amp;gt;
           search-reverse                               N               N
           select-line                                  V
           select-word
           set-mark                                     X               X
           start-of-line                                0               C-a
           stop-selection
           toggle-position                              P               P
           top-line                                     H               M-R
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  さいごに
&lt;/h2&gt;

&lt;p&gt;最終的にコンパクトにまとまって満足しています。&lt;a href="https://ja.wikipedia.org/wiki/Linux_Mint" rel="noopener noreferrer"&gt;Linux Mint&lt;/a&gt; がどんどん好きになってきました。&lt;/p&gt;

&lt;h2&gt;
  
  
  モクモク會
&lt;/h2&gt;

&lt;p&gt;本記事は以下のモクモク會での成果です。みなさんから刺激と元氣をいただき、ありがとうございました。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/c0LP23SM7BU" rel="noopener noreferrer"&gt;https://youtu.be/c0LP23SM7BU&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://okazakirin-beam.connpass.com/" rel="noopener noreferrer"&gt;https://okazakirin-beam.connpass.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://autoracex.connpass.com" rel="noopener noreferrer"&gt;https://autoracex.connpass.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;もしご興味のある方はお氣輕にご參加ください。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/piacerex/items/09876caa1e17169ec5e1" rel="noopener noreferrer"&gt;https://qiita.com/piacerex/items/09876caa1e17169ec5e1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://speakerdeck.com/elijo/elixirkomiyunitei-falsebu-kifang-guo-nei-onrainbian" rel="noopener noreferrer"&gt;https://speakerdeck.com/elijo/elixirkomiyunitei-falsebu-kifang-guo-nei-onrainbian&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/torifukukaiou/items/57a40119c9eefd056cae" rel="noopener noreferrer"&gt;https://qiita.com/torifukukaiou/items/57a40119c9eefd056cae&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/piacerex/items/e0b6e46b1325bb931122" rel="noopener noreferrer"&gt;https://qiita.com/piacerex/items/e0b6e46b1325bb931122&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/torifukukaiou/items/1edb3e961acf002478fd" rel="noopener noreferrer"&gt;https://qiita.com/torifukukaiou/items/1edb3e961acf002478fd&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/piacerex/items/e5590fa287d3c89eeebf" rel="noopener noreferrer"&gt;https://qiita.com/piacerex/items/e5590fa287d3c89eeebf&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/torifukukaiou/items/4481f7884a20ab4b1bea" rel="noopener noreferrer"&gt;https://qiita.com/torifukukaiou/items/4481f7884a20ab4b1bea&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://note.com/awesomey/n/n4d8c355bc8f7" rel="noopener noreferrer"&gt;https://note.com/awesomey/n/n4d8c355bc8f7&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp2vsp42waha8vwe96e4m.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp2vsp42waha8vwe96e4m.jpeg" width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>ubuntu</category>
      <category>tmux</category>
      <category>neovim</category>
    </item>
    <item>
      <title>Elixirで進捗表示ダウンロード</title>
      <dc:creator>Masatoshi Nishiguchi</dc:creator>
      <pubDate>Mon, 22 May 2023 02:08:47 +0000</pubDate>
      <link>https://dev.to/mnishiguchi/elixirdejin-bu-biao-shi-daunrodo-3pj4</link>
      <guid>https://dev.to/mnishiguchi/elixirdejin-bu-biao-shi-daunrodo-3pj4</guid>
      <description>&lt;p&gt;Elixirで進捗状況を表示しながらダウンロードする方法について検討します。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://livebook.dev/run?url=https%3A%2F%2Fgithub.com%2Fmnishiguchi%2Flivebooks%2Fblob%2Fmain%2Fnotebooks%2Fdownloader.livemd" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flivebook.dev%2Fbadge%2Fv1%2Fblue.svg" alt="Run in Livebook" width="170" height="40"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  やりたいこと
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/elixir-nx/bumblebee" rel="noopener noreferrer"&gt;Bumblebee&lt;/a&gt; を使っているときにファイルをダウンロードするとこういうダウンロード進捗表示がでます。これをやってみたいです。コードは自分で書きます。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc5pq2wdlz4mzkn13z65q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc5pq2wdlz4mzkn13z65q.png" width="758" height="64"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bumblebeeのコード
&lt;/h2&gt;

&lt;p&gt;プライベートの &lt;a href="https://github.com/elixir-nx/bumblebee/blob/776e57c6b6d06c0fed47afa26d8144c7c2541149/lib/bumblebee/utils/http.ex#L26" rel="noopener noreferrer"&gt;Bumblebee.Utils.HTTP&lt;/a&gt; モジュールにダウンロード関連のコードがありました。&lt;/p&gt;

&lt;p&gt;Erlang の &lt;a href="https://www.erlang.org/doc/man/httpc.html" rel="noopener noreferrer"&gt;httpc&lt;/a&gt; モジュールと &lt;a href="https://github.com/henrik/progress_bar" rel="noopener noreferrer"&gt;ProgressBar&lt;/a&gt; パッケージを使って実装されています。&lt;/p&gt;

&lt;p&gt;ちなみに &lt;a href="https://www.erlang.org/doc/man/httpc.html" rel="noopener noreferrer"&gt;httpc&lt;/a&gt; の使い方はElixir Forum にまとめられています。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://elixirforum.com/t/httpc-cheatsheet/50337" rel="noopener noreferrer"&gt;https://elixirforum.com/t/httpc-cheatsheet/50337&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;同じように &lt;a href="https://www.erlang.org/doc/man/httpc.html" rel="noopener noreferrer"&gt;httpc&lt;/a&gt; モジュールを使って実装しても良いのですが、個人的に日頃よく利用する &lt;a href="https://github.com/wojtekmach/req" rel="noopener noreferrer"&gt;Req&lt;/a&gt; を使って１から実装してみようと思います。&lt;/p&gt;

&lt;p&gt;まずは、 &lt;a href="https://github.com/wojtekmach/req" rel="noopener noreferrer"&gt;Req&lt;/a&gt; をつかって簡単なGETリクエストする方法から始めます。ここではElixir のロゴの画像データをダウンロードの対象とします。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;source_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://elixir-lang.org/images/logo/logo.png"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fropy4q75v8ygycoau2k9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fropy4q75v8ygycoau2k9.png" width="450" height="188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reqをつかって進捗表示なしにダウンロード
&lt;/h2&gt;

&lt;p&gt;まずは、 &lt;a href="https://github.com/wojtekmach/req" rel="noopener noreferrer"&gt;Req&lt;/a&gt; をつかって進捗表示なしにダウンロードしてみます。&lt;br&gt;
&lt;br&gt;
　&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# データとしてダウンロード&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;binary&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;ローカルファイルとして保存したい場合は &lt;code&gt;:output&lt;/code&gt; オプションで保存先を指定します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;destination_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tmp_dir!&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s2"&gt;"elixir_logo.png"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ダウンロードしてファイルに保存&lt;/span&gt;
&lt;span class="no"&gt;Req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;output:&lt;/span&gt; &lt;span class="n"&gt;destination_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ちゃんと読み込めるか検証&lt;/span&gt;
&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;destination_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;進捗表示を追加するにはどうしたら良いのでしょうか。&lt;a href="https://github.com/elixir-nx/bumblebee" rel="noopener noreferrer"&gt;Bumblebee&lt;/a&gt; のコードから &lt;a href="https://github.com/henrik/progress_bar" rel="noopener noreferrer"&gt;ProgressBar&lt;/a&gt; パッケージを利用できることはすでにわかっています。それをどのように &lt;a href="https://github.com/wojtekmach/req" rel="noopener noreferrer"&gt;Req&lt;/a&gt; と連携させるかを調べます。&lt;/p&gt;

&lt;h2&gt;
  
  
  Req の構成要素（3 つ）
&lt;/h2&gt;

&lt;p&gt;Req は 3 つの主要部分で構成されています。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Req - 高階層のAPI&lt;/li&gt;
&lt;li&gt;Req.Request - 低階層のAPIとリクエスト構造体&lt;/li&gt;
&lt;li&gt;Req.Steps - ひとつひとつの処理&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;カスタマイズは比較的容易にできそうです。&lt;/p&gt;

&lt;h2&gt;
  
  
  Req.Steps.run_finch/1
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://hexdocs.pm/req/Req.Steps.html#run_finch/1" rel="noopener noreferrer"&gt;Req.Steps.run_finch/1&lt;/a&gt; に手を加えることにより、リクエストのロジックを変更できることがわかりました。ドキュメントにわかりにくい部分がありますが、サンプルコードを読んでみて高階層のAPIに  &lt;code&gt;:finch_request&lt;/code&gt; オプションに関数を注入して &lt;a href="https://hexdocs.pm/req/Req.Steps.html#run_finch/1" rel="noopener noreferrer"&gt;Req.Steps.run_finch/1&lt;/a&gt; ステップを入れ替えることができるようです。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/sneako/finch" rel="noopener noreferrer"&gt;Finch&lt;/a&gt; とは 初期設定の &lt;a href="https://github.com/wojtekmach/req" rel="noopener noreferrer"&gt;Req&lt;/a&gt; が依存するHTTPクライアントだそうです。さらに &lt;a href="https://github.com/sneako/finch" rel="noopener noreferrer"&gt;Finch&lt;/a&gt; は &lt;a href="https://github.com/elixir-mint/mint" rel="noopener noreferrer"&gt;Mint&lt;/a&gt; と &lt;a href="https://github.com/dashbitco/nimble_pool" rel="noopener noreferrer"&gt;NimblePool&lt;/a&gt; を使って性能を意識して実装されているそうです。&lt;/p&gt;

&lt;p&gt;余談ですが、Elixirの関数に「闘魂」を注入する方法については以下の@torifukukaiouさんの記事がおすすめです。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/torifukukaiou/items/c414310cde9b7099df55" rel="noopener noreferrer"&gt;https://qiita.com/torifukukaiou/items/c414310cde9b7099df55&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reqをつかって進捗表示付きダウンロードしてみる
&lt;/h2&gt;

&lt;p&gt;このような形になりました。ポイントをいくつかあげます。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://hexdocs.pm/req/Req.html#get/2" rel="noopener noreferrer"&gt;Req.get/2&lt;/a&gt; に &lt;code&gt;:finch_request&lt;/code&gt; オプションとしてリクエストを処理するカスタムロジック（関数）を注入します。&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hexdocs.pm/finch/Finch.html#stream/5" rel="noopener noreferrer"&gt;Finch.stream/5&lt;/a&gt; でリクエストの多重化が可能です。ストリームという概念に疎いので 「&lt;a href="https://gihyo.jp/magazine/wdpress/archive/2021/vol123" rel="noopener noreferrer"&gt;WEB+DB PRESS Vol.１２３&lt;/a&gt;」 を読み返しました。「イーチ、ニィー、サン、ぁッ ダー！！！」&lt;/li&gt;
&lt;li&gt;ストリームからは3パターンのメッセージが返ってくるようです。

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;{:status, status}&lt;/code&gt; - the status of the http response&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{:headers, headers}&lt;/code&gt; - the headers of the http response&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{:data, data}&lt;/code&gt; - a streaming section of the http body&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;進捗表示に必要な情報はふたつ。

&lt;ul&gt;
&lt;li&gt;データ全体のバイト数&lt;/li&gt;
&lt;li&gt;受信完了したバイト数&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;進捗状況は記憶しておく必要があるので、&lt;a href="https://hexdocs.pm/req/Req.Response.html#t:t/0" rel="noopener noreferrer"&gt;Req.Response&lt;/a&gt; の &lt;code&gt;:private&lt;/code&gt; フィールドに格納し、データを受信するたびに更新します。
&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MNishiguchi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Utils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;HTTP&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;download&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req_options&lt;/span&gt; &lt;span class="p"&gt;\\&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;Req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;finch_request:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;finch_request&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="n"&gt;req_options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;download!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req_options&lt;/span&gt; &lt;span class="p"&gt;\\&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;finch_request:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;finch_request&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="n"&gt;req_options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;finch_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req_request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;finch_request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;finch_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;finch_options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;acc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;Finch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;finch_request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;finch_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;handle_message&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;finch_options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;req_request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;req_request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;handle_message&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="ss"&gt;status:&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;handle_message&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="ss"&gt;:headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;total_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"content-length"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_v&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:private&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;total_size:&lt;/span&gt; &lt;span class="n"&gt;total_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;downloaded_size:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;handle_message&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="ss"&gt;:data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;new_downloaded_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;private&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;downloaded_size&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;byte_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;ProgressBar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_downloaded_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;private&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;suffix:&lt;/span&gt; &lt;span class="ss"&gt;:bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:private&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="ss"&gt;downloaded_size:&lt;/span&gt; &lt;span class="n"&gt;new_downloaded_size&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;以上のコードを IEx でランしてみます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;MNishiguchi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Utils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;download!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|===&lt;/span&gt;                                                                               &lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.36&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;34.95&lt;/span&gt; &lt;span class="no"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|=======&lt;/span&gt;                                                                           &lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2.73&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;34.95&lt;/span&gt; &lt;span class="no"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|==========&lt;/span&gt;                                                                        &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;4.10&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;34.95&lt;/span&gt; &lt;span class="no"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|=============&lt;/span&gt;                                                                     &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;5.47&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;34.95&lt;/span&gt; &lt;span class="no"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|================&lt;/span&gt;                                                                  &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;6.84&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;34.95&lt;/span&gt; &lt;span class="no"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|===================&lt;/span&gt;                                                               &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;8.20&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;34.95&lt;/span&gt; &lt;span class="no"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|======================&lt;/span&gt;                                                            &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;9.57&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;34.95&lt;/span&gt; &lt;span class="no"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|=========================&lt;/span&gt;                                                        &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;10.94&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;34.95&lt;/span&gt; &lt;span class="no"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|============================&lt;/span&gt;                                                     &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;12.31&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;34.95&lt;/span&gt; &lt;span class="no"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|===================================&lt;/span&gt;                                              &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="mi"&gt;43&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;15.04&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;34.95&lt;/span&gt; &lt;span class="no"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|======================================&lt;/span&gt;                                           &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;16.41&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;34.95&lt;/span&gt; &lt;span class="no"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|=========================================&lt;/span&gt;                                        &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="mi"&gt;51&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;17.78&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;34.95&lt;/span&gt; &lt;span class="no"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|=============================================&lt;/span&gt;                                    &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;19.15&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;34.95&lt;/span&gt; &lt;span class="no"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|================================================&lt;/span&gt;                                 &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;20.52&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;34.95&lt;/span&gt; &lt;span class="no"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|===================================================&lt;/span&gt;                              &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="mi"&gt;63&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;21.88&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;34.95&lt;/span&gt; &lt;span class="no"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|======================================================&lt;/span&gt;                           &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="mi"&gt;67&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;23.25&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;34.95&lt;/span&gt; &lt;span class="no"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|=========================================================&lt;/span&gt;                        &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;24.62&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;34.95&lt;/span&gt; &lt;span class="no"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|============================================================&lt;/span&gt;                     &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="mi"&gt;74&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;25.99&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;34.95&lt;/span&gt; &lt;span class="no"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|===============================================================&lt;/span&gt;                  &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;27.36&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;34.95&lt;/span&gt; &lt;span class="no"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|==================================================================&lt;/span&gt;               &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="mi"&gt;82&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;28.72&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;34.95&lt;/span&gt; &lt;span class="no"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|======================================================================&lt;/span&gt;           &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="mi"&gt;86&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;30.09&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;34.95&lt;/span&gt; &lt;span class="no"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|=========================================================================&lt;/span&gt;        &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;31.46&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;34.95&lt;/span&gt; &lt;span class="no"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|============================================================================&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="mi"&gt;94&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;32.83&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;34.95&lt;/span&gt; &lt;span class="no"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|=======================================================================================|&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;34.95&lt;/span&gt; &lt;span class="no"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Livebook でやるともっといい感じに進捗状況が更新されるはずです。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://livebook.dev/run?url=https%3A%2F%2Fgithub.com%2Fmnishiguchi%2Flivebooks%2Fblob%2Fmain%2Fnotebooks%2Fdownloader.livemd" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flivebook.dev%2Fbadge%2Fv1%2Fblue.svg" alt="Run in Livebook" width="170" height="40"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bumblebee.Utils.HTTP.download/2
&lt;/h2&gt;

&lt;p&gt;Bumblebee を使っているのであれば、&lt;code&gt;Bumblebee.Utils.HTTP.download/2&lt;/code&gt; で同じようなことができます。ドキュメントには載ってませんが利用可能です。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="no"&gt;Bumblebee&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Utils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;download&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;destination_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Nerves Livebook
&lt;/h2&gt;

&lt;p&gt;せっかくいい感じのコードが書けたので Nerves Livebook に寄贈いたしました。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/livebook-dev/nerves_livebook/blob/9515bd61b4da6b30c6165b33f9a0ae56880ddc44/priv/samples/tflite.livemd" rel="noopener noreferrer"&gt;https://github.com/livebook-dev/nerves_livebook/blob/9515bd61b4da6b30c6165b33f9a0ae56880ddc44/priv/samples/tflite.livemd&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Elixirコミュニティ
&lt;/h2&gt;

&lt;p&gt;本記事は以下のモクモク會での成果です。みなさんから刺激と元氣をいただき、ありがとうございました。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/c0LP23SM7BU" rel="noopener noreferrer"&gt;https://youtu.be/c0LP23SM7BU&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://okazakirin-beam.connpass.com/" rel="noopener noreferrer"&gt;https://okazakirin-beam.connpass.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://autoracex.connpass.com" rel="noopener noreferrer"&gt;https://autoracex.connpass.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;もしご興味のある方はお氣輕にご參加ください。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/piacerex/items/09876caa1e17169ec5e1" rel="noopener noreferrer"&gt;https://qiita.com/piacerex/items/09876caa1e17169ec5e1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://speakerdeck.com/elijo/elixirkomiyunitei-falsebu-kifang-guo-nei-onrainbian" rel="noopener noreferrer"&gt;https://speakerdeck.com/elijo/elixirkomiyunitei-falsebu-kifang-guo-nei-onrainbian&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/torifukukaiou/items/57a40119c9eefd056cae" rel="noopener noreferrer"&gt;https://qiita.com/torifukukaiou/items/57a40119c9eefd056cae&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/piacerex/items/e0b6e46b1325bb931122" rel="noopener noreferrer"&gt;https://qiita.com/piacerex/items/e0b6e46b1325bb931122&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/torifukukaiou/items/1edb3e961acf002478fd" rel="noopener noreferrer"&gt;https://qiita.com/torifukukaiou/items/1edb3e961acf002478fd&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/piacerex/items/e5590fa287d3c89eeebf" rel="noopener noreferrer"&gt;https://qiita.com/piacerex/items/e5590fa287d3c89eeebf&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/torifukukaiou/items/4481f7884a20ab4b1bea" rel="noopener noreferrer"&gt;https://qiita.com/torifukukaiou/items/4481f7884a20ab4b1bea&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://note.com/awesomey/n/n4d8c355bc8f7" rel="noopener noreferrer"&gt;https://note.com/awesomey/n/n4d8c355bc8f7&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp2vsp42waha8vwe96e4m.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp2vsp42waha8vwe96e4m.jpeg" width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>erlang</category>
      <category>闘魂</category>
      <category>猪木</category>
    </item>
    <item>
      <title>Elixir OpenCV: StbImageとNx.Tensor と Evision.Mat で相互変換</title>
      <dc:creator>Masatoshi Nishiguchi</dc:creator>
      <pubDate>Thu, 04 May 2023 02:23:10 +0000</pubDate>
      <link>https://dev.to/mnishiguchi/elixir-opencv-stbimagetonxtensor-to-evisionmat-dexiang-hu-bian-huan-o19</link>
      <guid>https://dev.to/mnishiguchi/elixir-opencv-stbimagetonxtensor-to-evisionmat-dexiang-hu-bian-huan-o19</guid>
      <description>&lt;p&gt;Elixir で &lt;a href="https://ja.wikipedia.org/wiki/OpenCV" rel="noopener noreferrer"&gt;OpenCV&lt;/a&gt; を使うときにはどのような素材をどこから取ってきてどう加工するかによって求められる関数が異なり、場合により複数のパッケージが連携する必要があるかと思います。&lt;/p&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/ryowakabayashi"&gt;@ryowakabayashi&lt;/a&gt; さんの&lt;a href="https://qiita.com/RyoWakabayashi/items/34de2207bcf8d745a01a" rel="noopener noreferrer"&gt;Elixir Image と Nx と evision で相互変換&lt;/a&gt; が大変参考になりました。&lt;/p&gt;

&lt;p&gt;ここでは &lt;a href="https://github.com/elixir-image/image" rel="noopener noreferrer"&gt;elixir-image/image&lt;/a&gt; よりシンプルな &lt;a href="https://github.com/elixir-nx/stb_image" rel="noopener noreferrer"&gt;elixir-nx/stb_image&lt;/a&gt; と &lt;a href="https://github.com/cocoa-xu/evision" rel="noopener noreferrer"&gt;cocoa-xu/evision&lt;/a&gt; で相互変換をしてみようと思います。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/RyoWakabayashi/items/34de2207bcf8d745a01a" rel="noopener noreferrer"&gt;https://qiita.com/RyoWakabayashi/items/34de2207bcf8d745a01a&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  セットアップ
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="no"&gt;Mix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:nx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 0.5.0"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:evision&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"0.1.31"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:stb_image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 0.6.0"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="c1"&gt;# 画像を Web からダウンロードするため&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 0.3.0"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="c1"&gt;# Livebook上で画像を表示するため&lt;/span&gt;
  &lt;span class="c1"&gt;# {:kino, "~&amp;gt; 0.9.0"}&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/cocoa-xu/evision" rel="noopener noreferrer"&gt;cocoa-xu/evision&lt;/a&gt; は &lt;a href="https://github.com/cocoa-xu/evision/blob/main/CHANGELOG.md" rel="noopener noreferrer"&gt;パッチバージョンでも破壊的な変更&lt;/a&gt;があるようのでバージョンをロックしておいた方が無難そうです。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/RyoWakabayashi/items/2846133bc7014319d172" rel="noopener noreferrer"&gt;https://qiita.com/RyoWakabayashi/items/2846133bc7014319d172&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/cocoa-xu/evision" rel="noopener noreferrer"&gt;cocoa-xu/evision&lt;/a&gt; の作者は好んで &lt;code&gt;Cv&lt;/code&gt; エイリアスを使用しているのでそれに則ります。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/cocoa-xu/evision/tree/main/examples" rel="noopener noreferrer"&gt;https://github.com/cocoa-xu/evision/tree/main/examples&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="no"&gt;Evision&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;as:&lt;/span&gt; &lt;span class="no"&gt;Cv&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  画像加工の一例
&lt;/h2&gt;

&lt;p&gt;一例として画像をこのように加工することができます。ここで &lt;code&gt;StbImage&lt;/code&gt; と &lt;code&gt;Nx.Tensor&lt;/code&gt; と &lt;code&gt;Evision.Mat&lt;/code&gt; で相互変換がされています。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;img_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://upload.wikimedia.org/wikipedia/commons/thumb/6/65/Antonio_Inoki_IMG_0398-2_20121224.JPG/330px-Antonio_Inoki_IMG_0398-2_20121224.JPG"&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;rect_start_point&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;rect_end_point&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;rect_color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;rect_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;thickness:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="no"&gt;Req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;StbImage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read_binary!&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;StbImage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_nx&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Cv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Mat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_nx_2d&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Cv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cvtColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Cv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Constant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cv_COLOR_BGR2RGB&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Cv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rectangle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rect_start_point&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rect_end_point&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rect_color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rect_options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;こういう書き方もできそうです。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;img_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://upload.wikimedia.org/wikipedia/commons/thumb/6/65/Antonio_Inoki_IMG_0398-2_20121224.JPG/330px-Antonio_Inoki_IMG_0398-2_20121224.JPG"&lt;/span&gt;
&lt;span class="n"&gt;save_as&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tmp_dir!&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode_www_form&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img_url&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;rect_start_point&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;rect_end_point&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;rect_color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;rect_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;thickness:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="no"&gt;Req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;output:&lt;/span&gt; &lt;span class="n"&gt;save_as&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Cv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;imread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;save_as&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Cv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rectangle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rect_start_point&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rect_end_point&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rect_color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rect_options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyho5r8lheusi1119vygr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyho5r8lheusi1119vygr.png" alt="アントニオ猪木" width="330" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;image creadit: &lt;a href="https://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%B3%E3%83%88%E3%83%8B%E3%82%AA%E7%8C%AA%E6%9C%A8" rel="noopener noreferrer"&gt;https://ja.wikipedia.org/wiki/アントニオ猪木&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  画像の読込
&lt;/h2&gt;

&lt;p&gt;画像はバイナリとして読み込む場合もあれば、ファイルを読み込む場合もあると思います。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Req.get!/2&lt;/code&gt; を用いて Web から画像バイナリをダウンロードすることができます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;img_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://upload.wikimedia.org/wikipedia/commons/thumb/6/65/Antonio_Inoki_IMG_0398-2_20121224.JPG/330px-Antonio_Inoki_IMG_0398-2_20121224.JPG"&lt;/span&gt;

&lt;span class="n"&gt;img_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="no"&gt;Req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;また、画像をファイルとしてローカルに保存したい場合があるかもしれません。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;download&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="n"&gt;save_as&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tmp_dir!&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode_www_form&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="no"&gt;Req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;output:&lt;/span&gt; &lt;span class="n"&gt;save_as&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;save_as&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;img_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;download&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Binary -&amp;gt; StbImage
&lt;/h2&gt;

&lt;p&gt;画像バイナリの読み込みには &lt;code&gt;StbImage.read_binary!/1&lt;/code&gt; が手軽で便利です。&lt;code&gt;StbImage&lt;/code&gt; 構造体が帰ります。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;img_stb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;StbImage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read_binary!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  File -&amp;gt; Evision.Mat
&lt;/h2&gt;

&lt;p&gt;ローカルに保存された画像ファイルを読み込む場合は &lt;code&gt;Cv.imread/1&lt;/code&gt; が使えます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;img_mat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Cv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;imread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  StbImage &amp;lt;-&amp;gt; Nx.Tensor
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;StbImage&lt;/code&gt; は簡単に &lt;code&gt;Nx.Tensor&lt;/code&gt; へ変換できます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;img_nx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;StbImage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_nx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img_stb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;逆も簡単です。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;img_stb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;StbImage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_nx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img_nx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Nx.Tensor &amp;lt;-&amp;gt; Evision.Mat
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Nx.Tensor&lt;/code&gt; から &lt;code&gt;Evision.Mat&lt;/code&gt; への変換も基本的には同様にできるのですが、ここでは注意が必要です。2点あります。&lt;/p&gt;

&lt;h3&gt;
  
  
  RGB vs BGR
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;OpenCVは色チャネルをB（青）・G（緑）・R（赤）の順に保持しています。ですので、適切な場所で B と R 入れ替える必要があります。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Evision.Mat.from_nx vs Evision.Mat.from_nx_2d
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;これは正直いうとよくわかりませんが、2Dの画像を取り扱う場合は &lt;code&gt;from_nx_2d&lt;/code&gt; を使わないとうまく処理ができないようです。OpenCV の関数を使って画像加工しても何も起こらない場合はここを見落としている可能性があります。エラーがでないので厄介です。
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;img_nx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="n"&gt;img_mat&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Cv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cvtColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Cv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Constant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cv_COLOR_BGR2RGB&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Cv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Mat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_nx&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;img_mat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="n"&gt;img_nx&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Cv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Mat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_nx_2d&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Cv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cvtColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Cv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Constant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cv_COLOR_BGR2RGB&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Evision.Mat &amp;lt;-&amp;gt; StbImage
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Evision.Mat&lt;/code&gt; と &lt;code&gt;StbImage&lt;/code&gt; との間では直接の変換はできませんが、&lt;code&gt;Nx.Tensor&lt;/code&gt; を介在させることにより実現可能です。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;img_mat&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Cv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cvtColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Cv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Constant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cv_COLOR_BGR2RGB&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Cv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Mat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_nx&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;StbImage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_nx&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;img_stb&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;StbImage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_nx&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Cv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Mat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_nx_2d&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Cv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cvtColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Cv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Constant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cv_COLOR_BGR2RGB&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  アルファチャネルを取り除く変換
&lt;/h2&gt;

&lt;p&gt;場合によりアルファチャネルを取り除きたい場合があると思いますが、現時点では &lt;code&gt;Evision.Backend&lt;/code&gt; はスライス構文に対応していないようです。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="no"&gt;Cv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Mat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_nx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img_mat&lt;/span&gt;&lt;span class="p"&gt;)[[&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;

&lt;span class="c1"&gt;# ** (RuntimeError) operation slice is not yet supported on Evision.Backend.&lt;/span&gt;
&lt;span class="c1"&gt;# Please use another backend like Nx.BinaryBackend or Torchx.Backend.&lt;/span&gt;
&lt;span class="c1"&gt;#   To use Torchx.Backend, :torchx should be added to your app's deps.&lt;/span&gt;
&lt;span class="c1"&gt;#   Please see &amp;lt;https://github.com/elixir-nx/nx/tree/main/torchx&amp;gt; for more information on how to install and use it.&lt;/span&gt;
&lt;span class="c1"&gt;# To convert the tensor to another backend, please use Evision.Mat.to_nx(tensor, Backend.ModuleName)&lt;/span&gt;
&lt;span class="c1"&gt;#   for example, Evision.Mat.to_nx(tensor, Nx.BinaryBackend) or Evision.Mat.to_nx(tensor, Torchx.Backend).&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Evison.Mat&lt;/code&gt; を &lt;code&gt;Nx.Tensor&lt;/code&gt; に変換するときにバックエンドをしていするとうまくいきます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="no"&gt;Cv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Mat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_nx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img_mat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Nx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;BinaryBackend&lt;/span&gt;&lt;span class="p"&gt;)[[&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>elixir</category>
      <category>opencv</category>
      <category>nx</category>
      <category>inoki</category>
    </item>
  </channel>
</rss>
