Author Archives: steven.proctor@gmail.com

Erlang Thursday – ETS Introduction, Part 2

Today’s Erlang Thursday continues the introduction to the ets module, and ETS in general.

We saw last time that ETS tables are destroyed when the parent process crashes, so the question comes, how might we be able to keep our ETS tables alive if we just “Let It Crash!”?

To solve this problem, we will take a look at the function ets:give_away/3 and the option of specifying the heir at table construction.

First, we will create a function that will represent a process we can give the table ownership to. This function just does a receive and never times out.

Fun = fun() -> receive after infinity -> ok end end.
% #Fun<erl_eval.20.54118792>

And now with that function, we can spawn a process to run that function.

Process = spawn(Fun).
% <0.53.0>

We create a new ETS Table,

Table = ets:new(table, []).
% 20498

and give it away to the process we just spawned.

ets:give_away(Table, Process, []).
% true

We can look at the table info, and see the owner is now the process we spawned as the Pid for the process aligns with the Pid in the owner tuple in the table settings.

ets:info(Table).
% [{read_concurrency,false},
%  {write_concurrency,false},
%  {compressed,false},
%  {memory,305},
%  {owner,<0.53.0>},
%  {heir,none},
%  {name,table},
%  {size,0},
%  {node,nonode@nohost},
%  {named_table,false},
%  {type,set},
%  {keypos,1},
%  {protection,protected}]

Now that we have supposedly transferred ownership, time to crash our current process, which is the one that was the original owner before the transfer.

1 = 2.
% ** exception error: no match of right hand side value 2
self().
% <0.58.0>

We check if the process we spawned is still alive, mostly out of showing that there is nothing up our sleeves.

is_process_alive(Process).
% true

And let’s take a look at the “info” for the table again, and see if it is still available.

ets:info(Table).
% [{read_concurrency,false},
%  {write_concurrency,false},
%  {compressed,false},
%  {memory,305},
%  {owner,<0.53.0>},
%  {heir,none},
%  {name,table},
%  {size,0},
%  {node,nonode@nohost},
%  {named_table,false},
%  {type,set},
%  {keypos,1},
%  {protection,protected}]

It is still alive!!! We did transfer ownership, so if our process crashes the ETS table still stays alive.

Time to kill that process

exit(Process, "Because").
% true
is_process_alive(Process).
% false

and watch the ETS table disappear…

ets:info(Table).
% undefined

This time, let’s use the heir option when creating an ETS table, and take advantage of the magic of ownership transfer for an ETS table to a heir.

In this case, the shell will be the heir when the owning process dies.

TableWithHeir = ets:new(table, [{heir, self(), "something went wrong"}]).
% 24594

We create a new process, and assign ownership of the ETS table to the new process.

Process2 = spawn(Fun).
% <0.71.0>
ets:give_away(TableWithHeir, Process2, []).
% true

We then look at the info for the table, and we can see both the owner is the new process, and the heir is our current process.

self().
% <0.58.0>
ets:info(TableWithHeir).
% [{read_concurrency,false},
%  {write_concurrency,false},
%  {compressed,false},
%  {memory,349},
%  {owner,<0.71.0>},
%  {heir,<0.58.0>},
%  {name,table},
%  {size,0},
%  {node,nonode@nohost},
%  {named_table,false},
%  {type,set},
%  {keypos,1},
%  {protection,protected}]

Time to kill the owning process again…

exit(Process2, "Because").
% true
is_process_alive(Process2).
% false

And if we inspect the table info again, we can see the current process is now both the owner and the heir.

ets:info(TableWithHeir).
% [{read_concurrency,false},
%  {write_concurrency,false},
%  {compressed,false},
%  {memory,349},
%  {owner,<0.58.0>},
%  {heir,<0.58.0>},
%  {name,table},
%  {size,0},
%  {node,nonode@nohost},
%  {named_table,false},
%  {type,set},
%  {keypos,1},
%  {protection,protected}]

We spawn up a new process, and we give the table to that new process.

Process3 = spawn(Fun).
% <0.78.0>
ets:give_away(TableWithHeir, Process3, []).
% true

The owner now becomes that new process, and our current process is still the heir.

