weavejester / ragtime

Database-independent migration library
Eclipse Public License 1.0
610 stars 85 forks source link

Richer EDN syntax #98

Closed plexus closed 8 years ago

plexus commented 8 years ago

This is a suggestion on how to extend Ragtime. I found myself using this in my own project. I'm not sure if this is in line with the design ideas of Ragtime, but if there is interest to add this then I'd be happy to cook up a proper PR.

The idea is, in EDN files, to allow not just strings of raw sql, but also structured expressions, so you get structural editing, syntax highlighting, etc.

The result looks like this

{:up [(create-table :fruit
                    [:name "varchar(32)" :primary :key]
                    [:appearance "varchar(32)"]
                    [:cost :int]
                    [:grade :real])]
 :down [(drop-table :fruit)]}

The syntax for the table name and fields is identical to what clojure.java.jdbc/create-table-ddl uses, in fact it just delegates to create-table-ddl and friends.

This is what my current implementation looks like. It uses a different file extension (.migration), but it could just as well replace the .edn implementation, it would be backwards compatible.

The code is a bit hack-ish (pokes into private vars), so it would need some cleanup.

(ns lambdaisland.db.migrations
  (require [clojure.edn :as edn]
           [clojure.java.jdbc :as jdbc]
           [ragtime.jdbc :as ragtime :refer [load-files sql-migration]]))

(def commands
  {'create-table jdbc/create-table-ddl
   'drop-table jdbc/drop-table-ddl
   'insert @#'jdbc/insert-sql
   'update @#'jdbc/update-sql
   'delete @#'jdbc/delete-sql})

(defn spec->sql [s]
  (cond
    (string? s) s
    (list? s)
    (let [[cmd & args] s]
      (apply (commands cmd) args))))

(defmethod load-files ".migration" [files]
  (for [file files]
    (-> (slurp file)
        (edn/read-string)
        (update-in [:id] #(or % (-> file
                                    (@#'ragtime/basename)
                                    (@#'ragtime/remove-extension))))
        (update-in [:up] #(mapv spec->sql %))
        (update-in [:down] #(mapv spec->sql %))
        (sql-migration))))
weavejester commented 8 years ago

Code that generates SQL is a rabbit hole that I don't want to go down in Ragtime itself. However, as Ragtime is extensible, you could create your own library that parses that syntax and converts it into records or objects that satisfy the Migration protocol.

plexus commented 8 years ago

Alright, good to know. Thanks for the speedy response!