Happy with Hex is my Credo

The use case

A ticket system should be able to receive email and store the email messages including attachments in the appropriate ticket.

I use email to register at Facebook, Twitter, Slack, Instagram and Skype.
Call me old fashioned, but I want to be able to sent email to a helpdesk, even though emails are not secure. I don’t want to tweet them, or login to their quirky support website. Just sent email.

So, surely Elixir or Erlang must have a module to receive email.

1309207482-300px

When I google with keywords like Elixir or Mix, I not always get the desired results. Search for Elixir potion, and you’ll see what I mean. So, after a while I stopped doing that, and just relied on hex, the package manager for the Erlang ecosystem.

Generally, I first search in elixir-lang then hex.pm, then erldocs and then I give up.

When I searched for mail I found a lot of projects for sending mail, and a few server-side components, but no POP3 nor IMAP client for reading mail.
The friendly people on Slack helped me out; I should use Google. Look here it is: erlpop. Mmm, the last update was 8 years ago.Would that still work?

Well, POP3 hasn’t changed much.I-Love-80s-100px

POP3 originates from 1988.

But Erlang has changed and; NO the POP3 client doesn’t work anymore.face-sad-50px

However, look more carefully. In Github in the upper right corner:Schermafdruk van 2016-05-30 21:55:18
It says that there are 11 forks of this repository.

You can see them if you click on the small number. DON’T PUSH THE FORK BUTTON! Happened to me so many times. The fork button instantly creates a new fork of the current repository. And only via the Github’s Danger Zone you will be able to delete this fork.

One of the forks seemed to be the latest and greatest. It worked and I played a bit with the epop_client. It was not completely what I hoped for. In many cases the emails I receive consisting of multipart content with  base64 or quoted printable encoded text, and the epop client does not decode the body.

I searched for solutions and I found mimemail, but I am not smart enough to get it to work. iconv, used for character conversions doesn’t seem to be standard available in Erlang.

So, I thought:

Let’s write some Elixir code.

How hard could it be? Well, harder than I first thought. I had to cope with RFC822, RFC2045, RFC2047, RFC2231. Multipart content can be nested, and it can be mixed, alternative and relative.

I also learned a few things about Gmail. Google doesn’t like to throw away private information, so it turned out that it had kept all my emails. In this case that was very useful as a testset.

I few things where surprisingly easy:

  • The epop client is not in hex, but mix can handle git(hub) dependencies. Just add the dependency and mix downloads and compiles the Erlang code for me.
  • Publishing my package on hex worked as advertised. Just follow the simple instructions.

So, here it is:

pop3mail

I learned a lot about Elixir:

  • It can handle binaries and bits (which I used in the quoted-printable decoder)
  • There are many Erlang functions that only accept character lists and not strings. Always check the documentation.
  • Single quoted text is a character list. All list functions work on it.
  • Strings contain utf-8 characters and are binaries.
    So, single quoted text are lists, double quoted text are binaries.
    lists are concatenated with ++
    binaries are concatenated with <>
  • Not all binaries are utf-8 strings. If it contains displayable chars, IO.inspect will display a string. If you concat <<0>> to the string, IO.inspect will display the values of the binary.
    Many Elixir modules require utf-8 strings and can fail if you give them binary content.
  • Regular expressions work nicely with unicode if you simple add the /u flag
  • Regex functions are not suitable for Elixir pipes because they use the regular expression as first parameter. However, the String module match/replace/split methods also accept regular expressions and do have the target as first parameter.
  • You cannot change values from within a try-block, but the try-block has a return value. See: scoping rules

 

My code is reviewed by Credo. It’s like having an annoying smart colleague:

  • Credo warns me if I have used CamelCase again instead of snake_case.
  • Tells me that my functions have too many parameters.
  • Tells me that I write too complex functions.
  • That I shouldn’t nest my if-statements that deep.

 

I am very pleased with Mix, Hex and Credo.

 

 

Decompile .beam files to Erlang

What is going on? What did Elixir with my code?

Laserbeams

It’s good to know what is going on inside Elixir. What is the Erlang code that Elixir generates? Where is it? Well, I couldn’t find it, but the end result are beam files, and beam files can be decompiled easily to Erlang files.

