I’m looking for a review of my code to make it better. I know it’s not great as is, but I’m not sure what all could be improved by more idiomatic Elixir usage. You can view a gist of the code here, or the full text below. The goal of the code is to parse output from this query builder tool.
defmodule Mix.Tasks.Parser do
use Mix.Task
require IEx
@shortdoc "A test of the parser."
def run(_args) do
Mix.shell.info "Greetings from the parser"
# The following tree represents a query that looks like:
# (users.enrollment_status == "E" || users.dob == "2017-11-06" || (users.is_member == true && (users.gender == "M" && users.site_id IN ("234siteid"))))
tree = Poison.decode!(~s(
{
"usedFields":[
"users.enrollment_status",
"users.dob",
"users.is_member",
"users.gender",
"users.site_id"
],
"rules":[
{
"id":"9bab8999-cdef-4012-b456-715f92375987",
"field":"users.enrollment_status",
"type":"select",
"input":"select",
"operator":"select_equals",
"values":[
{
"type":"select",
"value":"E"
}
]
},
{
"id":"aabaa8ab-89ab-4cde-b012-315f92377265",
"field":"users.dob",
"type":"date",
"input":"date",
"operator":"equal",
"values":[
{
"type":"date",
"value":"2017-11-06"
}
]
},
{
"rules":[
{
"id":"9a9b899a-0123-4456-b89a-b15f9237a0de",
"field":"users.is_member",
"type":"boolean",
"input":"boolean",
"operator":"equal",
"values":[
{
"type":"boolean",
"value":true
}
]
},
{
"rules":[
{
"id":"aa98baaa-89ab-4cde-b012-315f9238650b",
"field":"users.gender",
"type":"select",
"input":"select",
"operator":"select_equals",
"values":[
{
"type":"select",
"value":"M"
}
]
},
{
"id":"a9bb8a8b-4567-489a-bcde-f15f92389859",
"field":"users.site_id",
"type":"select",
"input":"select",
"operator":"select_any_in",
"values":[
{
"type":"multiselect",
"value":[
"234siteid"
]
}
]
}
],
"condition":"AND"
}
],
"condition":"AND"
}
],
"condition":"OR"
}
))
parsed_output = parse(tree)
IO.puts parsed_output
end
def parse(node) do
res = Enum.map(Map.get(node, "rules"), &(parse_node/1))
|> Enum.join(" " <> Map.get(node, "condition") <> " ")
"(#{res})"
end
# so every node is a set of tuples --
def parse_node(node) do
if (is_top_level_node(node)), do: parse(node), else: sql_from_rule(node)
end
def is_top_level_node(node) do
cond do
is_map(node) ->
Map.keys(node) |> Enum.member?("condition")
true -> # default condition
false
end
end
def sql_from_rule(rule) do
# IEx.pry
field = Map.get(rule, "field")
# is there a better way here?
value = Map.get(rule, "values") |> List.first |> Map.get("value")
# there will be a lot of logic around picking the operator, and
# any tips there would be appreciated
"#{field} = #{value}"
end
end