ets:info(TableWithHeir).
% [{read_concurrency,false},
%  {write_concurrency,false},
%  {compressed,false},
%  {memory,349},
%  {owner,<0.78.0>},
%  {heir,<0.58.0>},
%  {name,table},
%  {size,0},
%  {node,nonode@nohost},
%  {named_table,false},
%  {type,set},
%  {keypos,1},
%  {protection,protected}]

So by taking advantage of the ability to specify a heir, and using ets:give_away/3, we can help keep the ETS table alive.

One way this might be taken advantage of is that we have a supervisor create a “heir” process, and then create the child process that would own the ETS table, and if the child dies, it can then transfer ownership back to the heir process until the new “owning” process can be restarted, and then the heir process can then transfer ownership of the ETS table to the “newly restarted” process.

–Proctor

Ruby Tuesday – SSL Version In Ruby

Today’s Ruby Tuesday takes a look at the OpenSSL::SSL::SSLContext#ssl_version.

At work today, I was pulled into a bit of a “fire”, where I was told that one of the sets of services our app at work depends on is going to be removing support for all but TLS 1.2.

Just doing a basic Net::HTTP.get did not get us a result back, and we first had to figure out how to get TLS 1.2 as something that Ruby as the client would say it supports.

Before we can start testing any of this, we need to require openssl and net/http.

require 'openssl'
# => true
require 'net/http'
# => true

It turns out when you don’t specify a version the default version is SSLv23, which can be found by looking at DEFAULT_PARAMS on the SSLContext.

OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:ssl_version]
=> "SSLv23"

Since TLSv1.2, is a “higher” version than SSLv23, we were getting errors back because we a connection using TLSv1.2 would never be negotiated.

To get the ability to support TLS generically, instead of just hardcoding a specific version, e.g. TLSv1, or TLSv1_2, the ssl_version needs to be set to nil to tell Ruby’s OpenSSL components to use TLS instead of SSL.

This opened up the question of if we set OpenSSL to be TLS instead of SSL, would the TLS Protocol negotiate down to SSL if we happen to have an endpoint we need to talk to that doesn’t currently support TLS, but only SSL.

Playing around with Net::HTTP.start I was able to play with sending HTTPs requests using different settings for the ssl_version.

As I was also testing against a local instance of nginx that would only support SSLv3 using a self-signed certificate, I had the veryfy_mode to VERIFY_NONE for testing. Note that I do NOT recommend this for real use cases.

The first helper method I created doesn’t specify a ssl_version option, so it just uses the default ssl_version setting.

def test_url_no_version(url)
  Net::HTTP.start(url.hostname, nil,
                  use_ssl: url.scheme == "https",
                  verify_mode: OpenSSL::SSL::VERIFY_NONE ) do |http|
    response = http.request(Net::HTTP::Get.new(url))
    puts response.inspect
    response
  end
end
# => :test_url_no_version

The the next helper method I created sets the ssl_version option to nil to allow it to use TLS instead of SSL.

def test_url_ssl_version_is_nil(url)
  Net::HTTP.start(url.hostname, nil,
                  use_ssl: url.scheme == "https",
                  verify_mode: OpenSSL::SSL::VERIFY_NONE,
                  ssl_version: nil ) do |http|
    response = http.request(Net::HTTP::Get.new(url))
    puts response.inspect
    response
  end
end
# => :test_url

The the last helper method I created sets the ssl_version option to :SSLv3, which is the only version the webserver is setup to handle.

def test_url_ssl3(url)
  Net::HTTP.start(url.hostname, nil,
                  use_ssl: url.scheme == "https",
                  verify_mode: OpenSSL::SSL::VERIFY_NONE,
                  ssl_version: :SSLv3 ) do |http|
    response = http.request(Net::HTTP::Get.new(url))
    puts response.inspect
    response
  end
end
# => :test_url_ssl3

Now that we have these, we can test the results of asking the test webserver for a request and see what happens. We will also use Google as a baseline to compare against.

First we will test the verion where the ssl_version is set to nil. This would tell us if it would fall back to try a SSL variant.

