add documentation
This commit is contained in:
parent
eb00afa136
commit
bd0b14f475
219
README.md
219
README.md
|
@ -1,16 +1,221 @@
|
|||
# views.honeysql
|
||||
|
||||
HoneySQL plugin for the [views](https://github.com/gered/views) library.
|
||||
[HoneySQL][1] plugin for the [views][2] library. Allows for views to be
|
||||
created which retrieve data via [clojure.java.jdbc][3] using SQL
|
||||
queries provided as Clojure maps. Provides an alternate `execute!`-like
|
||||
function to execute INSERT/UPDATE/DELETE queries and add appropriate
|
||||
hints to the view system at the same time to trigger view refreshes.
|
||||
|
||||
[1]: https://github.com/jkk/honeysql
|
||||
[2]: https://github.com/gered/views
|
||||
[3]: https://github.com/clojure/java.jdbc
|
||||
|
||||
views.honeysql interops well with [views.sql][4] when both types of
|
||||
views are included within the same system.
|
||||
|
||||
[4]: https://github.com/gered/views.sql
|
||||
|
||||
## Leiningen
|
||||
|
||||
```clj
|
||||
[gered/views.honeysql "0.2-SNAPSHOT"]
|
||||
```
|
||||
|
||||
### This is a fork!
|
||||
This is a fork of the [original][5] by Kira Inc. I made some tweaks
|
||||
to keep things consistent with the changes in my fork of the views
|
||||
library, but not much else has been changed. Since I'm keeping my fork
|
||||
of the views library separate for now this library will also be kept
|
||||
separate along with it.
|
||||
|
||||
[5]: https://github.com/kirasystems/views-honeysql
|
||||
|
||||
You will **not** be able to use this fork of views.honeysql
|
||||
successfully with the original views library!
|
||||
|
||||
|
||||
### This repository is an experimental fork!
|
||||
## Creating SQL Views
|
||||
|
||||
I've made some breaking and possibly contentious changes in this fork
|
||||
with the end goal of simplifying usage of this library (and hopefully
|
||||
improving it in the process).
|
||||
```clj
|
||||
(require '[views.core :as views]
|
||||
'[views.honeysql.view :as vhsql])
|
||||
|
||||
This will be lacking in documentation and usage examples until I mostly
|
||||
finalize all the changes I'm intending on making.
|
||||
(def db ... ) ; a standard JDBC database connection map
|
||||
(def view-system ... ) ; pre-initialized view system
|
||||
|
||||
|
||||
; view functions. these are just functions that return HoneySQL maps.
|
||||
; (you could also use honeysql.core/build to build the HoneySQL maps if you wish)
|
||||
|
||||
(defn my-view-sql []
|
||||
{:select [:*]
|
||||
:from [:foo]})
|
||||
|
||||
(defn people-by-type-sql [type]
|
||||
{:select [:first_name :last_name]
|
||||
:from [:people]
|
||||
:where [:= :type type]})
|
||||
|
||||
|
||||
; add 2 views, :my-view and :people-by-type, to the view system
|
||||
|
||||
(views/add-views!
|
||||
view-system
|
||||
[(vhsql/view :my-view db my-view-sql)
|
||||
(vhsql/view :people-by-type db people-by-type-sql)])
|
||||
```
|
||||
|
||||
The calls to `views.honeysql.view/view` return instances of a
|
||||
`HSQLView` view. The "view functions" which contain the actual HoneySQL
|
||||
queries are called in two instances:
|
||||
|
||||
* When the view's data is being refreshed. The view function is called
|
||||
to get the SQL to be run via `clojure.java.jdbc/query` using the `db`
|
||||
connection that was provided to the view.
|
||||
* Whenever hints are being checked for relevancy against the view when
|
||||
the view system is determining whether the view needs to be refreshed
|
||||
or not.
|
||||
|
||||
Note also that the view functions can take any number of parameters
|
||||
which are provided during view subscription:
|
||||
|
||||
```clj
|
||||
(require '[views.core :refer [subscribe! ->view-sig]])
|
||||
|
||||
(subscribe! view-system (->view-sig :my-namespace :my-view []) 123 nil)
|
||||
(subscribe! view-system (->view-sig :my-namespace :people-by-type ["student"]) 123 nil)
|
||||
```
|
||||
|
||||
### Extra Features and Options
|
||||
|
||||
You can use clojure.java.jdbc's `:row-fn` and `:result-set-fn` (see
|
||||
[here][4] and [here][5] for more info on what these options are) with
|
||||
HoneySQL views:
|
||||
|
||||
```clj
|
||||
(vhsql/view :foobar-view db foobar-view-sql {:row-fn my-row-fn
|
||||
:result-set-fn my-result-set-fn})
|
||||
```
|
||||
|
||||
[4]: http://clojure-doc.org/articles/ecosystem/java_jdbc/using_sql.html#processing-each-row-lazily
|
||||
[5]: http://clojure-doc.org/articles/ecosystem/java_jdbc/using_sql.html#processing-a-result-set-lazily
|
||||
|
||||
Additionally the `db` argument can be a function that accepts a
|
||||
namespace and returns a standard database connection map.
|
||||
|
||||
```clj
|
||||
(defn db-selector [namespace]
|
||||
(case namespace
|
||||
:foo foo-db
|
||||
:bar bar-db
|
||||
default-db))
|
||||
|
||||
(vhsql/view :people-by-type db-selector people-by-type-sql)
|
||||
```
|
||||
|
||||
In this case, `db-selector` would be called only when the view data is
|
||||
being refreshed (it is not used during hint relevancy checks). The
|
||||
namespace that would be passed in is taken from the view
|
||||
subscription(s) for which the view is being refresh for (so it could
|
||||
be anything, even `nil`... whatever was provided as the namespace at
|
||||
the time subscriptions are created).
|
||||
|
||||
|
||||
## Running INSERT/UPDATE/DELETE Queries
|
||||
|
||||
Instead of using clojure.java.jdbc's `execute!` or `query!`, you
|
||||
should instead use `views.honeysql.core/vexec!`:
|
||||
|
||||
```clj
|
||||
(require '[views.honeysql.core :refer [vexec!]])
|
||||
|
||||
(vexec! view-system db
|
||||
{:insert-into :people
|
||||
:values [{:type "student"
|
||||
:first_name "Foo"
|
||||
:last_name "Bar"}]})
|
||||
```
|
||||
|
||||
This will both, execute the SQL query and also analyze it to determine
|
||||
what hints need to be added to the view system and then add them.
|
||||
|
||||
With the above `vexec!` call the hints that would be added to the view
|
||||
system would trigger view refreshes for anyone subscribed to any
|
||||
HoneySQL views in the system that use a SELECT query to retrieve data
|
||||
from the "people" table (either using another simple SELECT, or JOINing
|
||||
it with other tables as part of a larger query, a sub-SELECT, etc).
|
||||
|
||||
### Transactions
|
||||
|
||||
If you need to run some SQL queries within a transaction, you should
|
||||
use `views.honeysql.core/with-view-transaction` instead of
|
||||
clojure.java.jdbc's `with-db-transaction`. It basically works exactly
|
||||
the same:
|
||||
|
||||
```clj
|
||||
(require '[views.honeysql.core :refer [with-view-transaction]])
|
||||
|
||||
(with-view-transaction
|
||||
view-system ; need to pass in the view-system atom
|
||||
[dt db]
|
||||
(vexec! view-system dt
|
||||
{:insert-into :users
|
||||
:values [{:username "fbar"}]})
|
||||
(vexec! view-system dt
|
||||
{:insert-into :people
|
||||
:values [{:type "student"
|
||||
:first_name "Foo"
|
||||
:last_name "Bar"
|
||||
:user_id {:select [:u.user_id]
|
||||
:from [[:users :u]]
|
||||
:where [:= :u.username "fbar"]}}]}))
|
||||
```
|
||||
|
||||
The hints generated by any `vexec!` calls within a transaction are
|
||||
collected in a list and only at the end of the (successful) transaction
|
||||
are they added to the view system.
|
||||
|
||||
### Namespaces
|
||||
|
||||
Namespaces can be specified in an additional options map as the last
|
||||
argument to `vexec!`. If you don't provide this, then a `nil` namespace
|
||||
is used for the hints sent to the view system.
|
||||
|
||||
```clj
|
||||
(vexec! view-system db
|
||||
{:insert-into :people
|
||||
:values [{:type "student"
|
||||
:first_name "Foo"
|
||||
:last_name "Bar"}]}
|
||||
{:namespace :my-namespace)
|
||||
```
|
||||
|
||||
|
||||
## Hints
|
||||
|
||||
Hints for the view system are automatically determined from the SQL
|
||||
queries being used in the view functions and from `vexec!` calls by
|
||||
analyzing the HoneySQL map and figuring out what tables are being
|
||||
queried from or changed. All you need to do is write the HoneySQL query.
|
||||
|
||||
The hints themselves are simply SQL table names represented as
|
||||
keywords, e.g. `:people` for the "people" table. Hints are considered
|
||||
relevant to a HoneySQL view if the list of tables being queried from in
|
||||
the view's SELECT statement have at least some matches against the
|
||||
hints being compared against.
|
||||
|
||||
Since HoneySQL maps are easily parsed, this should "just work" as long
|
||||
as you're writing correctly formatted HoneySQL. HoneySQL gives you
|
||||
various ways to also make use of vendor-specific extensions should you
|
||||
need them, and this shouldn't be a problem when it comes time to parsing
|
||||
the HoneySQL map to get the hints from it.
|
||||
|
||||
> Hints generated by views.honeysql are compatible with the hints
|
||||
> generated by [views.sql][6], so you can easily mix-and-match these
|
||||
> views within the same system and get view refreshes triggered as you
|
||||
> would expect for both types of views.
|
||||
|
||||
[6]: https://github.com/gered/views.sql
|
||||
|
||||
|
||||
## License
|
||||
|
|
Loading…
Reference in a new issue