lib/db_mnesia.ex

defmodule DbMnesia do
  def select(table_name) do
    :mnesia.start
    table_atom = String.to_atom(table_name)
    :mnesia.wait_for_tables([table_atom], 1000)
    columns = :mnesia.table_info(table_atom, :attributes) |> Enum.map(& Atom.to_string(&1))
    specs = [table_atom] ++ Enum.map(1..Enum.count(columns), & :"$#{&1}") |> List.to_tuple
    rows = :mnesia.transaction(fn ->
      :mnesia.select(table_atom, [{specs, [], [:"$$"]}]) end) |> elem(1)
    %{
      columns: columns,
      command: :select,
      connection_id: 0,
      num_rows: Enum.count(rows),
      rows: rows
   }
  end
   def insert(table_name, _columns, values) do
       # TODO: 列選択はそのうち
       :mnesia.start
       table_atom = String.to_atom(table_name)
       :mnesia.wait_for_tables([table_atom], 1000)
       result = :mnesia.transaction(fn -> 
         next_id = max_id(table_atom) + 1
         insert_spec = values |> values_value 
           |> Tuple.insert_at(0, next_id) |> Tuple.insert_at(0, table_atom)
         writed = :mnesia.write(insert_spec)
         {writed, next_id}
       end)
       case result do
         {:atomic,  {:ok, id}} -> {:ok,    id }
         {:aborted, {err, _}}  -> {:error, err}
         {_,        {err, _}}  -> {:error, err}
         {err, _}              -> {:error, err}
       end
     end
    
     def max_id(table_atom) do
       {:atomic, keys} = :mnesia.transaction(fn -> :mnesia.all_keys(table_atom) end)
       case keys do
         [] -> -1
         _  -> Enum.max(keys)
       end
     end

     def update(table_name, sets, wheres) do
         :mnesia.start
         table_atom = String.to_atom(table_name)
         :mnesia.wait_for_tables([table_atom], 1000)
         where_value = wheres |> wheres_value |> elem(0)
         update_spec = sets |> sets_value 
           |> Tuple.insert_at(0, where_value) |> Tuple.insert_at(0, table_atom)
         result = :mnesia.transaction(fn -> :mnesia.write(update_spec) end)
         case result do
           {:atomic,  :ok} -> {:ok}
           {:aborted, err} -> {:error, err}
           {_,        err} -> {:error, err}
           err             -> {:error, err}
         end
       end
      
       def delete(table_name, wheres) do
         :mnesia.start
         table_atom = String.to_atom(table_name)
         :mnesia.wait_for_tables([table_atom], 1000)
         where_spec = wheres |> wheres_value |> Tuple.insert_at(0, table_atom)
         result = :mnesia.transaction(fn -> :mnesia.delete(where_spec) end)
         case result do
           {:atomic,  :ok} -> {:ok}
           {:aborted, err} -> {:error, err}
           {_,        err} -> {:error, err}
           err             -> {:error, err}
         end
       end

       def sets_value(sets) do
           sets
           |> Enum.reduce({}, fn set, acc ->
             kv = String.split(set, "=") |> Enum.map(& String.trim(&1))
             v = List.last(kv)
             Tuple.append(acc, raw_value(v))
           end)
         end

         @doc """
     SQL Values strings(list) to value list
    
     ## Examples
       iex> DbMnesia.values_value(["123", "'hoge'"])
       {123, "hoge"} 
       iex> DbMnesia.values_value(["987", "'foo'"])
       {987, "foo"} 
     """
     def values_value(values) do
       values 
       |> Enum.reduce({}, fn v, acc ->
         Tuple.append(acc, raw_value(v))
       end)
     end
    
     @doc """
     String value to raw value
    
     ## Examples
       iex> DbMnesia.raw_value("")
       ""
       iex> DbMnesia.raw_value("123")
       123
       iex> DbMnesia.raw_value("12.34")
       12.34
       iex> DbMnesia.raw_value("'hoge'")
       "hoge"
       iex> DbMnesia.raw_value("foo")
       "foo"
       iex> DbMnesia.raw_value("12ab3")
       "12ab3"
     """
     def raw_value(v) when is_binary(v) do
       case String.match?(v, ~r/^([0-9]|\.)+$/) do
         true -> 
           case String.match?(v, ~r/\./) do
             true -> String.to_float(v)
             _    -> String.to_integer(v)
           end
         _ -> v |> String.trim("'") |> String.trim("\"")
       end
     end
     def raw_value(v) when is_number(v), do: v
     def raw_value(v) when is_boolean(v), do: Atom.to_string(v)
      def wheres_value(wheres) do
         wheres
         |> Enum.reduce({}, fn where, acc ->
           kv = String.split(where, "=") |> Enum.map(& String.trim(&1))
           v = List.last(kv)
           Tuple.append(acc, raw_value(v))
         end)
       end
      
    end