test_url_ssl_version_is_nil(URI("https://www.google.com"))
# #<Net::HTTPOK 200 OK readbody=true>
# => #<Net::HTTPOK 200 OK readbody=true>
test_url_ssl_version_is_nil(URI("https://localhost/index.html"))
# OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=SSLv2/v3 read server hello A: unsupported protocol
# from /Users/proctor/.rvm/rubies/ruby-2.2.3/lib/ruby/2.2.0/net/http.rb:923:in `connect'

Google returns successfully, but the local test doesn’t so it doesn’t look to fall back to SSL from TLS.

Next we try with the default setting for ssl_version.

test_url_no_version(URI("https://www.google.com"))
# #<Net::HTTPOK 200 OK readbody=true>
# => #<Net::HTTPOK 200 OK readbody=true>
test_url_no_version(URI("https://localhost/index.html"))
# OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=SSLv2/v3 read server hello A: unsupported protocol
# from /Users/proctor/.rvm/rubies/ruby-2.2.3/lib/ruby/2.2.0/net/http.rb:923:in `connect'

Google still returns successfully, but the local test case still doesn’t work.

Finally we will test with specifying SSL v3 specifically.

test_url_ssl3(URI("https://www.google.com"))
# #<Net::HTTPOK 200 OK readbody=true>
# => #<Net::HTTPOK 200 OK readbody=true>
test_url_ssl3(URI("https://localhost/index.html"))
# #<Net::HTTPOK 200 OK readbody=true>
# => #<Net::HTTPOK 200 OK readbody=true>

And for this, Google and the local test both work. So we have shown that with the right ssl version specified, we do get a response back from our local test server, but the fallback from TLS to SSL doesn’t happen.

–Proctor

Erlang Thursday – ETS Introduction, Part 1

Today’s Erlang Thursday starts the beginning of an intro to the ets module, and ETS in general.

ETS stands for Erlang Term Storage, and is a in-memory store for Erlang terms, a.k.a pieces of an Erlang data type, that provides constant access time to the data stored.

ETS can be thought of as a key/value store style storage, and it uses the concept of tables as the way of grouping together data.

One of the first things that is useful to know is that ETS tables are created by a process which, unless transfered to another process, is the owner of the table.

When the owner dies, the table gets deleted, and is no longer accessible.

Let’s see what this would look like.

First, after starting a new Erlang shell, we will check the PID (process identifier) of the shell we are in.

self().
% <0.34.0>

We then will create a new ETS table. We will be going into future details about the various ways new tables can be created in future posts, so for now, we will just create a new table by only specifying a name and empty list of options.

TableId = ets:new(table, []).
% 20496

Capturing table id, we will take a look at the info that ETS knows about that table with ets:info/1.

ets:info(TableId).
% [{read_concurrency,false},
%  {write_concurrency,false},
%  {compressed,false},
%  {memory,305},
%  {owner,<0.34.0>},
%  {heir,none},
%  {name,table},
%  {size,0},
%  {node,nonode@nohost},
%  {named_table,false},
%  {type,set},
%  {keypos,1},
%  {protection,protected}]

Time to cause the owning process to crash. In this case we’ll do a bad pattern match to cause a bad match exception.

1 = 2.
% ** exception error: no match of right hand side value 2

And let’s check the PID of the process to double check that the shell has indeed started a new process for us to run in.

self().
% <0.40.0>

And yes, the PID self() returned is different than the PID we got when we called self() the first time.

Time to look at the info for the table we created earlier again and see what we get.

ets:info(TableId).
% undefined

undefined. So we no longer have any table found by ETS for that table id.

We take a secondary look using ets:all/0 to see if we can see if it might be floating around somewhere still but the call to ets:info/1 is just not returning for the table id.

ets:all().
% [8207,file_io_servers,inet_hosts_file_byaddr,
%  inet_hosts_file_byname,inet_hosts_byaddr,inet_hosts_byname,
%  inet_cache,inet_db,global_pid_ids,global_pid_names,
%  global_names_ext,global_names,global_locks,4098,1,ac_tab]

Doesn’t look like it, so let’s create another table with the same table name as before.

Table2Id = ets:new(table, []).
% 24592

That succeeds and doesn’t complain about trying to create a table with the same name as an existing table.

We will call ets:all/0 again, and we can see there is an item in the list with the id that was returned from ets:new/2.

