New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor the querying functions into their own module #1
Conversation
My initial opinion after having just glanced over the changes is that I like that you are splitting up some of the logic. It does make The problem I see -- and I have run into this in my own Elixir code -- is that the new What I suggest is either:
On the topic of passing errors through the pipeline: I really wish Elixir had a "fail early" option for pipes. I don't know how that would be implemented... probably with macros somehow. I'll take a closer look tomorrow after some sleep. |
|
||
def prepare(sql, database), do: :esqlite3.prepare(sql, database) | ||
|
||
def to_rows({_,_,{:error,_}=error,_}), do: error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You said you dislike the tuple here. I don't think it makes too much of a difference, but could you prepend everything with the following match:
def to_rows({types, cols, rows, into}), do: to_rows(types, cols, rows, into)
and then deal with four separate arguments in the remaining functions?
EDIT: Just to clarify: I didn't mean that as a request, just a suggestion if you prefer 4 arguments over tuples.
That is a really great insight about Between your two suggested solutions I think I prefer to have a public |
@@ -0,0 +1,54 @@ | |||
defmodule Sqlitex.Query do | |||
def bind_values({:error, _}=error, _opts), do: error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Talking to @film42 this morning, he mentioned that this looks a lot like carrying a MaybeMonad through our pipeline. That gave me the idea of using a macro that checks for {:error, _}
tuples through the pipeline and I found Elixir Pipes by Bruce Tate which already does this.
I think I'm going to try rewriting the pipeline one more time with that macro to see if I like the code better
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooooooo, good find. I think I love elixir-pipes.
Here is an implementation that is using the |
types = :esqlite3.column_types(statement) | ||
columns = :esqlite3.column_names(statement) | ||
data = :esqlite3.fetchall(statement) | ||
to_rows(types, columns, data, into) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got around the issue of having a different interface between execute
and to_rows
by making execute
call directly into to_rows
. That way the whole function either returns {:ok, results}
or {:error, some_error}
. Not a brilliant solution, but it works well enough.
I took one more swing at this before I go to bed and I'm not sure if I like it better or not. Basically instead of passing along large tuples, I created This makes the code a bit more verbose (more total lines). But I do like the fact that all the error checking is happening right by where the errors occur. This means let me get rid of a lot of function clauses that were checking for |
Travis is failing on the build now, maybe it has to do with the fact that Travis is using elixir 1.0.2 I'll have to check tomorrow, too sleepy to make progress now 😴 😪 💤 |
I love the changes so far. I love that the Sadly, the elixir-pipes guy hasn't had any activity on Github for a month :( I don't know when we might hear back from him on the hex package thing. You could reference the github page directly: {:pipe, git: "https://github.com/batate/elixir-pipes"} But at that point it might be best not to include the library at all. Maybe you could just copy/paste the functionality you need from elixir-pipes until hex is updated? As far as some of the Travis CI build failures are concerned, |
{:error, _}=error -> error | ||
end | ||
end | ||
def query(db, sql, opts \\ []), do: Sqlitex.Query.query(db, sql, opts) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just learned about defdelegate yesterday:
defdelegate query(db, sql, opts \\ []), to: Sqlitex.Query
Doesn't make much of a difference here, but it is great when you are wrapping a LOT of functions with many arguments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 I never knew about that. I'll give it a try
pipes 0.0.2 got released today and it specifies support for Elixir 1.0. So that problem is solved. I'll try to see if I can make the pipeline match on The github links for precompiled Elixir releases are all backed by AWS links. It seems like maybe kiex should skip github altogether, but I won't try to tackle that problem. I'll just try to re-run the build during a calm time of day. Thanks again for all your help. I'm learning a ton of good Elixir from this project. |
Woohoo! the more generic |
Looks great! Let me know if there are any final changes you would like to make. Otherwise, I'll pull it in tonight and finalize my PR. |
refactor the querying functions into their own module
I was reading through your PR tonight and I was wondering if it was possible to reorganize the
query
logic into a pipeline rather than a set of nested function calls.I'm not really sure if this is actually an improvement on the code, but I'm curious to get your feedback.
Things I Like
Sqlitex.query/2
is now a very readable pipeline, easier to dive into than walking through the nested function calls that previously existedThings I Don't Like
Sqlitex.Query.bind_values/2
has to use acase
statement to pass along the statement so we can keep it passing through the pipelineSqlitex.Query.to_rows/1
takes a single tuple with four items, instead of four arguments. Not a huge deal, but I would prefer 4 explicit arguments./cc @jazzyb