3 Simple examples from Ruby to Elixir

In this post we’re gonna see how to transform a simple script from Ruby to Elixir.

Installing Elixir

The first thing you need is to have Elixir installed on your box, the instructions are dead simple and you can
find them in the official Getting Started page.
For example on OS X is as simple as brew update; brew install elixir.

The Ruby Script

The script is the one I use to fire up my editor adding support for the file:line:column
format that is often found in error stacktraces. I keep this script in ~/bin/e.

#!/usr/bin/env ruby

command = ['mate']

if ARGV.first
  file, line_and_column = ARGV.first.split(':', 2)

  command << file
  command += ['-l', line_and_column] if line_and_column
command << '.' if command.size == 1
exec *command

Take 1: Imperative Elixir

As we all know, no matter the language, you can keep your old style. In this first example we’ll see the same

#!/usr/bin/env elixir

if System.argv != [] do
  [file| line_and_column] = String.split(hd(System.argv), ":", parts: 2)
  args = [file]

  if line_and_column != [] do
    args = args ++ ["-l"| line_and_column]
  args = ["."]
System.cmd("mate", args)

The “Guillotine” operator

The first thing we notice the change in syntax for the splat assignment:

# Ruby
a, b = [1,2,3]
a # => 1
b # => 2
<pre><code class="elixir"># Elixir

[a| b] = [1,2,3]
a # => 1
b # => [2,3]

The | operator in Elixir will in fact take out the head of the list and leave the rest on its right.
It can be used multiple times:

[a| [b| c]] = [1,2,3]
a # => 1
b # => 2
c # => [3]

what happens here is that the list that in the first example was b is now beheaded again.
If instead we wanted c to equal 3 the assignment would look like this:

[a| [b| [c]]] = [1,2,3]
a # => 1
b # => 2
c # => 3

As we can see Elixir matches the form of the two sides of the assignments and extracts values and variables accordingly.

Other notes

Let’s see a couple of other things that we can learn in this simple example

List concatenation: ++

The ++ operator simply concatenates two lists:

a = [1,2] ++ [3,4]
a # => [1,2,3,4]

Double quoted "strings"

All strings need to be double quoted in Elixir, as single quotes are reserved for other uses.
I make the mistake of using single quotes all the time. Probably that’s the price for being a
ROFLScale expert.

Take 2: First steps in Pattern Matching

With this second version we’re gonna see the pattern matched case.

Notice anything?

Yes. All ifs are gone.

#!/usr/bin/env elixir

args = System.argv
args = case args do
  [] -> []
  [""] -> []
  [path] -> String.split(path, ":", parts: 2)

args = case args do
  [] -> ["."]
  [file] -> [file]
  [file, ""] -> [file]
  [file, line_and_column] -> [file, "-l", line_and_column]

System.cmd("mate", args)

We now reduced the whole program to a couple of switches that will route the input and transform it
towards the intended result.

That’s it. No highlights for this implementation. Just a LOLCAT.

cat getting scared for no reason

Take 3: Modules and pipes

<pre><code class="elixir">#!/usr/bin/env elixir

defmodule Mate do
def open(argv), do: System.cmd(“mate”, argv |> parse_argv)

def parse_argv([]), do: [“.”]
def parse_argv([options]) do
[file| line_and_column] = String.split(options, “:”, parts: 2)
[file| line_and_column |> line_option]

def line_option([]), do: []
def line_option([“”]), do: []
def line_option([line_and_column]), do: [“-l”, line_and_column]

Mate.open System.argv

Module and defs

As you have seen we have now organized out code into a module and moved stuff to defined module
functions. The same function can be defined multiple times, Elixir will take care of matching the arguments
you pass to a function to the right.

Let’s review the two forms of function definition:

defmodule Greetings do
  # extended
  def hello(name) do
    IO.inspect("hello #{name}")

  # onliner
  def hello(), do: IO.inspect("hello world!")

Greetings.hello "ppl" # => "hello ppl"
Greetings.hello       # => "hello world!"

Be sure to remember the comma before do: otherwise Elixir will complaint.

The |> pipe operator

If you’re familiar with commandline piping you’ll fell like at home with the pipe operator.
Basically it will take the result of each expression and pass it as the first argument of the next one.

Let’s see an example:

"hello world" |> String.capitalize |> IO.inspect # => "Hello world"

That is just the same of:

s = "hello world"
s = String.capitalize(s)
s = IO.inspect(s)
s # => "Hello world"


IO.inspect(String.capitalize("hello world")) # => "Hello world"

Where the latter is probably the least comprehensible to human eyes

What’s next?




Pimp my TextMate2 — Ruby edition