ets:all().
% [24592,8207,file_io_servers,inet_hosts_file_byaddr,
%  inet_hosts_file_byname,inet_hosts_byaddr,inet_hosts_byname,
%  inet_cache,inet_db,global_pid_ids,global_pid_names,
%  global_names_ext,global_names,global_locks,4098,1,ac_tab]

Time to crash the process again.

1 = 2.
% ** exception error: no match of right hand side value 2

We note that we do have a new PID again.

self().
% <0.47.0>

And if we call ets:all/0 one more time, we can see that the table identifier that was previously in the list has gone away.

ets:all().
% [8207,file_io_servers,inet_hosts_file_byaddr,
%  inet_hosts_file_byname,inet_hosts_byaddr,inet_hosts_byname,
%  inet_cache,inet_db,global_pid_ids,global_pid_names,
%  global_names_ext,global_names,global_locks,4098,1,ac_tab]

So with this initial look at ETS, we have demonstrated an owning process crash does remove the table, and we have also gotten an preview of a couple of the functions in the ets module, specifically ets:new/2, ets:info/1, and ets:all/0.

We will continue looking at the overview of ETS for a few posts, while doing some cursory coverage of some of the functions in the ets module, and after that, we will then start to get into the specifics of the different functions in the ets module.

–Proctor

Ruby Tuesday – Symbol#to_proc

Today’s Ruby Tuesday is on Symbol#to_proc.

Symbol#to_proc returns a Proc object which corresponds to the method represented by the symbol it is called on.

First, we will create a new method foo which just puts the string Foo to output.

def foo
  puts "Foo"
end
# => :foo

Now let’s call Symbol#to_proc on a couple of different symbols, and see what their return values are.

:foo.to_proc
# => #<Proc:0x007ff08395d9b0>
:to_s.to_proc
# => #<Proc:0x007ff0829f42a8>
:keys.to_proc
# => #<Proc:0x007ff0828280f0>

And we will also capture the results of calling to_proc on the symbol :to_s, so that we can be assured that it is the same Proc object later on.

to_s_proc = :to_s.to_proc
# => #<Proc:0x007ff0829f42a8>

Now that we have seen that Procs are being returned, it’s time to call those Procs to see how they behave.

:to_s.to_proc.call(1)
# => "1"
:foo.to_proc.call(1)
# Foo
# => nil
:keys.to_proc.call({:a => 1, :b => 2, :c => 3})
# => [:a, :b, :c]
:foo.to_proc.call()
# ArgumentError: no receiver given
# from (pry):40:in `call'
:foo.to_proc.call(1, 2, 3)
# ArgumentError: wrong number of arguments (2 for 0)
# from (pry):1:in `foo'

When we call a Proc generated in this way at the Ruby shell, the first argument is the context for what the Proc will be called against.

If we pass no arguments, we get an error of no receiver given, telling us that the Proc doesn’t have anything to call the method on.

:foo.to_proc.call()
# ArgumentError: no receiver given
# from (pry):40:in `call'

And if we pass in extra arguments to a Proc that was generated from calling Symbol#to_proc, it will error out as it expects the right number of arguments to be passed to the Proc.

:foo.to_proc.call(1, 2, 3)
# ArgumentError: wrong number of arguments (2 for 0)
# from (pry):1:in `foo'

And finally, we will call the Proc we captured in a variable, to show that the same Proc will operate against multiple objects of different types.

to_s_proc.call(1)
# => "1"
to_s_proc.call({:a => 1, :b => 2, :c => 3})
# => "{:a=>1, :b=>2, :c=>3}"

So we can see that the Proc we got from calling :to_s.to_proc will work and call to_s on both and integer and a Hash.

–Proctor

Erlang Thursday – digraph:del_path/3

Today’s Erlang Thursday is on digraph:del_path/3.

We will continue working with the same graph we started with in the previous post on digraph:get_path/3.

