I had the privilege of hanging around Brooklin Myers before he joined DockYard as an instructor in early 2022. A unique Elixir community slowly coalesced with the first video of the beta cohort starting on September 21st, 2022. I wouldn't join the group until October 15th as I wasn't sure what to make of it at first. I figured I would audit the class like I was some college kid.
The academy skews toward junior developers or other Elixir newbies without previous formal instruction. Despite that, the curriculum and the commitment of 2 hours per day was an exceptional resource regardless of experience level.
The curriculum is not as lightweight as Elixir koans, and it is not as self-paced as Exercism's Elixir track. I hadn't been a part of the Exercism Elixir cohort on Discord, but I suspect it may have been similar.
What sets the curriculum apart is that it starts in Livebook, a low barrier to entry for learning Elixir.
Eventually, it moves to bare mix new
projects, graduating to full-on mix phx.new
Phoenix applications.
The beta curriculum experience was different than the first cohort, and there are upcoming changes for the second cohort.
It's helpful to know the curriculum changes when pain points surface.
There is no sleight of hand or abandonware as the official repository is what is taught from start to finish.
As someone that can have analysis paralysis at times when it comes to what and how to learn, having the path chosen for me was extremely helpful. Exercism gates the syllabus, but that can be daunting to decipher when you're starting. I also rushed through the concepts I was interested in rather than taking the time to enjoy the journey. I firmly believe the curriculum and Exercism complement each other very well.
The curriculum culminated in a capstone project, a chance to bundle all the skills we learned to produce our applications. The capstone sets it apart from other learning materials.
The beta cohort was a mix of Elixir newbies, seasoned Elixir developers and mentors, and people that hadn't touched a programming language. We experimented with teaching styles and nailed a cadence "locked in" at the last minute. Everyone I paired with showed remarkable improvement between October and the demo day on January 20th. That level of improvement is a testament to Brooklin's teaching style. Fundamentals became second nature very quickly. I would be lucky to work with anybody I met in the cohort or Discord server, as everyone grew into a developer. Elixir has a way of binding cohesive communities, but Brooklin truly has his superpower with the people around him. As much as I love DockYard, this felt like "The Brooklin Show" *sponsored by DockYard(tm)
I was one of the fewer resident developers to present on Demo Day, and that almost didn't happen. My capstone project, Beatseek, was hastily thrown together by duct tape.
I had a working prototype at least a month before the deadline, but I had only given myself ten days from mix phx.new
to what I presented.
I thought it went well without a script, working through some prior presentations, but it was unpolished.
I used sleight of hand as I do on some demos, but as a magician, I wanted to show all the tricks.
I didn't cut a public release until two months after demo day because I wasn't happy with what I produced. I had to retrofit tests, which exposed several shortcomings. If I had to do it again, I would choose anything other than id3 tags because the edge cases are absurdly complex.
I had a few issues working through the curriculum or with other cohort members. Tracking progress was difficult, but I used an Obsidian daily standup journal template to check off the table of contents manually. The standup journal became a good way of tracking changes over time, though there were few. The ramp-up to Phoenix for people with no web development or API exposure was pretty steep for the beta cohort, but I don't know if this is still true. Web development fundamentals span a breadth of knowledge, but the curriculum helps cement these concepts. People new to web development may wish to spend more time going through the same sections a few times until the concepts of things like MVC are less foreign. It'll make the later parts much easier to push through.
I am 100% glad I had access to an instructor and mentor, even in a limited capacity. Everyone on the Discord server is excellent and a joy to be around. I would do this again in a heartbeat, but 2 hours was a sweet spot for someone like me with a full-time position to juggle. I can see how much more beneficial the 6-hour full day could be with more immersion, but that is a lot of material to cram. We had some luxury in drawing the material out and taking some time to keep everyone on the same pace.
There are certain instances where you may want to build ElixirLS to run against the version of Elixir/Erlang that you're using. The recommendation from the package is:
If you're packaging these archives in an IDE plugin, make sure to build using the minimum supported OTP version for the best backward-compatibility If you're like me, you may not care to support older versions of Elixir. How do we configure the plugin to run the latest version?
The output I see in VSCode's Output tab (Shift-Command-U on macOS) for the ElixirLS extension:
[Info - 4:33:53 PM] Started ElixirLS v0.13.0
[Info - 4:33:53 PM] ElixirLS built with elixir "1.12.3" on OTP "22"
[Info - 4:33:53 PM] Running on elixir "1.14.2 (compiled with Erlang/OTP 25)" on OTP "25"
[Info - 4:33:53 PM] Elixir sources not found (checking in /home/build/elixir). Code navigation to Elixir modules disabled.
[Info - 4:33:54 PM] Loaded DETS databases in 32ms
[Info - 4:33:54 PM] Starting build with MIX_ENV: test MIX_TARGET: host
[Info - 4:33:55 PM] Compile took 854 milliseconds
There are numerous articles on building from source. What if we'd prefer to build the extension instead?
Let's unpack that Docker command to perform each step:
git clone --recursive --branch v0.13.0 https://github.com/elixir-lsp/vscode-elixir-ls.git /tmp/vscode-elixir-ls
.cd /tmp/vscode-elixir-ls
.npm install
.elixir-ls
directory: cd elixir-ls
.mix deps.get
.cd ..
.npx vsce package
.extensions
directory in $HOME: mkdir -p $HOME/extensions
.cp /tmp/vscode-elixir-ls/elixir-ls-0.13.0.vsix $HOME/extensions
.rm -rf /tmp/vscode-elixir-ls
.It is crucial to install Elixir v1.14.x and Erlang 25.1.x using your favorite method prior to packaging the new extension. I'm using asdf global
to do this, but you could create a local .tool-versions
inside the tmp folder if you wish.
The extension should now live at /tmp/vscode-elixir-ls/elixir-ls-0.13.0.vsix
.
The remaining steps copy the package to a directory the Docker container knows, and it's okay to stop here.
Because the prepublish.bash
file that executes at step #7 runs mix deps.get
, we can eliminate steps 4, 5, and 6.
These commands also compile the extension using MIX_ENV=dev
, which we may not want.
To change this, we can edit the last line in prepublish.bash
to MIX_ENV=prod mix elixir_ls.release -o ../elixir-ls-release
to compile for production.
Putting all of the (now reduced) commands together:
git clone --recursive --branch v0.13.0 https://github.com/elixir-lsp/vscode-elixir-ls.git /tmp/vscode-elixir-ls
cd /tmp/vscode-elixir-ls
npm install
npx vsce package
mkdir -p $HOME/extensions
cp /tmp/vscode-elixir-ls/elixir-ls-0.13.0.vsix $HOME/extensions
rm -rf /tmp/vscode-elixir-ls
We can install the extension from the VSIX file using the UI or the command code --install-extension $HOME/extensions/elixir-ls-0.13.0.vsix
.
To take advantage of the new extension in our projects, we need to rm -rf .elixir_ls
and navigate to an Elixir file.
ElixirLS won't start compiling until an Elixir file is open in the editor, and it'll usually take a few minutes to rebuild everything.
With the new extension installed we should see the change in VSCode's Output tab:
[Info - 4:35:42 PM] Started ElixirLS v0.13.0
[Info - 4:35:43 PM] ElixirLS built with elixir "1.14.2" on OTP "25"
[Info - 4:35:43 PM] Running on elixir "1.14.2 (compiled with Erlang/OTP 25)" on OTP "25"
[Info - 4:35:43 PM] Elixir sources not found (checking in /home/build/elixir). Code navigation to Elixir modules disabled.
[Info - 4:35:48 PM] Loaded DETS databases in 414ms
[Info - 4:35:48 PM] Starting build with MIX_ENV: test MIX_TARGET: host
[Info - 4:35:49 PM] Compile took 1811 milliseconds
I upgraded the excellent asdf
version manager using Homebrew and ran into a snag when trying to perform mix
commands.
I encounterd the error /Users/jbrayton/.asdf/shims/mix: line 13: /usr/local/Cellar/asdf/0.10.2/libexec/bin/asdf: No such file or directory
.
The key to notice here is the path /usr/local/Cellar/asdf/0.10.2/
when the newest version is 0.11.0
, as there is clearly a mismatch.
I restarted my terminal and shell, but the problem persisted. I noticed all the files in ~/.asdf/shims
had the line exec /usr/local/Cellar/asdf/0.10.2/libexec/bin/asdf exec "odbcserver" "$@" # asdf_allow: ' asdf '
.
This line is not what we wanted and indicates the problem.
After looking at the pinned https://github.com/asdf-vm/asdf/issues/785 and then following that to https://github.com/asdf-vm/asdf/issues/1393, the solution rm -rf ~/.asdf/shims; asdf reshim
fixes my problem.
Now, whenever I examine one of the shim files, I see the line exec /usr/local/opt/asdf/libexec/bin/asdf exec "mix" "$@" # asdf_allow: ' asdf '
as expected.
The directory /usr/local/opt
is what I see when I run the command brew --prefix asdf
as the prefix is no longer /usr/local/Cellar/asdf/0.10.2/
or the Cellar location.
This corrective measure should be a more permanent solution moving forward as the prefix /usr/local/opt
should no longer change in the future.
This issue was also somewhat of a perfect storm as Phoenix 1.7 rc.1 dropped two days ago and I had just upgraded a bunch of homebrew packages, including asdf
.
Recently, after a brief outage at work, I wondered if it would be possible to replicate the problem locally using Laravel Valet. My Google search landed on this StackOverflow post, where the answers shot down the idea. Not to be dissuaded by something I read on the internet, I started investigating if it was possible and stumbled upon what I think is a viable solution. There aren't very many hoops to jump through or major quirks so I believe it's not only possible but could be supported out of the box.
In my case, I want to proxy the domain scdn-app.thinkorange.com
through my local version of the Laravel application.
~/.config/valet/config.json
on macOS and change the tld
parameter from test
to com
.valet link scdn-app.thinkorange
to set up our valet configuration to point the domain to this directory.valet secure scdn-app.thinkorange
to set up the SSL certificate.cd ~/.config/valet/dnsmasq.d
.cp tld-test.conf tld-com.conf
.address=/.com/127.0.0.1
and save the file.valet isolate --site scdn-app.thinkorange php@8.1
./etc/hosts
file to redirect the domain to 127.0.0.1
for ipv4 and ::1
. I use the excellent Gas Mask to make this step easier.Now we should have a functional production proxy through our local machine. This configuration creates a few problems around keeping the com
TLD.
Fortunately, a few extra steps are necessary for us to switch back to .test
while also keeping this site functional.
~/.config/valet/config.json
again and change the tld
parameter from com
back to test
. This change will immediately break our site.cd ~/.config/valet/Sites
.ls -al
to list the directory, we'll see our site scdn-app.thinkorange
. Let's change that.mv scdn-app.thinkorange scdn-app.thinkorange.com
.Our site should now be working again. We are also able to continue serving our previous local test domains.
Because we can create a permanently functional system using these steps, I believe it should be possible to create a pull request to reduce the number of hoops we have to jump through.
I'd love to be able to run valet link scdn-app.thinkorange.com.
with a period at the end to denote I'm including the full domain with TLD.
That would eliminate the temporary step of editing the config.json
file, and the Sites
directory would just work(TM) as it would include the .com
directory name.
I don't believe we even need the dnsmasq changes as I'm able to navigate to a functional site without them.
I believe Gas Mask is doing the work, but it's better to be safe than sorry.
If you'd prefer a YouTube video where I stumble through recreating these steps from scratch:
Tell me if you've done this before. You write up a nice little prototype of an idea in Livebook. You then get distracted by life situations like eating, writing an email, or taking a nap. You feel the need to close Livebook or prune the multiple sessions you've had running for weeks now. Because you have a million tabs open (with a session manager) and too many in Livebook to individually check, you restart your computer and let it crash(TM). When you open up Livebook again, "Oh. Shiiiiit" you exclaim. Where the hell did that notebook go? I'm 100% sure I clicked the disk icon, what the hell? If you're like me, you may have created this forked Livebook from memory, possibly taking a better approach.
There is a better way to handle this scenario. Livebook has had autosaves since 0.4:
The feature was added in this PR according to the changelog:
https://github.com/livebook-dev/livebook/pull/736
To find your autosave files:
For the Desktop application and CLI in production: ~/Library/Application Support/livebook/autosaved/
.
/Users/jbrayton/Library/Application Support/livebook/autosaved/
.For the dev environment: in config/dev.exs
, this is set as config :livebook, :data_path, Path.expand("tmp/livebook_data/dev"
.
/Users/Shared/repositories/personal/elixir/livebook/tmp/livebook_data/dev/autosaved/
.For the test environment: in config/test.exs
this is set as Path.expand("tmp/livebook_data/test")
.
/Users/Shared/repositories/personal/elixir/livebook/tmp/livebook_data/test/autosaved/
.Notebooks are saved by day in the autosave directory and the date corresponds to when they were created (when you immediately click the New notebook button).
To view or change your autosave directory in the CLI:
Settings
under the Home
and Learn
links.For the Desktop application, the port will be randomized but you can either change the URL to tack on /settings
after the port or click around to the settings page as described earlier.
If you are curious as to how this setting gets configured, we can start by looking at Livebook.Settings.default_autosave_path()
in https://github.com/livebook-dev/livebook/blob/main/lib/livebook/settings.ex#L32-L34.
We follow Livebook.Config.data_path()
to https://github.com/livebook-dev/livebook/blob/main/lib/livebook/config.ex#L76-L78 then the Erlang function :filename.basedir(:user_data, "livebook")
.
Running this in Livebook we get the output "/Users/jbrayton/Library/Application Support/livebook"
, precisely where the desktop app stores its files.
What lead me to this discovery, after vaguely remembering autosave was a thing, was looking for files on my computer.
I purposefully install and use the locate
command because I find it far easier to use than remembering the find -name
syntax.
Here's the output for checking that the word autosave
is in any directory or file name:
⋊> ~ locate autosaved/
/Users/Shared/repositories/personal/elixir/livebook/tmp/livebook_data/dev/autosaved/2022_10_31/18_25_03_mapset_drills_hedh.livemd
/Users/Shared/repositories/personal/elixir/livebook/tmp/livebook_data/dev/autosaved/2022_11_03/18_12_21_teller_bank_challenge_pv4e.livemd
/Users/Shared/repositories/personal/elixir/livebook/tmp/livebook_data/dev/autosaved/2022_11_03/18_13_39_untitled_notebook_pidb.livemd
/Users/Shared/repositories/personal/elixir/livebook/tmp/livebook_data/dev/autosaved/2022_11_03/19_31_57_dockyard_academy_amas_p75r.livemd
/Users/Shared/repositories/personal/elixir/livebook/tmp/livebook_data/dev/autosaved/2022_11_03/20_02_17_intro_to_timescale_jm7r.livemd
/Users/Shared/repositories/personal/elixir/livebook/tmp/livebook_data/dev/autosaved/2022_11_08/11_10_21_untitled_notebook_ervg.livemd
/Users/Shared/repositories/personal/elixir/livebook/tmp/livebook_data/dev/autosaved/2022_11_22/19_15_12_untitled_notebook_p75e.livemd
What I found interesting was that my files in ~/Library/Application Support/livebook/autosaved/
did not show up.
Had I not realized there could be different locations, I may have overlooked the notebook I was looking for all along.
I have no clue why locate
doesn't scour the directories in ~/Library
it should have access to but that's a problem for another day.