• Stars
    star
    210
  • Rank 187,585 (Top 4 %)
  • Language
    Clojure
  • License
    Eclipse Public Li...
  • Created about 12 years ago
  • Updated 7 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A Clojure & ClojureScript DSL for SQL

SQLingvo

https://img.shields.io/clojars/v/sqlingvo.svg https://github.com/r0man/sqlingvo/workflows/Clojure%20CI/badge.svg https://versions.deps.co/r0man/sqlingvo/status.svg https://versions.deps.co/r0man/sqlingvo/downloads.svg

SQLingvo is an embedded Clojure and ClojureScript DSL that allows you to build SQL statements within your favorite LISP. The SQL statements used by SQLingvo are compatible with the next.jdbc, clojure.java.jdbc, clojure.jdbc, postgres.async and node-postgres libraries.

If you want to execute SQL statements on Node.js, take a look at SQLingvo.node.

Note: SQLingvo is designed for the PostgreSQL database management system. That said, if you can avoid PostgreSQL specific features, you might be lucky and use it with other databases as well.

https://imgs.xkcd.com/comics/query.png

Usage

SQLingvo shadows some functions from the clojure.core namespace, such as distinct, group-by and update functions. It’s recommended to require the sqlingvo.core namespace via an alias, such as sql.

(require '[sqlingvo.core :as sql])

Database specification

SQLingvo uses a database specification to configure how SQL identifiers are quoted and column and table names are translated between Clojure and your database. The following code defines a database specification using the naming and quoting strategy for PostgreSQL.

(def my-db (sql/db :postgresql))

Such a database specification is needed by all functions that produce SQL statements. The following code uses the database specification my-db to build a simple SELECT statement.

(sql/sql (sql/select my-db [:first-name]
           (sql/from :people)))
["SELECT \"first-name\" FROM \"people\""]

Naming strategy

The naming strategy is used to configure how column and table names are translated between Clojure and the SQL dialect of the database. The strategy can be configured with the :sql-name entry in a database specification.

The default strategy used is clojure.core/name, which translates a Clojure keyword to a string.

A common use case is to translate from a keyword to a string and replace all hyphens with underscores. This can be done with the following code:

(require '[clojure.string :as str])

(defn underscore [s]
  (str/replace (name s) "-" "_"))

(def my-db' (sql/db :postgresql {:sql-name underscore}))

All the hyphens in column and table names are now translated to underscores.

(sql/sql (sql/select my-db' [:first-name]
           (sql/from :people)))
["SELECT \"first_name\" FROM \"people\""]

Quoting strategy

The quoting strategy defines how column and table names are quoted when building SQL. The strategy can be configured with the :sql-quote entry in a database specification.

You could change the quoting strategy with the following code:

(require '[sqlingvo.util :refer [sql-quote-backtick]])
(def my-db' (sql/db :postgresql {:sql-quote sql-quote-backtick}))

Now the column and table names are quoted with back ticks, instead of double quotes.

(sql/sql (sql/select my-db' [:first-name]
           (sql/from :people)))
["SELECT `first-name` FROM `people`"]

Placeholder strategy

The placeholder strategy defines how placeholders for SQL parameters are generated when building statements. The default sql-placeholder-constant strategy always uses the string ?, the sql-placeholder-count strategy uses increasing values starting from $1, $2, etc.

The strategy can be configured with the :sql-placeholder entry in a database specification.

(require '[sqlingvo.util :refer [sql-placeholder-count]])
(def my-db' (sql/db :postgresql {:sql-placeholder sql-placeholder-count}))

Now, the placeholders for SQL parameters will contain the index number of the parameter. Use this strategy if you are using SQLingvo with postgres.async.

(sql/sql (sql/select my-db'  [:*]
           (sql/from :distributors)
           (sql/where '(and (= :dname "Anvil Distribution")
                            (= :zipcode "21201")))))
["SELECT * FROM \"distributors\" WHERE ((\"dname\" = $1) and (\"zipcode\" = $2))" "Anvil Distribution" "21201"]

SQL statement

SQLingvo comes with functions for common SQL commands like select, insert, update and more. These functions return an instance of sqlingvo.expr.Stmt, a data structure that can be compiled into SQL with the sql function, or used by other functions to build derived statements.

Here’s an example:

(def commendy-films-stmt
  (sql/select my-db [:id :name]
    (sql/from :films)
    (sql/where '(= :kind "Comedy"))))

In the code above we select all the id and name columns of all rows in the films table that have a kind column with the value Comedy. The call to the select function returns and instance of sqlingvo.expr.Stmt, which is bound to the commendy-films-stmt var.

(class commendy-films-stmt)
sqlingvo.expr.Stmt

This instance can be compiled into SQL with the sql function. The result is a Clojure vector with the first entry being the compiled SQL string and the remaining entries the prepared statement parameters.

(sql/sql commendy-films-stmt)
["SELECT \"id\", \"name\" FROM \"films\" WHERE (\"kind\" = ?)" "Comedy"]

Those vectors could be fed to the clojure.jdbc and clojure.java.jdbc libraries to actually execute a statement.

Printing in the REPL

There is a print-method defined for the sqlingvo.expr.Stmt class, so instances of a statement are printed in their compiled from. This is convenient when building SQL statements in the REPL. If you type the following example directly into your REPL, it prints out the compiled form of the statement.

(sql/select my-db [:id :name]
  (sql/from :films)
  (sql/where '(= :kind "Comedy")))
["SELECT \"id\", \"name\" FROM \"films\" WHERE (\"kind\" = ?)" "Comedy"]

But the return value of the call to the select function above is still an instance of sqlingvo.expr.Stmt.

(class *1)
sqlingvo.expr.Stmt

SQL expressions

SQLingvo compiles SQL expressions from Clojure prefix notation into SQL. There’s built-in support for special operators, such as +, -, *, / and many others.

(sql/select my-db [1 '(+ 2 (abs 3)) '(upper "Hello")])
["SELECT 1, (2 + abs(3)), upper(?)" "Hello"]

You can influence the compilation of functions by extending the compile-fn multi method. In case a function uses a special compilation rule that is not built in, take a look at the multi method implementation of substring to see how to create your own compilation rule. Or even better, send a PR …

(sql/select my-db ['(substring "Fusion" from 2 for 3)])
["SELECT substring(? from 2 for 3)" "Fusion"]

Syntax quoting

When using SQLingvo to build parameterized SQL statements, you often want to use the parameters in a SQL expression. This can be accomplished with syntax quoting. Note the back tick character in the where clause.

(defn films-by-kind [db kind]
  (sql/select db [:id :name]
    (sql/from :films)
    (sql/where `(= :kind ~kind))))
(films-by-kind my-db "Action")
["SELECT \"id\", \"name\" FROM \"films\" WHERE (\"kind\" = ?)" "Action"]

Detailed SQL examples

The following examples show how to build SQL statements found in the PostgreSQL documentation with SQLingvo. Note that we don’t call the sql function anymore, because we are only interested in the printed result.

Copy

Copy from standard input.

(sql/copy my-db :country []
  (sql/from :stdin))
["COPY \"country\" FROM STDIN"]

Copy data from a file into the country table.

(sql/copy my-db :country []
  (sql/from "/usr1/proj/bray/sql/country_data"))
["COPY \"country\" FROM ?" "/usr1/proj/bray/sql/country_data"]

Copy data from a file into the country table with columns in the given order.

(sql/copy my-db :country [:id :name]
  (sql/from "/usr1/proj/bray/sql/country_data"))
["COPY \"country\" (\"id\", \"name\") FROM ?" "/usr1/proj/bray/sql/country_data"]

Create table

Define a new database table.

(sql/create-table my-db :films
  (sql/column :code :char :length 5 :primary-key? true)
  (sql/column :title :varchar :length 40 :not-null? true)
  (sql/column :did :integer :not-null? true)
  (sql/column :date-prod :date)
  (sql/column :kind :varchar :length 10)
  (sql/column :len :interval)
  (sql/column :created-at :timestamp-with-time-zone :not-null? true :default '(now))
  (sql/column :updated-at :timestamp-with-time-zone :not-null? true :default '(now)))
["CREATE TABLE \"films\" (\"code\" CHAR PRIMARY KEY, \"title\" VARCHAR NOT NULL, \"did\" INTEGER NOT NULL, \"date-prod\" DATE, \"kind\" VARCHAR, \"len\" INTERVAL, \"created-at\" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), \"updated-at\" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now())"]

Delete

Clear the table films.

(sql/delete my-db :films)
["DELETE FROM \"films\""]

Delete all films but musicals.

(sql/delete my-db :films
  (sql/where '(<> :kind "Musical")))
["DELETE FROM \"films\" WHERE (\"kind\" <> ?)" "Musical"]

Delete completed tasks, returning full details of the deleted rows.

(sql/delete my-db :tasks
  (sql/where '(= :status "DONE"))
  (sql/returning :*))
["DELETE FROM \"tasks\" WHERE (\"status\" = ?) RETURNING *" "DONE"]

Insert

Insert expressions

Insert expressions into the films table.

(sql/insert my-db :films [:code :title :did :date-prod :kind]
  (sql/values [['(upper "t_601") "Yojimbo" 106 "1961-06-16" "Drama"]]))
["INSERT INTO \"films\" (\"code\", \"title\", \"did\", \"date-prod\", \"kind\") VALUES (upper(?), ?, 106, ?, ?)" "t_601" "Yojimbo" "1961-06-16" "Drama"]

Insert expressions and default values into the films table.

(sql/insert my-db :films []
  (sql/values [["UA502" "Bananas" 105 :DEFAULT "Comedy" "82 minutes"]
               ["T_601" "Yojimbo" 106 :DEFAULT "Drama" :DEFAULT]]))
["INSERT INTO \"films\" VALUES (?, ?, 105, DEFAULT, ?, ?), (?, ?, 106, DEFAULT, ?, DEFAULT)" "UA502" "Bananas" "Comedy" "82 minutes" "T_601" "Yojimbo" "Drama"]

Insert records

Insert records into the films table.

(sql/insert my-db :films []
  (sql/values [{:code "B6717" :title "Tampopo" :did 110 :date-prod "1985-02-10" :kind "Comedy"},
               {:code "HG120" :title "The Dinner Game" :did 140 :date-prod "1985-02-10" :kind "Comedy"}]))
["INSERT INTO \"films\" (\"code\", \"date-prod\", \"did\", \"kind\", \"title\") VALUES (?, ?, 110, ?, ?), (?, ?, 140, ?, ?)" "B6717" "1985-02-10" "Comedy" "Tampopo" "HG120" "1985-02-10" "Comedy" "The Dinner Game"]

Insert returning records

Insert a row into the films table and return the inserted records.

(sql/insert my-db :films []
  (sql/values [{:code "T_601" :title "Yojimbo" :did 106 :date-prod "1961-06-16" :kind "Drama"}])
  (sql/returning :*))
["INSERT INTO \"films\" (\"code\", \"date-prod\", \"did\", \"kind\", \"title\") VALUES (?, ?, 106, ?, ?) RETURNING *" "T_601" "1961-06-16" "Drama" "Yojimbo"]

Insert default values

Insert a row consisting entirely of default values.

(sql/insert my-db :films []
  (sql/values :default))
["INSERT INTO \"films\" DEFAULT VALUES"]

Insert from a select statement

Insert rows into the films table from the tmp-films table with the same column layout as films.

(sql/insert my-db :films []
  (sql/select my-db [:*]
    (sql/from :tmp-films)
    (sql/where '(< :date-prod "2004-05-07"))))
["INSERT INTO \"films\" SELECT * FROM \"tmp-films\" WHERE (\"date-prod\" < ?)" "2004-05-07"]

Insert or update rows on conflict

Insert or update new distributors as appropriate. Assumes a unique index has been defined that constrains values appearing in the did column. Note that the special excluded table is used to reference values originally proposed for insertion:

(sql/insert my-db :distributors [:did :dname]
  (sql/values [{:did 5 :dname "Gizmo Transglobal"}
               {:did 6 :dname "Associated Computing, Inc"}])
  (sql/on-conflict [:did]
    (sql/do-update {:dname :EXCLUDED.dname})))
["INSERT INTO \"distributors\" (\"did\", \"dname\") VALUES (5, ?), (6, ?) ON CONFLICT (\"did\") DO UPDATE SET \"dname\" = EXCLUDED.\"dname\"" "Gizmo Transglobal" "Associated Computing, Inc"]

Insert or do nothing on conflict

Insert a distributor, or do nothing for rows proposed for insertion when an existing, excluded row (a row with a matching constrained column or columns after before row insert triggers fire) exists. Example assumes a unique index has been defined that constrains values appearing in the did column:

(sql/insert my-db :distributors [:did :dname]
  (sql/values [{:did 7 :dname "Redline GmbH"}])
  (sql/on-conflict [:did]
    (sql/do-nothing)))
["INSERT INTO \"distributors\" (\"did\", \"dname\") VALUES (7, ?) ON CONFLICT (\"did\") DO NOTHING" "Redline GmbH"]

Insert or update rows on conflict with condition

Don’t update existing distributors based in a certain ZIP code.

(sql/insert my-db (as :distributors :d) [:did :dname]
  (sql/values [{:did 8 :dname "Anvil Distribution"}])
  (sql/on-conflict [:did]
    (sql/do-update {:dname '(:|| :EXCLUDED.dname " (formerly " :d.dname ")")})
    (sql/where '(:<> :d.zipcode "21201"))))
["INSERT INTO \"distributors\" AS \"d\" (\"did\", \"dname\") VALUES (8, ?) ON CONFLICT (\"did\") DO UPDATE SET \"dname\" = (EXCLUDED.\"dname\" || ? || \"d\".\"dname\" || ?) WHERE (\"d\".\"zipcode\" <> ?)" "Anvil Distribution" " (formerly " ")" "21201"]

Insert or do nothing by constraint

Name a constraint directly in the statement. Uses associated index to arbitrate taking the DO NOTHING action.

(sql/insert my-db :distributors [:did :dname]
  (sql/values [{:did 9 :dname "Antwerp Design"}])
  (sql/on-conflict-on-constraint :distributors_pkey
    (sql/do-nothing)))
["INSERT INTO \"distributors\" (\"did\", \"dname\") VALUES (9, ?) ON CONFLICT ON CONSTRAINT \"distributors_pkey\" DO NOTHING" "Antwerp Design"]

Join

Join the weathers table with the cities table.

(sql/select my-db [:*]
  (sql/from :weather)
  (sql/join :cities.name :weather.city))
["SELECT * FROM \"weather\" JOIN \"cities\" ON (\"cities\".\"name\" = \"weather\".\"city\")"]

The code above is a common use case and is syntactic sugar for the following. Use this version if you want to join on an arbitrary SQL expression.

(sql/select my-db [:*]
  (sql/from :weather)
  (sql/join :cities '(on (= :cities.name :weather.city))))
["SELECT * FROM \"weather\" JOIN \"cities\" ON (\"cities\".\"name\" = \"weather\".\"city\")"]

The type of join can be given as a keyword argument.

(sql/select my-db [:*]
  (sql/from :weather)
  (sql/join :cities '(on (= :cities.name :weather.city)) :type :inner))
["SELECT * FROM \"weather\" INNER JOIN \"cities\" ON (\"cities\".\"name\" = \"weather\".\"city\")"]

Select

Select all films.

(sql/select my-db [:*]
  (sql/from :films))
["SELECT * FROM \"films\""]

Select all Comedy films.

(sql/select my-db [:*]
  (sql/from :films)
  (sql/where '(= :kind "Comedy")))
["SELECT * FROM \"films\" WHERE (\"kind\" = ?)" "Comedy"]

Retrieve the most recent weather report for each location.

(sql/select my-db (sql/distinct [:location :time :report] :on [:location])
  (sql/from :weather-reports)
  (sql/order-by :location (desc :time)))
["SELECT DISTINCT ON (\"location\") \"location\", \"time\", \"report\" FROM \"weather-reports\" ORDER BY \"location\", \"time\" DESC"]

Update

Change the word Drama to Dramatic in the kind column of the films table.

(sql/update my-db :films {:kind "Dramatic"}
  (sql/where '(= :kind "Drama")))
["UPDATE \"films\" SET \"kind\" = ? WHERE (\"kind\" = ?)" "Dramatic" "Drama"]

Change all the values in the kind column of the table films to upper case.

(sql/update my-db :films {:kind '(upper :kind)})
["UPDATE \"films\" SET \"kind\" = upper(\"kind\")"]

Order by

The sort expression(s) can be any expression that would be valid in the query’s select list.

(sql/select my-db [:a :b]
  (sql/from :table-1)
  (sql/order-by '(+ :a :b) :c))
["SELECT \"a\", \"b\" FROM \"table-1\" ORDER BY (\"a\" + \"b\"), \"c\""]

A sort expression can also be the column label

(sql/select my-db [(sql/as '(+ :a :b) :sum) :c]
  (sql/from :table-1)
  (sql/order-by :sum))
["SELECT (\"a\" + \"b\") AS \"sum\", \"c\" FROM \"table-1\" ORDER BY \"sum\""]

or the number of an output column.

(sql/select my-db [:a '(max :b)]
  (sql/from :table-1)
  (sql/group-by :a)
  (sql/order-by 1))
["SELECT \"a\", max(\"b\") FROM \"table-1\" GROUP BY \"a\" ORDER BY 1"]

Having clause

Groups can be restricted via a HAVING clause.

(sql/select my-db [:city '(max :temp-lo)]
  (sql/from :weather)
  (sql/group-by :city)
  (sql/having '(< (max :temp-lo) 40)))
["SELECT \"city\", max(\"temp-lo\") FROM \"weather\" GROUP BY \"city\" HAVING (max(\"temp-lo\") < 40)"]

Values

A bare VALUES command.

(sql/values my-db [[1 "one"] [2 "two"] [3 "three"]])
["VALUES (1, ?), (2, ?), (3, ?)" "one" "two" "three"]

This will return a table of two columns and three rows. It’s effectively equivalent to.

(sql/union
 {:all true}
 (sql/select my-db [(sql/as 1 :column1) (sql/as "one" :column2)])
 (sql/select my-db [(sql/as 2 :column1) (sql/as "two" :column2)])
 (sql/select my-db [(sql/as 3 :column1) (sql/as "three" :column2)]))
["SELECT 1 AS \"column1\", ? AS \"column2\" UNION ALL SELECT 2 AS \"column1\", ? AS \"column2\" UNION ALL SELECT 3 AS \"column1\", ? AS \"column2\"" "one" "two" "three"]

More usually, VALUES is used within a larger SQL command. The most common use is in INSERT.

(sql/insert my-db :films []
  (sql/values [{:code "T-601"
                :title "Yojimbo"
                :did 106
                :date-prod "1961-06-16"
                :kind "Drama"}]))
["INSERT INTO \"films\" (\"code\", \"date-prod\", \"did\", \"kind\", \"title\") VALUES (?, ?, 106, ?, ?)" "T-601" "1961-06-16" "Drama" "Yojimbo"]

In the context of INSERT, entries of a VALUES list can be DEFAULT to indicate that the column default should be used here instead of specifying a value.

(sql/insert my-db :films []
  (sql/values [["UA502" "Bananas" 105 :DEFAULT "Comedy" "82 minutes"]
               ["T_601" "Yojimbo" 106 :DEFAULT "Drama" :DEFAULT]]))
["INSERT INTO \"films\" VALUES (?, ?, 105, DEFAULT, ?, ?), (?, ?, 106, DEFAULT, ?, DEFAULT)" "UA502" "Bananas" "Comedy" "82 minutes" "T_601" "Yojimbo" "Drama"]

VALUES can also be used where a sub SELECT might be written, for example in a FROM clause:

(sql/select my-db [:f.*]
  (sql/from (sql/as :films :f)
            (sql/as (sql/values [["MGM" "Horror"] ["UA" "Sci-Fi"]])
                    :t [:studio :kind]))
  (sql/where '(and (= :f.studio :t.studio)
                   (= :f.kind :t.kind))))
["SELECT \"f\".* FROM \"films\" \"f\", (VALUES (?, ?), (?, ?)) AS \"t\" (\"studio\", \"kind\") WHERE ((\"f\".\"studio\" = \"t\".\"studio\") and (\"f\".\"kind\" = \"t\".\"kind\"))" "MGM" "Horror" "UA" "Sci-Fi"]

Note that an AS clause is required when VALUES is used in a FROM clause, just as is true for SELECT. It is not required that the AS clause specify names for all the columns, but it’s good practice to do so. (The default column names for VALUES are column1, column2, etc in PostgreSQL, but these names might be different in other database systems.)

(sql/update my-db :employees
  {:salary '(* :salary :v.increase)}
  (sql/from (sql/as (sql/values [[1 200000 1.2] [2 400000 1.4]])
                    :v [:depno :target :increase]))
  (sql/where '(and (= :employees.depno :v.depno)
                   (>= :employees.sales :v.target))))
["UPDATE \"employees\" SET \"salary\" = (\"salary\" * \"v\".\"increase\") FROM (VALUES (1, 200000, 1.2), (2, 400000, 1.4)) AS \"v\" (\"depno\", \"target\", \"increase\") WHERE ((\"employees\".\"depno\" = \"v\".\"depno\") and (\"employees\".\"sales\" >= \"v\".\"target\"))"]

When VALUES is used in INSERT, the values are all automatically coerced to the data type of the corresponding destination column. When it’s used in other contexts, it might be necessary to specify the correct data type. If the entries are all quoted literal constants, coercing the first is sufficient to determine the assumed type for all:

(sql/select my-db [:*]
  (sql/from :machines)
  (sql/where `(in :ip-address
                  ~(sql/values [['(cast "192.168.0.1" :inet)]
                                ["192.168.0.10"]
                                ["192.168.1.43"]]))))
["SELECT * FROM \"machines\" WHERE \"ip-address\" IN (VALUES (CAST(? AS INET)), (?), (?))" "192.168.0.1" "192.168.0.10" "192.168.1.43"]

With Queries / Common table expressions

You can compose more complex SQL statements with common table expressions.

Define the regional-sales and top-regions helper functions.

(defn regional-sales [db]
  (sql/select db [:region (sql/as '(sum :amount) :total-sales)]
    (sql/from :orders)
    (sql/group-by :region)))
(defn top-regions [db]
  (sql/select db [:region]
    (sql/from :regional-sales)
    (sql/where `(> :total-sales
                   ~(sql/select db ['(/ (sum :total-sales) 10)]
                      (sql/from :regional-sales))))))

And use them in a common table expression.

(sql/with my-db [:regional-sales (regional-sales my-db)
                 :top-regions (top-regions my-db)]
  (sql/select my-db [:region :product
                     (sql/as '(sum :quantity) :product-units)
                     (sql/as '(sum :amount) :product-sales)]
    (sql/from :orders)
    (sql/where `(in :region ~(sql/select my-db [:region]
                               (sql/from :top-regions))))
    (sql/group-by :region :product)))
["WITH \"regional-sales\" AS (SELECT \"region\", sum(\"amount\") AS \"total-sales\" FROM \"orders\" GROUP BY \"region\"), \"top-regions\" AS (SELECT \"region\" FROM \"regional-sales\" WHERE (\"total-sales\" > (SELECT (sum(\"total-sales\") / 10) FROM \"regional-sales\"))) SELECT \"region\", \"product\", sum(\"quantity\") AS \"product-units\", sum(\"amount\") AS \"product-sales\" FROM \"orders\" WHERE \"region\" IN (SELECT \"region\" FROM \"top-regions\") GROUP BY \"region\", \"product\""]

For more complex examples, look at the tests.

License

Copyright © 2012-2020 r0man

Distributed under the Eclipse Public License, the same as Clojure.

More Repositories

1

sablono

Lisp/Hiccup style templating for Facebook's React in ClojureScript.
Clojure
696
star
2

cljs-http

A ClojureScript HTTP library.
Clojure
576
star
3

inflections-clj

Rails-like inflection library for Clojure and ClojureScript
Clojure
207
star
4

ring-cors

Ring middleware for Cross-Origin Resource Sharing.
Clojure
165
star
5

oauth-clj

Clojure OAuth library
Clojure
93
star
6

geocoder-clj

A Clojure library for various geocoder services.
Clojure
43
star
7

soundklaus.el

Play music on SoundCloud with Emacs via EMMS
Emacs Lisp
42
star
8

svm-clj

A LibSVM wrapper for Clojure
Clojure
34
star
9

geo-clj

Experimental Clojure geo library.
Clojure
29
star
10

docopt.el

A Docopt implementation in Emacs Lisp.
Emacs Lisp
27
star
11

hdfs-clj

A Clojure library for the Hadoop Distributed File System
Clojure
27
star
12

.emacs.d

My Emacs configuration
Emacs Lisp
25
star
13

asahi-guix

Asahi Linux on GNU Guix
18
star
14

datumbazo

A JDBC driver for SQLingvo
Clojure
16
star
15

netcdf-clj

NetCDF Clojure Library
Clojure
14
star
16

closure-templates-clj

Clojure Library for Google's Closure Templates.
Clojure
11
star
17

grafeo

A GraphQL document and schema language based on S-expressions in Clojure & ClojureScript
Clojure
11
star
18

validation-clj

A validation library for Clojure.
Clojure
11
star
19

dotfiles

My Dot Files
Shell
10
star
20

commandline-clj

Clojure command line parsing library.
Clojure
9
star
21

noencore

Clojure/ClojureScript functions not in core.
Clojure
9
star
22

acts_as_rateable

Fork of Juixe Software's acts_as_rateable plugin.
Ruby
9
star
23

paimon.el

An Emacs mode for Splunk
Emacs Lisp
9
star
24

geonames-clj

Clojure API for GeoNames.
Clojure
8
star
25

sqlingvo.node

A ClojureScript driver for SQLingvo on Node.js.
Clojure
8
star
26

google-maps-clj

Google Maps Clojure Library
Clojure
8
star
27

postgis.spec

Clojure specs and generators for PostGIS types
Clojure
8
star
28

hive-el

Hive SQL mode extension for Emacs
Emacs Lisp
7
star
29

mahout-in-action

Clojure Examples from Mahout in Action
Clojure
7
star
30

hal-clj

A Clojure library to build HAL maps.
Clojure
6
star
31

acts_as_voteable

Fork of Juixe Software's acts_as_voteable plugin.
Ruby
5
star
32

migrate-clj

Rails-like Database Migration for Clojure
Clojure
5
star
33

rum-mdc

Material Design components for Rum
CSS
5
star
34

closure-lint-mode

An Emacs Mode for the Closure Linter
5
star
35

cascalog-nutch

Cascading Schemes and Cascalog helpers for Nutch sequence files.
Clojure
4
star
36

routes-clj

A Clojure & ClojureScript library to build url and path fns.
Clojure
4
star
37

lein-dpkg

Leiningen plugin for the Debian package management system
Clojure
4
star
38

flickrj

Java
4
star
39

freebase-clj

Clojure Library for Freebase.
Clojure
3
star
40

rest-clj

Clojure
2
star
41

request-clj

A HTTP library for Clojure & ClojureScript.
Clojure
2
star
42

diplomarbeit

Emacs Lisp
2
star
43

lein-env

Leiningen project environments.
Clojure
2
star
44

plz-media-type

A plz.el extension library to handle media types.
Emacs Lisp
2
star
45

conduit-rabbitmq

A library for building distributed applications in Clojure using RabbitMQ
Clojure
1
star
46

ovh-clj

A Clojure Library for the OVH Web Services.
Java
1
star
47

vertica-el

Vertica SQL mode extension for Emacs
Emacs Lisp
1
star
48

immutant-stackoverflow

Clojure
1
star
49

om-unmount-test

Clojure
1
star
50

relajso

Clojure
1
star
51

conduit

A library for stream processing in Clojure
Clojure
1
star
52

cascalog-printtap

A Cascalog tap that prints tuples via Clojure's pr-str function.
Clojure
1
star
53

arrows

A library for creating arrows in Clojure
Clojure
1
star
54

r0man.github.com

My GitHub pages.
1
star
55

guix-system

Scheme
1
star
56

sqlingvo.ksql

A Clojure DSL to build SQL statements for KSQL DB.
Clojure
1
star
57

ring-graphql-multipart

Ring middleware for GraphQL multipart form requests aka file uploads
Clojure
1
star