Graph = digraph:new().
% {digraph,20498,24595,28692,true}
V1 = digraph:add_vertex(Graph).
% ['$v'|0]
V2 = digraph:add_vertex(Graph).
% ['$v'|1]
V3 = digraph:add_vertex(Graph).
% ['$v'|2]
V4 = digraph:add_vertex(Graph).
% ['$v'|3]
E1 = digraph:add_edge(Graph, V1, V2).
% ['$e'|0]
E2 = digraph:add_edge(Graph, V2, V3).
% ['$e'|1]
E3 = digraph:add_edge(Graph, V3, V4).
% ['$e'|2]
E4 = digraph:add_edge(Graph, V2, V4).
% ['$e'|3]
E5 = digraph:add_edge(Graph, V4, V1).
% ['$e'|4]

digraph:del_path/3 takes three arguments, a Graph, a source vertex, and a destination vertex, and removes all edges in a each path in the graph from the source vertex to the destination vertex, until no path exist between the source and destination vertices.

The return value of digraph:del_path/3 is always a return value of true.

Looking at the picture of the graph above as reference, we are going to call digraph:del_path/3 for the graph with a source vertex of V1, and a destination vertex of V4.

digraph:del_path(Graph, V1, V4).
% true
digraph:vertices(Graph).
% [['$v'|2],['$v'|1],['$v'|0],['$v'|3]]
digraph:edges(Graph).
% [['$e'|1],['$e'|2],['$e'|4]]

Translating the edge names, we see that the edge from V1 to V2 has been removed, as well as the edge from V2 to V4 has been removed.

So how did Erlang come up with this result?

This puzzled me at first, as it wasn’t one of the two scenarios I was expecting to see, which were either: remove all edges but the edge from V4 to V1, or remove only the edge from V1 to V2.

I then opened the Erlang source code on Github for digraph module to clarify my thinking, and looking at the code it then made sense what was happening.

First digraph:del_path/3 calls digraph:get_path/3, and removes all edges in that path, and then recurses until no path is found.

This is when it clicked as to why Erlang was removing only those edges.

If we call digraph:get_path/3 on a fresh version of the graph, we see that it returns the path of V1 -> V2 -> V4.

digraph:get_path(Graph, V1, V4).
[['$v'|0],['$v'|1],['$v'|3]]

Erlang then removes the edges in that path, and the will call digraph:del_path/3, which calls digraph:get_path/3 again, but as we removed the edge between V1 and V2, no path is found so the process is finished.

This is why we see more edges removed if we reset the Graph again (by exiting the shell and recreating it from scratch by pasting the initialization into the shell), and call digraph:del_path/3 between V2 and V4.

digraph:del_path(Graph, V2, V4).
% true
digraph:edges(Graph).
% [['$e'|0],['$e'|4]]

This case there are the paths V2 -> V4 and V2 -> V3 -> V4, and if we remove the path V2 -> V4, the removal of all the edges associated with that path doesn’t break the path of V2 -> V3 -> V4, so it can remove all edges in that path as well.

So we have a win in the case where the documentation wasn’t quite as clear as it could be, but having the Erlang standard library be open source gets us a win because we can always go in and check out what the code is really doing.

–Proctor

Ruby Tuesday – Symbol#id2name

Today’s Ruby Tuesday is on Symbol#id2name.

`Symbol#id2name` returns the string value for a given symbol that is the name of the symbol.

:foo.id2name
# => "foo"
:'HelloWorld'.id2name
# => "HelloWorld"
:nil.id2name
# => "nil"
:''.id2name
# => ""

If we scroll down the documentation page for `Symbol` down to Symbol#to_s, we can see it is using `id2name` in the example.

So let’s see if we can quickly notice a difference in what is returned.

:foo.to_s
# => "foo"
:''.to_s
# => ""
:nil.to_s
# => "nil"

We can see the behavior appears to indeed be the same, so we will dig a little deeper.

If you go to the documentation page for both methods, and then toggle the source for both of the methods, you can see they both use the underlying procedure `rb_sym_to_s`, and are infact aliases for each other.

–Proctor

Erlang Thursday – digraph:get_cycle/2

Today’s Erlang Thursday is on digraph:get_cycle/2.

We will continue working with the graph from the previous post on digraph:get_path/3.

