From Full-time to Consulting

Working full-time at a company over a sustained period can be fulfilling. You get to see your product and team evolve as the business grows and changes. You develop an intimate knowledge of your product and codebase. And there's something magical about being familiar with the history of a line of code, a name, or a feature. This level of intimacy with a product takes time to develop and is incredibly rewarding.

Over time, though, the magic wears off. Comfort can be a double edged sword when new problems, new processes and new people are what inspire you to grow and learn. I think there's value in focusing on breadth versus depth. Rather than spending years toiling away on the same problems, what if we could constantly be exposed to new problems in new domains? What if it was our job to work with people on a short term basis to help them solve their most pressing problems?

Around a month ago, I found myself in-between jobs and asking myself these same questions. I came to the conclusion that I want to work with a revolving door of new people and companies. I want more exposure to new problems and I want to leverage the skills I learned helping build Contently to solve them. It took some time, but I realized that consulting is a clear path forward. I'm excited to get started.


Interfacing With Erlang's Xmerl Library to Parse XML

The Record module lets us interface with Erlang records. The first argument to #defrecord is an atom representing the name of the erlang record, the second argument is the record definition in the xmerl library we are using.

defmodule WeatherParser do
  require Record
  Record.defrecord :xmlElement, Record.extract(:xmlElement, from_lib: "xmerl/include/xmerl.hrl")
  Record.defrecord :xmlText, Record.extract(:xmlText, from_lib: "xmerl/include/xmerl.hrl")

  def parse(xml, attrs) do
    xml
    |> :binary.bin_to_list
    |> :xmerl_scan.string
    |> extract_values(attrs, [])
  end

  def extract_values(xml, [], res), do: Enum.reverse(res)

  def extract_values({xml, _rest}, list, res), do: extract_values(xml, list, res)

  def extract_values(xml, [h | tail], res) do
    val = extract_element(xml, h)
          |> extract_element_content
          |> extract_content_value

    extract_values(xml, tail, [val | res])
  end

  def extract_element(xml, name), do: :xmerl_xpath.string('/current_observation/#{name}', xml)

  def extract_element_content([element]), do: xmlElement(element, :content)

  def extract_content_value([content_element]), do: xmlText(content_element, :value)
end

Using head / tail recursion and an accumulator, we are able to build up a list of the parsed results. Elixir’s pipe operator makes the transformation very clear. And just to be sure, some test cases to make sure everything is working smoothly:

defmodule WeatherParserTest do
  use ExUnit.Case

  test "returns the specified attributes" do
    data = WeatherParser.parse(sample_xml, [:location, :temp_f, :temp_c])
    assert data == ['Unknown Station', '39.2', '4.0']
  end

  def sample_xml do
  """
  <?xml version="1.0" encoding="ISO-8859-1"?>
  <?xml-stylesheet href="latest_ob.xsl" type="text/xsl"?>
  <current_observation version="1.0"
     xmlns:xsd="http://www.w3.org/2001/XMLSchema"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="http://www.weather.gov/view/current_observation.xsd">
    <credit>NOAA's National Weather Service</credit>
    <credit_URL>http://weather.gov/</credit_URL>
    <image>
      <url>http://weather.gov/images/xml_logo.gif</url>
      <title>NOAA's National Weather Service</title>
      <link>http://weather.gov</link>
    </image>
    <suggested_pickup>15 minutes after the hour</suggested_pickup>
    <suggested_pickup_period>60</suggested_pickup_period>
    <location>Unknown Station</location>
    <station_id>ALIA2</station_id>
    <observation_time>Last Updated on Feb 13 2016, 2:30 pm AST</observation_time>
          <observation_time_rfc822>Sat, 13 Feb 2016 14:30:00 -0400</observation_time_rfc822>
    <temperature_string>39.2 F (4.0 C)</temperature_string>
    <temp_f>39.2</temp_f>
    <temp_c>4.0</temp_c>
    <water_temp_f>40.3</water_temp_f>
    <water_temp_c>4.6</water_temp_c>
    <wind_string>Northeast at 9.2 MPH (7.97 KT)</wind_string>
    <wind_dir>Northeast</wind_dir>
    <wind_degrees>60</wind_degrees>
    <wind_mph>9.2</wind_mph>
    <wind_gust_mph>0.0</wind_gust_mph>
    <wind_kt>7.97</wind_kt>
    <pressure_string>988.5 mb</pressure_string>
    <pressure_mb>988.5</pressure_mb>
    <windchill_string>33 F (1 C)</windchill_string>
          <windchill_f>33</windchill_f>
          <windchill_c>1</windchill_c>
    <mean_wave_dir>Northeast</mean_wave_dir>
    <mean_wave_degrees></mean_wave_degrees>
    <disclaimer_url>http://weather.gov/disclaimer.html</disclaimer_url>
    <copyright_url>http://weather.gov/disclaimer.html</copyright_url>
    <privacy_policy_url>http://weather.gov/notice.html</privacy_policy_url>
  </current_observation>
  """
  end
end

Elixir: Using CaseTemplate for DB Setup and Teardown

After spending some time spinning my wheels on figuring out how to clean up after each test run, I discovered some functions defined in the Ecto.Adapters.SQL module. Including this template in your test files that integrate with the database will give you a clean slate for each test run. Enjoy!

defmodule DBTransactions do
  use ExUnit.CaseTemplate

  setup_all do
    Ecto.Adapters.SQL.begin_test_transaction(Repo)

    on_exit fn ->
      Ecto.Adapters.SQL.rollback_test_transaction(Repo)
    end
  end

  setup do
    Ecto.Adapters.SQL.restart_test_transaction(Repo, [])
  end
end

# Use it in your test module
defmodule MessageTest do
  use DBTransactions
end