I found this decompiler on stackoverflow, and I tried it on a HelloWorld module. You could think that Elixir translates IO.puts into io:fwrite but that is not the case. Instead, it calls the puts function from the Elixir IO module. This means that the Elixir modules are essential.

In general I found the Erlang code easy to compare with original Elixir code.

First I called the decompiler from the Elixir shell, but soon I got tired of typing and found a way to call it directly from the command line:

erl -pa . -run utility decompile ./Elixir.HelloWorld.beam -run init stop -noshell

This is the decompiler, utility.erl:

%% Author: PCHAPIER
%% Created: 25 mai 2010
-module(utility).

%%
%% Include files
%%

%%
%% Exported Functions
%%
-export([decompile/1, decompdir/1]).

-export([shuffle/1]).


%%
%% API Functions
%%

decompdir(Dir) ->
    Cmd = "cd " ++ Dir,
    os:cmd(Cmd),
    L = os:cmd("dir /B *.beam"),
    L1 = re:split(L,"[\t\r\n+]",[{return,list}]),
    io:format("decompdir: ~p~n",[L1]),
    decompile(L1).


decompile(Beam = [H|_]) when is_integer(H) ->
    io:format("decompile: ~p~n",[Beam]),
    {ok,{_,[{abstract_code,{_,AC}}]}} = beam_lib:chunks(Beam ++ ".beam",[abstract_code]),
    {ok,File} = file:open(Beam ++ ".erl",[write]),
    io:fwrite(File,"~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]),
    file:close(File);

decompile([H|T]) ->
    io:format("decompile: ~p~n",[[H|T]]),
    decompile(removebeam(H)),
    decompile(T);

decompile([]) ->
    ok.

shuffle(P) ->
    Max = length(P)*10000,
    {_,R}= lists:unzip(lists:keysort(1,[{random:uniform(Max),X} || X <- P])),
    R.



%%
%% Local Functions
%%
removebeam(L) ->
    removebeam1(lists:reverse(L)).

removebeam1([$m,$a,$e,$b,$.|T]) ->
    lists:reverse(T);
removebeam1(L) ->
    lists:reverse(L).

And below you can see how to use it:

nico@nico-ubuntu:~/elixir/adhoc_scripts$ cat hello_world.ex 

defmodule HelloWorld do
  def main(_) do
    IO.puts "hello, world"
  end
end

nico@nico-ubuntu:~/elixir/adhoc_scripts$ elixirc hello_world.ex 
nico@nico-ubuntu:~/elixir/adhoc_scripts$ ls -l Elix*
-rw-rw-r-- 1 nico nico 1352 jan 10 20:25 Elixir.HelloWorld.beam
nico@nico-ubuntu:~/elixir/adhoc_scripts$ ls -l utility.erl
-rw-rw-r-- 1 nico nico 1172 dec 11 23:05 utility.erl
nico@nico-ubuntu:~/elixir/adhoc_scripts$ erlc utility.erl 
nico@nico-ubuntu:~/elixir/adhoc_scripts$ iex
Erlang/OTP 18 [erts-7.2]  [smp:4:4] [async-threads:10] [kernel-poll:false]

Interactive Elixir (1.2.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> :utility.decompile(['Elixir.HelloWorld'])
decompile: ["Elixir.HelloWorld"]
decompile: "Elixir.HelloWorld"
:ok
iex(2)> 
BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
       (v)ersion (k)ill (D)b-tables (d)istribution
a
nico@nico-ubuntu:~/elixir/adhoc_scripts$ cat Elixir.HelloWorld.erl 
-compile(no_auto_import).

-file("hello_world.ex", 2).

-module('Elixir.HelloWorld').

-export(['__info__'/1, main/1]).

-spec({{'__info__', 1},
       [{type, 2, 'fun',
	 [{type, 2, product,
	   [{type, 2, union,
	     [{atom, 2, attributes}, {atom, 2, compile},
	      {atom, 2, exports}, {atom, 2, functions},
	      {atom, 2, macros}, {atom, 2, md5}, {atom, 2, module},
	      {atom, 2, native_addresses}]}]},
	  {type, 2, union,
	   [{type, 2, atom, []},
	    {type, 2, list,
	     [{type, 2, union,
	       [{type, 2, tuple,
		 [{type, 2, atom, []}, {type, 2, any, []}]},
		{type, 2, tuple,
		 [{type, 2, atom, []}, {type, 2, byte, []},
		  {type, 2, integer, []}]}]}]}]}]}]}).

'__info__'(functions) -> [{main, 1}];
'__info__'(macros) -> [];
'__info__'(info) ->
    erlang:get_module_info('Elixir.HelloWorld', info).

main(_) -> 'Elixir.IO':puts(<<"hello, world">>).
nico@nico-ubuntu:~/elixir/adhoc_scripts$ locate Elixir.IO.beam
/usr/local/lib/elixir/lib/elixir/ebin/Elixir.IO.beam
nico@nico-ubuntu:~/elixir/adhoc_scripts$ rm Elixir.HelloWorld.erl 
nico@nico-ubuntu:~/elixir/adhoc_scripts$ erl
Erlang/OTP 18 [erts-7.2]  [smp:4:4] [async-threads:10] [kernel-poll:false]

Eshell V7.2  (abort with ^G)
1> utility:decompile(["Elixir.HelloWorld"]).
decompile: ["Elixir.HelloWorld"]
decompile: "Elixir.HelloWorld"
ok
2> 
BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
       (v)ersion (k)ill (D)b-tables (d)istribution
a
nico@nico-ubuntu:~/elixir/adhoc_scripts$ rm Elixir.HelloWorld.erl
nico@nico-ubuntu:~/elixir/adhoc_scripts$ erl -pa . -run utility decompile ./Elixir.HelloWorld.beam -run init stop -noshell
decompile: ["./Elixir.HelloWorld.beam"]
decompile: "./Elixir.HelloWorld"
nico@nico-ubuntu:~/elixir/adhoc_scripts$ cd ../grep_templates
nico@nico-ubuntu:~/elixir/grep_templates$ cp ../adhoc_scripts/utility.erl .
nico@nico-ubuntu:~/elixir/grep_templates$ erlc utility.erl 
nico@nico-ubuntu:~/elixir/grep_templates$ find _build/test -name '*.beam'
_build/test/lib/grep_templates/ebin/Elixir.GrepTemplates.beam
nico@nico-ubuntu:~/elixir/grep_templates$ find _build/test -name '*.beam' | xargs echo | xargs -i erl -pa . -run utility decompile {} -run init stop -noshell
decompile: ["_build/test/lib/grep_templates/ebin/Elixir.GrepTemplates.beam"]
decompile: "_build/test/lib/grep_templates/ebin/Elixir.GrepTemplates"
nico@nico-ubuntu:~/elixir/grep_templates$ find _build/test -name '*.erl' -ls
505873    4 -rw-rw-r--   1 nico     nico         2513 jan 10 20:34 _build/test/lib/grep_templates/ebin/Elixir.GrepTemplates.erl

 

Create a mix task that runs during the compilation phase

In my previous blog post I used Mix to create a new project and converted my script into a module. However, I still want that the final deliverable to be an Elixir script. I can add the missing line by hand, but I want this to be done during the build automatically. So, I made a custom mix task for the project.

The mix configuration is an Elixir script called ‘mix.exs’. It can be extended with your own Elixir code. And in Elixir it is fairly easy to scan, copy, delete and create files.

In the mix.exs the following Mix.Task module is added at the end, below the Mix.Project module. I borrowed some code from this blog.

defmodule Mix.Tasks.Compile.ScriptIt do
  use Mix.Task

   @shortdoc "Copy .ex files to script subdirectory. Rename to .exs and append a line to call the main method."
   @moduledoc @shortdoc

   def run(_) do
      # create directory to store scripts
      unless File.dir?("script"), do: File.mkdir! "script"

      # lookup source path
      project_config = Mix.Project.config
      source_paths = project_config[:elixirc_paths]

      # find modules and change them into script files
      Mix.Utils.extract_files(source_paths, "*.ex")
      |> Enum.map(&(script_it(&1)))
   end

   defp script_it(filename) do
     basename = Path.basename(filename, ".ex")
     scriptfilename = "script/" <> basename <> ".exs"
     File.copy!(filename, scriptfilename)
     file = File.open!(scriptfilename, [:append])
     modulename = Mix.Utils.camelize basename
     IO.puts(file, "\n" <> modulename <> ".main(System.argv())")
     File.close(file)
   end
end

Explanation of the code:

  • Line 7: In a Mix.Task, the run function will be called.
  • Line 9: The “script” directory will be created if it doesn’t exist.
  • Line 12: The project configuration.
  • Line 13: Extract the compiler source paths.
  • Line 16: Use Mix.Utils to search source files with the extension .ex
  • Line 17: For all the files in the list, call the script_it function.
  • Line 21: Extract the filename without path and without extension.
  • Line 22: This will be the name of the script file that will be created.
  • Line 23: Copy the module.
  • Line 24: Open the script file in append mode.
  • Line 25: Derive the module name from the file name. Use Mix.Utils to convert snake_case to CamelCase.
  • Line 26: Append a line to the script file. It’s a script line to call the main method of the module. It’s blindly assumed that the module has a main method.
  • Line 27: Files should be closed.

In the Mix documentation it’s mentioned that the compilers can be prepended or appended with other compilers. I inspected what Mix.compilers returns.
It is: [yecc, :leex, :erlang, :elixir, :app]

I added :script_it, so whenever

$ mix compile

is invoked, or indirectly:

$ mix test

the scripit task will run as well.

This is how my project configuration looks:

  def project do
    [app: :grep_templates,
     version: "0.0.1",
     elixir: "~> 1.1",
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     compilers: Mix.compilers ++ [:script_it],
     escript: [main_module: GrepTemplates],
     deps: deps]
  end

That’s it!

 

The project with all sources is available for download on Github.

Best wishes for 2016

 

Elixir unit tests

Social coding
In my previous blog post I wrote an Elixir script. I decided to write some unit tests, and make it available on Github for download.

ExUnit

ExUnit tests modules and not scripts. I can only test the module defined in my script. Even with only one module it’s good to use the power of mix:

$ mix new grep_templates
$ cd grep_templates

This creates a grep_templates.ex file in the lib subdirectory with a module named ‘GrepTemplates’. The naming convention is to use CamelCase for module names and snake_case for everything else.
I overwrote the grep_templates.ex file with the code from my previous blog, and removed the lines below the module (lines 60-62). If you don’t remove these script lines, the Elixir compiler will execute them. It’s a known issue / feature. Yes, the compiler runs the code. I know, it’s weird.

I copied the test xml files to the test directory and created .out files for the expected output.
Writing the unit tests was easy, since there is a CaptureIO module to capture the output of stdout and stderr, and to supply input via stdin if needed.

Tests are run with:

$ mix test

Note that there is a test_helper.exs file in the test directory. Mix test will run this script first. This helper calls ExUnit.start.

Here are the unit tests:

defmodule GrepTemplatesTest do
   use ExUnit.Case, async: true

   import ExUnit.CaptureIO
   import GrepTemplates, only: [ main: 1 ]

   test "--help returns help output ignoring other parameters" do
      fun = fn -> main(["test/mltest_a.xml", "test/thisfiledoesnotexist", "--help"]) end
      expected = "usage: grep_templates.exs [--help] [file...]\n"
      actual   = capture_io(:stdio, fun)
      assert actual == expected
   end

   test "returns errors about all nonexisting options" do
      fun = fn -> main(["test/mltest_a.xml", "test/thisfiledoesnotexist", "-xy", "-z", "--what"]) end
      expected = """
      grep_templates.exs: Unknown option 'xy'
      grep_templates.exs: Unknown option 'z'
      grep_templates.exs: Unknown option '-what'
      Type 'grep_templates.exs --help' for more information.
      """
      actual   = capture_io(:stderr, fun)
      assert actual == expected
   end

   test "scan files mltest_a.xml and mltest_b.xml" do
      fun = fn -> main(["test/mltest_a.xml", "test/mltest_b.xml"]) end
      expected1 = File.read! "test/mltest_a.out" 
      expected2 = File.read! "test/mltest_b.out"
      expected = expected1 <> expected2
      actual   = capture_io(:stdio, fun)
      assert actual == expected
   end

   test "zero arguments, search in stdin" do
      fun = fn -> main([]) end
      input = """
      aaa <Templates> bbb
      ccc </Templates> ddd
      """
      expected = "<Templates> bbb\nccc </Templates>\n"
      actual   = capture_io(:stdio, input, fun)
      assert actual == expected
   end

   test "search in files and stdin" do
      fun = fn -> main(["test/mltest_a.xml", "-", "test/mltest_b.xml"]) end
      input = """
      eee <Templates> fff
      ggg </Templates> hhh
      """
      expected1 = File.read! "test/mltest_a.out" 
      expected2 = File.read! "test/mltest_b.out"
      expected = expected1 <> "<Templates> fff\nggg </Templates>\n" <> expected2
      actual   = capture_io(:stdio, input, fun)
      assert actual == expected
   end

end

Explanation:

  • Line 5: Import GrepTemplates, but only the main method.
  • Line 8: Define a function to call the main method with the desired arguments.
  • Lines 10: Run the test and capture stdout.
  • Line 11: Compare actual with expected result.

ExDoc

With mix you can also generate documentation for the module. To use ExDoc the mix.exs file must be edited to add ExDoc as a dependency:

  defp deps do
    [
      {:earmark, "~> 0.1", only: :dev},
      {:ex_doc, "~> 0.11", only: :dev}
    ]
  end

and run:

$ mix deps.get
$ mix docs

and view doc/index.html to see the result.

Escript

The Elixir script can be compiled to an Erlang script. The escript command runs a script written in Erlang.

In the mix.exs file the name of the main module must be put in the project configuration like this:

 def project do
    [app: :grep_templates,
     version: "0.0.1",
     elixir: "~> 1.1",
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     escript: [main_module: GrepTemplates],
     deps: deps]
  end

and run:

$ mix escript.build

The script is placed in the root of the project directory, and can be run immediately. However, it’s 2.4 MB big, and only the first 3 lines are human readable. It’s looks nothing like a normal escript. Here an example of Hello world in Erlang:

$ cat helloworld 
#!/usr/bin/env escript
main(_) -> io:fwrite("Hello, world!\n").

$ chmod +x helloworld 
$ ./helloworld 
Hello, world!
$ 

Now, this is a script that can be read, inspected and modified. You will not get that when you use mix escript.build.

Terminal recording

It’s an animated gif and might not work on small devices:

ttyrecord_mix_git

Elixir CLI multiline search in files

21 years after Perl 5, 15 years after Larry Wall’s State of the onion, 12 years after the book Perl 6 essentials, yes finally, cropped-camelia-small-santa1Perl 6 is coming this Christmas (2015) !!! I am a happy webmin user, and I can book a hotel room via booking.com. Perl is still alive.

 

My favorite Perl 5 script is an one-liner:

perl -0777 -ne 'print "$1\n" while m!(<Templates>.*?</Templates>)!sg'
  • -0 is the record separator, which is normally the newline. 777 is a magic number to tell that there is no separator. This helps to search across newlines.
  • -n loop through all records
  • -e run script from the command line
  • print $1 ; print group 1 of the regular expression, e.g. the text between ( and )
  • m! match with ! as alternative delimiter. A regular expression is normally written as /<pattern>/ or m/<pattern>/. / is the delimiter. Perl allows you to use another delimiter. ! is used such that / can occur in the search text without escaping it with a backslash.
  • <Templates> … </Templates> is what I search for in my xml files.
  • .* is a wildcard match. Normally you get the longest match, but .*? gives the shortest match. In other words: match non-greedy
  • !sg The s modifier let the . from .* match newlines. The g is to search for all matches. g = global match

What the above perl script does is searching for <Templates> blocks in xml files. It doesn’t work correctly with nested blocks, but I don’t need that anyway. It’s purpose is to quickly scan in a project for Template information without needing a heavy duty program to parse XML files. If I want to find something else I just change the regular expression to my likings.

Wait a minute, isn’t this blog about Elixir?

Of course it is. I was curious how the Perl script would look like in Elixir, and I wanted to get acquainted with Elixir with something simple, so I decided to make an Elixir command line script that could do the same.

I first looked how regular expressions are done in Elixir. I found the Regex module, and tried some things in the iex shell:

nico@nico-ubuntu:~/elixir/adhoc_scripts/multilinesearch$ iex
Erlang/OTP 18 [erts-7.1]  [smp:4:4] [async-threads:10] [kernel-poll:false]

Interactive Elixir (1.1.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Regex.replace(~r/abc/, "aaabccccc abc qq", "def")
"aadefcccc def qq"
iex(2)> Regex.replace(~r/a(b|x)c/, "aaaxccccc abc axc q", "\\1")
"aaxcccc b x q"
iex(3)> Regex.scan(~r/BEGIN.*END/, "ab BEGIN cde \n fg END h \n ij BEGIN klm END nop")
[["BEGIN klm END"]]
iex(4)> Regex.scan(~r/BEGIN.*END/U, "ab BEGIN cde \n fg END h \n ij BEGIN klm END nop")
[["BEGIN klm END"]]
iex(5)> Regex.scan(~r/BEGIN.*END/s, "ab BEGIN cde \n fg END h \n ij BEGIN klm END nop")
[["BEGIN cde \n fg END h \n ij BEGIN klm END"]]
iex(6)> Regex.scan(~r/BEGIN.*END/Us, "ab BEGIN cde \n fg END h \n ij BEGIN klm END nop")
[["BEGIN cde \n fg END"], ["BEGIN klm END"]]
iex(7)>
BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
(v)ersion (k)ill (D)b-tables (d)istribution
a

My conclusion: use Regex.scan to find all matches, with the U modifier for the ungreedy match and the s modifier to let . match newlines as well.

I needed some test data, so I created two xml files:

mltest_a.xml:

<?xml version="1.0" encoding="utf-8"?>
<root>
	<item name"item a1">
		<definition />
		<Templates>
			<tpl name="tpl1">tpl text1</tpl>
			<tpl name="tpl2¨>tpl text2</tpl>
		</Templates>
		<properties />
	</item>
	<item name="item a2"><Templates><tpl name="tpl3" >tpl text3</tpl></Templates></item>
</root>

mltest_b.xml:

<?xml version="1.0" encoding="utf-8"?>
<root>
	<item name"item b">
		<definition />
		<Templates>
			<tpl name="tpl4">tpl text4</tpl>
			<!-- here comes a nested template -->
			<Templates>
				<tpl name="tpl5">tpl text5</tpl>
			</Templates>
			<tpl name="tpl6¨>this one will be missed out</tpl>
		</Templates>
		<properties />
	</item>
</root>

Output

The perl output when I pass both files as arguments is:

<Templates>
			<tpl name="tpl1">tpl text1</tpl>
			<tpl name="tpl2¨>tpl text2</tpl>
		</Templates>
<Templates><tpl name="tpl3" >tpl text3</tpl></Templates>
<Templates>
			<tpl name="tpl4">tpl text4</tpl>
			<!-- here comes a nested template -->
			<Templates>
				<tpl name="tpl5">tpl text5</tpl>
			</Templates>

Then I had a look at the OptionParser, because I have to parse the command line parameters.

If I don’t pass any paramer, perl will read from stdin. The same applies if you pass the ‘-‘ parameter. Maybe you know this trick:

echo "and the next file comes here:" | cat mltest_a.xml - mltest_b.xml

This will print the text between the content of the files.


iex(1)> OptionParser.parse(["--what", "-c=5", "-d", "100", "test.xml"], switches: [help: :boolean])
{[what: true], ["test.xml"], [{"-c", "5"}, {"-d", "100"}]}
iex(2)> OptionParser.parse(["--what", "-c=5", "-d", "100", "test.xml"], strict: [help: :boolean])
{[], ["100", "test.xml"], [{"--what", nil}, {"-c", nil}, {"-d", nil}]}
iex(3)> OptionParser.parse(["--help", "-c=6", "-d", "200", "test2.xml"], switches: [help: :boolean])
{[help: true], ["test2.xml"], [{"-c", "6"}, {"-d", "200"}]}
iex(4)> OptionParser.parse(["--help", "-c=6", "-d", "200", "test2.xml"], strict: [help: :boolean])
{[help: true], ["200", "test2.xml"], [{"-c", nil}, {"-d", nil}]}
iex(5)> { ok, files, failures } = OptionParser.parse(["--help", "-c=6", "-d", "200", "test2.xml"], strict: [help: :boolean])
{[help: true], ["200", "test2.xml"], [{"-c", nil}, {"-d", nil}]}
iex(6)> ok
[help: true]
iex(7)> files
["200", "test2.xml"]
iex(8)> failures
[{"-c", nil}, {"-d", nil}]

 

This is the final result:

grep_templates.exs:

#!/usr/bin/env elixir
#

defmodule GrepTemplates do

   @moduledoc """
   Demonstrate multiline search in file content.

   Prints everything between <Templates> and </Templates> markers.

   Runs as CLI utility.
   """

   @doc "Call main with file names. E.g. main([\"xmlfile.xml\"])"
   def main(args) do
      # OptionParser.parse returns { option_list, file_list, unknown_option_list } 
      parse = OptionParser.parse(args, strict: [help: :boolean])
      case parse do
         {[help: true] , _, _ }      -> show_help
         {_, [], [] }                -> scan ["-"]
         {_, file_name_list, [] }    -> scan file_name_list
         {_, _, failed_option_list } -> show_error failed_option_list
         _                           -> IO.puts(:stderr, "Error while parsing arguments.")
      end
   end

   # "print usage line"
   defp show_help, do: IO.puts "usage: grep_templates.exs [--help] [file...]"

   # "print last line of unknown options"
   defp show_error([]), do: IO.puts(:stderr, "Type 'grep_templates.exs --help' for more information.")

   # "print unknown options"
   defp show_error([option_value | tail]) do
      { option, _ } = option_value
      IO.puts(:stderr, "grep_templates.exs: Unknown option '" <> String.slice( option, 1..-1 ) <> "'")
      show_error tail
   end

   def scan([]), do: :ok

   def scan(["-" | tail]) do
      stdin_text = IO.read :all
      print_templates stdin_text
      scan tail 
   end

   @doc "search in given files for everything between <Templates> and </Templates> markers"
   def scan([filename | tail]) do
      file = File.read! filename
      print_templates file
      scan tail 
   end

   defp print_templates(text) do
      list_of_lists = Regex.scan(~r/<Templates>.*<\/Templates>/Us, text, capture: :all)
      IO.puts Enum.join(list_of_lists, "\n")
   end
end

# call main method with given arguments
GrepTemplates.main(System.argv())

Make the script executable:

chmod +x *.exs

Make sure that elixir is in the executable search path and start the script:

./grep_templates.exs mltest_a.xml mltest_b.xml

The output is exact the same as the perl script.

There is a noticeable delay when you start the script before you get any output. On my system it takes half a second. I would not worry about it too much, but don’t place the script in a big loop. I combine the find and xargs commands to get the list of files that must be passed on to the script:

find . -type f -name '*.xml' | xargs ./grep_templates.exs

 

Explanation of the code:

  • Line 1: is the shebang which will invoke elixir to run the script.
  • Line 62: This makes it a script. Most of the code is put in a module because that will make it easier adopt it in a ‘mix’ project.
  • Lines 6-14: This is for generating documentation with ExDoc. You can use markdown, but please don’t use # or h1, because that looks weird in the end result. From the @moduledoc the first line will appear in modules overview page.
  • Line 15: I could have used any function name, but I used main/1 for compatibility with ‘mix escript.build’.
  • Line 16: use the OptionParser
  • Line 17-24: some serious pattern matching. Works like a router.
  • Line 28: private function to print the usage line
  • Line 31: Prints this line when at the end when unknown switches are used. This also ends the recursive showError calls.
  • Line 34: Pick the first item of the list
  • Line 35: We are only interested in the option of the option-value tuple
  • Line 36: <> is used for string concatenation
  • Line 37, 45, 52: recursive call to handle the other items of the list
  • Line 40: recursion of the scan function ends here
  • Line 42: match the “-” parameter
  • Line 43: read all lines from stdin
  • Line 44, 51: pass on the text to print the Templates in the text
  • Line 45: recursive call to the handle
  • Line 50: read the file content. Fail if the file doesn’t exist
  • Line 56: the regular expression to find anything between <Templates> and </Templates>
  • Line 57: join the strings in the list of lists, use newline as separator

 

This was a fun exercise for me.

Nico

 

How I heard about Phoenix and Elixir

I first heard about Elixir and Phoenix when I came across this meteor thread. And while discovering more about Elixir and Phoenix I started to wonder why haven’t I heard of them before.googlebubble

Have I been living under a rock, sleeping, trapped in a Google bubble?
No, yes and yes.

 

This blog about Elixir is dated 22 januari 2013

And there are already 5 books about Elixir:

And there is a book about Phoenix as well:

Programming Phoenix, Chris McCord

But “Phoenix’’ or “Elixir” are not really helpful terms to use in a search engine.

And Phoenix is a young framework. Version 1.0 appeared 28 august 2015.
I also wouldn’t have recognized the full potential of Elixir if Phoenix wasn’t there to proof it.

Meanwhile, love declarations keep popping up:

 

The party has just started and we should spread the word.

How did you hear about Phoenix and Elixir?

 

Elixir & Phoenix, it’s golden

Read this if you want to stay competitive!

I started this blog to tell about the upcoming revolution in software development of custom web applications.

The use case: on a new project, a custom made modern web application, you need something lightning fast, scalable, straightforward, and you have limited budget/time/resources.

I checked the following web frameworks:

  • Go: Beego
  • Java: Apache Wicket, Vaadin, ZK framework, Manydesigns Portofino, Spring boot with thymeleaf, JSF
  • Javascript: Meteor, Node.js Express, AngularJS
  • PHP: Laravel
  • Python: Django, Web2py
  • Ruby: Ruby on Rails
  • Scala: Akka + Play framework
  • F#: ASP.NET Core (vNext) & MVC Core & Entity Framework Core (NPGSQL) & Razor with tag helpers, previously called ASP.NET 5 & MVC 6 & EF 7

And this is what I highly recommend:

Elixir with the Phoenix Framework

Say what?

Elixir is the programming language, and Phoenix is the web framework built with Elixir.elixir

What is Elixir?

“Elixir is what would happen if Erlang, Clojure, and Ruby somehow had a baby and it wasn’t an accident” – Devin Torres

Ok, sure, that clears things up.

Let’s read what José Valim, the creator of Elixir says:

Elixir is a concurrent and distributed programming language that runs on the Erlang Virtual Machine (Erlang VM) focusing on productivity and maintainability” – José Valim

Ok, but what is Erlang?

Erlang is the programming language invented by Ericsson telephone company, giving Whatsapp the power to handle more than 2 million concurrent TCP connections on an individual server.

Impressive. And Phoenix is?logo

Like Ruby has Ruby on Rails, Elixir has Phoenix. The Phoenix framework is heavily influenced by Ruby on Rails.

So what? What does it mean?

This means that anyone experienced with coding in a similar MVC web framework, can pick up Phoenix quickly.

If I only had Elixir I wouldn’t know to start, but with Phoenix  I can start immediately. Just pick up a Phoenix tutorial and start typing.

Do note that Elixir’s functional programming language will be a paradigm shift for Object Oriented programmers. It will raise questions like:

  • How do I store state in a language where data is immutable? answer
  • How do I make my functions tail recursive?

Right now, you will not find experienced Elixir / Phoenix developers, but more important are these development skills: HTML, data modelling, Javascript, CSS, Web security and SQL.

If you hear somebody speak about web application development, Andy_Tools_Hammer_Spanner_clip_art_smallask:

Is it made with Elixir & Phoenix?

 Why?

It’s golden, that’s why.

Nico