Graph = digraph:new().
% {digraph,20498,24595,28692,true}
V1 = digraph:add_vertex(Graph).
% ['$v'|0]
V2 = digraph:add_vertex(Graph).
% ['$v'|1]
V3 = digraph:add_vertex(Graph).
% ['$v'|2]
V4 = digraph:add_vertex(Graph).
% ['$v'|3]
E1 = digraph:add_edge(Graph, V1, V2).
% ['$e'|0]
E2 = digraph:add_edge(Graph, V2, V3).
% ['$e'|1]
E3 = digraph:add_edge(Graph, V3, V4).
% ['$e'|2]
E4 = digraph:add_edge(Graph, V2, V4).
% ['$e'|3]
E5 = digraph:add_edge(Graph, V4, V1).
% ['$e'|4]

digraph:get_cycle/2 takes a graph G, and an vertex V, and tries to find a path that creates a cycle between the vertex V in graph G.

digraph:get_cycle(Graph, V1).
% [['$v'|0],['$v'|1],['$v'|3],['$v'|0]]
digraph:get_cycle(Graph, V2).
% [['$v'|1],['$v'|3],['$v'|0],['$v'|1]]

Next, we add a new vertex V5, and a new edge originating from V4 and ending on V5

We then call digraph:get_cycle/2 on V5, and we get back a false as no cyle exists in the graph with vertex V5 in it.

V5 = digraph:add_vertex(Graph).
% ['$v'|4]
E6 = digraph:add_edge(Graph, V4, V5).
% ['$e'|5]
digraph:get_cycle(Graph, V5).
% false

The digraph module also contains the function digraph:get_short_cycle/2.

digraph:get_short_cycle/2 attempts to find the shortest cycle in the graph G for vertex V.

The documentation for digraph:get_short_cycle/2 exact phrasing is:

Tries to find an as short as possible simple cycle through the vertex V of the digraph G.

So depending on how you read that, the shortest cycle might not be guaranteed to be returned, but simply a shorter cycle, which may depend on the overall size and complexity of the graph.

digraph:get_short_cycle(Graph, V1).
% [['$v'|0],['$v'|1],['$v'|3],['$v'|0]]
digraph:get_short_cycle(Graph, V5).
% false

–Proctor

Ruby Tuesday – Hash#fetch

Today’s Ruby Tuesday is on Hash#fetch.

Hash#fetch is one of those methods that would have to go in my Top 10 of most underutilized methods in Ruby.

Why would Hash#fetch make that list? Because it makes explicit the results of a key lookup in a Hash.

If we have a Hash constructed using a hash literal, and do a key lookup using the square brackets, it will return a nil when a key is not found, as nil is the default default-value for Hashes.

hash1 = {:a => 1, :b => 2, :c => 3}
# => {:a=>1, :b=>2, :c=>3}
hash1[:a]
# => 1
hash1[:d]
# => nil

You might be thinking, “That’s not so bad, I can just check for nils”.

What happens when there is a key that references nil? Now you can’t know if nil was in there because it was “valid”, or just returned.

hash2 = {:a => 1, :b => 2, :c => 3, :d => nil}
# => {:a=>1, :b=>2, :c=>3, :d=>nil}
hash2[:d]
# => nil

Or what happens when you get a Hash that was not created with a Hash literal?

hash3 = Hash.new {|hash, key| "foo"}
# => {}
hash4 = Hash.new("foo")
# => {}
hash3[:a]
# => "foo"
hash4[:a]
# => "foo"

Oops. We no longer get a nil back that we can check against.

This is where Hash#fetch becomes nice and explicit.

If we call Hash#fetch and no key is found, it will raise an key not found exception.

hash2.fetch(:d)
# => nil
hash1.fetch(:d)
# KeyError: key not found: :d
# from (pry):8:in `fetch'

Why is this nice? Well, it means we encountered and error, and we stop execution at the point of the error, instead of propagating nils throughout the code base to crash at some later point.

If we don’t want an exception, we can also pass in a default value to Hash#fetch allowing us to control the default value when a key is not found.

hash1.fetch(:d, :key_not_found)
# => :key_not_found
hash2.fetch(:d, :key_not_found)
# => nil
hash3.fetch(:a, :key_not_found)
# => :key_not_found
hash4.fetch(:a, :key_not_found)
# => :key_not_found

It even works when the Hash fetch is being called on specifies different default behavior.

Even when that default behavior would try to add the key into the Hash with a default value.