Here at Mikamai It’s no secret I’m a happy TextMate user and early TM2 adopter. I’m always there if either an editor war is catching fire or if someone needs help setting up his editor.

Above all I still find that TextMate is the best choice for a Ruby developer, even if SublimeText, emacs and vim seem more fashionable these days. Even if I’m saving the full list of reasons for another post I’ll tell just this one: TextMate relies on Ruby for a big part of its implementation that has always been opensource*.

Of course I’m talking about bundles, if you’re not convinced look at the code used to align assignments (⌥⌘]) from the source bundle (which is responsible for actions common to any programming language).

Just to be clear I would still use SublimeText if I were to program from Linux or (ugh!) Windows.

That said I want to gather here some of the stuff that makes using TextMate2 for Ruby and Rails development so awesome.

* Of course I know about redcar (which seems quite dead, but I didn’t tried it recently) and other TM clones err enhancements like Chocolat

ALERT: shameless self promotion follows

The Bundles and Settings parade

1. Effortless opening of Bundled Gems ⌥⌘O

This I do all the time, opening the source of bundled gems. Please behold and don’t be horrified. Especially in Ruby-land the source of gems is the best source of documentation, and as explained by Glenn Vanderburg) there’s probably a good reason for that. Also the README and specs are included most of the time and reading other’s code is a healthy activity.

Needless to say that the best place to read source code is your editor.

⌥⌘O will present the complete list of gems from your Gemfile.lock, start typing the first letters of a gem and use arrow if you don’t want to touch the mouse (or trackpad).

opening gems from the current bundle

Source: https://github.com/elia/bundler.tmbundle

2. Beautiful Markdown rendering

No README.md reading activity would be on par with a the GFM rendered version without code blocks highlighting.

This bundle almost looks like GFM while typing, press ⌃⌥⌘P (the standard TM key equivalent for preview) to get it rendered to the HTML window.

Redcarpet Markdown Bundle in action

Bonus Install the Scott Web Theme from Preferences → Bundles for a nice looking preview

Source: https://github.com/streeter/markdown-redcarpet.tmbundle

3. Restart Pow! in a single stroke ⌃⌥⌘R

Restarts the current app detecting a tmp/ directory in current project or in a parent dir.

Falls back to /tmp

Source: https://github.com/elia/pow-server.tmbundle

4. Open the terminal in your current project folder

Works with both Terminal and iTerm, just press ⌃⌥⌘T from a project.

Source: https://github.com/elia/avian-missing.tmbundle

5. Trailing whitespace fix, cross-tab completion and more…

Command Description
⌃⎋ Cross tab completion
⌃⌥⌘T Open Project directory in Terminal
⌃⌥⌘L Keep current file as reference

Source: https://github.com/elia/avian-missing.tmbundle

Installing the whole thing

Download the latest version of TextMate2 here: https://api.textmate.org/downloads/release

mkdir -p ~/Library/Application Support/Avian/Bundles
cd ~/Library/Application Support/Avian/Bundles

git clone https://github.com/elia/avian-missing.tmbundle
git clone https://github.com/elia/bundler.tmbundle
git clone https://github.com/streeter/markdown-redcarpet.tmbundle

# Activate the system ruby (if you're using a Ruby version manager):
type rvm &> /dev/null && rvm use system # for RVM
export RBENV_VERSION="system"           # for rbenv

# Install the required gems
sudo gem install redcarpet -v 2.3.0
sudo gem install pygments.rb

# Trailing whitespace
defaults write com.macromates.TextMate.preview environmentVariables -array-add 
    '{ "enabled" = YES; "name" = "TM_STRIP_WHITESPACE_ON_SAVE"; "value" = "true"; }' # enable trailing whitespace removal and EOF fix

echo <<-INI >> ~/.tm_properties
[ "*.y{,a}ml" ]
# Disable trailing whitespace fix for YAML 
# files that can be broken by this feat.

The following will set the tabs/file-browser/html-windows to my current taste, I don’t pretend it matches everyone prefs but can still be useful for cherry-picking.

# File browser fixes and general UI fixes
defaults write com.macromates.TextMate.preview fileBrowserStyle SourceList       # lighblue file browser background
defaults write com.macromates.TextMate.preview fileBrowserPlacement left         # keep it on the left
defaults write com.macromates.TextMate.preview tabsAboveDocument -bool YES       # no tabs above the file browser
defaults write com.macromates.TextMate.preview allowExpandingLinks -bool YES     # make symlinks expandable
defaults write com.macromates.TextMate.preview htmlOutputPlacement right         # place the html output to the right
defaults write com.macromates.TextMate.preview disableTabBarCollapsing -bool YES # keep the tab-bar alway visible
defaults write com.macromates.TextMate.preview scrollPastEnd -bool YES           # give me some air after the file ends