hash5 = Hash.new {|hash, key| hash[key] = "foo"}
# => {}
hash5.fetch(:d)
# KeyError: key not found: :d
# from (pry):43:in `fetch'
hash5.fetch(:d, "bar")
# => "bar"

Hash#fetch can also take a block that will be called with the key as an argument, when the key is not found in the Hash.

hash1.fetch(:d) do |key|
  puts "no key found for key: `#{key}`"
  :no_key_found
end
# no key found for key: `d`
# => :no_key_found

I encourage you to play with it, and see if it doesn’t start to creep up into your list of favorite methods that are underutilized.

–Proctor

Erlang Thursday – digraph:in_neighbors/2

Today’s Erlang Thursday is on digraph:in_neighbors/2.

digraph:in_neighbors/2 takes a graph G, and a vertex V, and will return a list of all the vertices that have edges originating from them that are directed toward the vertex V.

We will continue working with the graph from last week’s post on digraph:get_path/3.

Graph = digraph:new().
% {digraph,20498,24595,28692,true}
V1 = digraph:add_vertex(Graph).
% ['$v'|0]
V2 = digraph:add_vertex(Graph).
% ['$v'|1]
V3 = digraph:add_vertex(Graph).
% ['$v'|2]
V4 = digraph:add_vertex(Graph).
% ['$v'|3]
E1 = digraph:add_edge(Graph, V1, V2).
% ['$e'|0]
E2 = digraph:add_edge(Graph, V2, V3).
% ['$e'|1]
E3 = digraph:add_edge(Graph, V3, V4).
% ['$e'|2]
E4 = digraph:add_edge(Graph, V2, V4).
% ['$e'|3]
E5 = digraph:add_edge(Graph, V4, V1).
% ['$e'|4]

With that graph setup again, we can now find the in_neighbors of different vertices in our graph.

digraph:in_neighbours(Graph, V4).
% [['$v'|1],['$v'|2]]
digraph:in_neighbours(Graph, V1).
% [['$v'|3]]
digraph:in_neighbours(Graph, V2).
% [['$v'|0]]

So for vertex V4 we see the return value of [['$v'|1],['$v'|2]], which are the vertices V2 and V3. For V1 we have an inbound neighbor of V4, and for V2 we have the inbound neighbor of V1.

digraph:out_neighbors/2

The digraph module also contains the function digraph:out_neighbors/2, which returns a list of the vertices that a the given vertex “points to” with its edges in the directed graph.

digraph:out_neighbours(Graph, V2).
% [['$v'|3],['$v'|2]]
digraph:out_neighbours(Graph, V4).
% [['$v'|0]]
digraph:out_neighbours(Graph, V1).
% [['$v'|1]]

We can see from the picture of our graph that V2 has edges that “point to” the vertices V3 and V4, and if we look at the result of digraph:out_neighbors/2, we get the result of the vertices V3 and V4.

In this case we get the list of vertices where V4 is first and V3 is second, but that may not be the case, as the documentation states that the the edges are “in some unspecified order”, which holds true of digraph:in_neighbors/2 as well.

–Proctor

Ruby Tuesday – Array#to_h

Today’s Ruby Tuesday is on Array#to_h.

Array#to_h will turn an Array of two element Arrays into a Hash where each two element Array represents a key value pair in the new Hash.

[[1, :a], [2, :b]].to_h
# => {1=>:a, 2=>:b}
[["cat", "meow"], ["dog", "woof"], ["mouse", "squeak"]].to_h
# => {"cat"=>"meow", "dog"=>"woof", "mouse"=>"squeak"}

If called on an Array that is not an Array of Arrays, you get a TypeError.

[1, 2, 3, 4].to_h
# TypeError: wrong element type Fixnum at 0 (expected array)
# from (pry):4:in `to_h'

If called on an Array that is an Array of Arrays, but the nested Arrays are not of length two, an ArgumentError is raised.

[[1, 2, 3], [2, 3, 4]].to_h
# ArgumentError: wrong array length at 0 (expected 2, was 3)
# from (pry):5:in `to_h'

The items in the nested arrays can be anything, even more Arrays, but those arrays will just be used as is.

[[1, [2, 3]], [[:a, :b], [3, 4]]].to_h
# => {1=>[2, 3], [:a, :b]=>[3, 4]}

–Proctor