我开始学习Clojure,并希望对我为管理数据库迁移而编写的一些代码提供反馈。任何让它更健壮、更高效、更地道、更优雅的建议.不客气!
(ns myapp.models.migrations
(:require [clojure.java.jdbc :as sql]
[myapp.models.database :as db]))
;;;; Manages database migrations.
;;;;
;;;; Usage:
;;;;
;;;; user=> (migrate!) ; migrate to the latest version
;;;; user=> (migrate! 20140208) ; migrate to a specific version
(let [db-spec db/spec]
;; WARNING: Only works with PostgreSQL!
;;
;; TODO: Can this be made generic to all databases? Look into using the
;; JDBC database metadata to determine if a table exists.
(defn table-exists? [table-name]
(-> (sql/query db-spec
["select count(*) from pg_tables where tablename = ?" table-name])
first :count pos?))
;;; The migrations to apply
;;;
;;; The order in which migrations are apply is determined by the :version property.
;;; Each migration must have :apply and :remove functions so we can migrate up or down.
(def migration-0 {:version 0
:description "Starting point. Does nothing, but allows us to remove all other migrations if we want to."
:apply (fn [] nil)
:remove (fn [] nil)})
(def migration-20140208 {:version 20140208
:description "Create the articles table."
:apply (fn []
(when (not (table-exists? "articles"))
(sql/db-do-commands db-spec (sql/create-table-ddl :articles
[:title "varchar(32)"]
[:content "text"]))))
:remove (fn []
(when (table-exists? "articles")
(sql/db-do-commands db-spec (sql/drop-table-ddl :articles))))})
(def db-migrations [ migration-0
migration-20140208 ])
;;; Forms for processing the migrations.
(defn create-migrations-table! []
(when (not (table-exists? "migrations"))
(sql/db-do-commands db-spec
(sql/create-table-ddl :migrations [:version :int]))))
(defn drop-migrations-table! []
(when (table-exists? "migrations")
(sql/db-do-commands db-spec
(sql/drop-table-ddl :migrations))))
(defn migration-recorded? [migration]
(create-migrations-table!)
(-> (sql/query db-spec ["select count(*) from migrations where version = ?" (:version migration)])
first :count pos?))
(defn record-migration! [migration]
(create-migrations-table!)
(when (not (migration-recorded? migration))
(sql/insert! db-spec :migrations {:version (:version migration)})))
(defn erase-migration! [migration]
(create-migrations-table!)
(when (migration-recorded? migration)
(sql/delete! db-spec :migrations ["version = ?" (:version migration)])))
(defn migrate-up! [to-version]
(let [filtered-migrations (sort-by :version (filter #(<= (:version %) to-version) db-migrations))]
(doseq [m filtered-migrations]
(when (not (migration-recorded? m))
((:apply m))
(record-migration! m)))))
(defn migrate-down! [to-version]
(let [filtered-migrations (reverse (sort-by :version (filter #(> (:version %) to-version) db-migrations)))]
(doseq [m filtered-migrations]
(when (migration-recorded? m)
((:remove m))
(erase-migration! m)))))
(defn migrate!
([]
(let [last-migration (last (sort-by :version db-migrations))]
(when last-migration (migrate! (:version last-migration)))))
([to-version]
(let [version (or to-version 0)
migration-exists (not (nil? (some #(= (:version %) version) db-migrations)))
already-applied (migration-recorded? {:version version})]
(cond
(not migration-exists)
(println (format "migration %s was not found" version))
already-applied
(migrate-down! version)
:else
(migrate-up! version))))))发布于 2014-03-19 14:05:40
老实说,我觉得这个代码看起来很棒!这对一个刚学过Clojure的人来说特别好。我只做了几个小小的改进:
(defn create-migrations-table! []
(when-not (table-exists? "migrations")
(sql/db-do-commands db-spec
(sql/create-table-ddl :migrations [:version :int]))))使用(when-not x)而不是(when (not x)) --它将为您节省一些括号:)
(defn record-migration! [migration]
(create-migrations-table!)
(when-not (migration-recorded? migration)
(sql/insert! db-spec :migrations {:version (:version migration)})))(when-not也是如此)
(defn migrate-up! [to-version]
(let [filtered-migrations (sort-by :version (filter #(<= (:version %) to-version) db-migrations))]
(doseq [m filtered-migrations]
(when-not (migration-recorded? m)
((:apply m))
(record-migration! m)))))(使用when-not的另一个机会)
(defn migrate!
([]
(when-let [last-migration (last (sort-by :version db-migrations))]
(migrate! (:version last-migration))))
...只要您有(let [x (something)] (when x (do-something)))表单的语句,就可以将其简化为(when-let [x (something)] (do-something))。
最后,我会考虑调用migration-exists migration-exists?,因为它表示一个布尔值。
对我来说,唯一突出的是,您将(create-migrations-table!)作为第一行包含在其他几个函数中。这似乎是一种解决方案,并可能从函数式编程的角度引起问题。您可以考虑将(when-not (table-exists? "migrations" ...从create-migrations-table!的函数定义中删除,并将其作为检查包含在其他3个函数中,如下所示:
(defn create-migrations-table! []
(sql/db-do-commands db-spec
(sql/create-table-ddl :migrations [:version :int])))
(defn record-migration! [migration]
(when-not (table-exists? "migrations")
(create-migrations-table!))
(when-not (migration-recorded? migration)
(sql/insert! db-spec :migrations {:version (:version migration)}))) 在我看来,这种方式似乎更直观-- create-migrations-table!应该假设还没有这样的方法,除非您将(table-exists? "migrations")作为条件检查,否则您可能不会使用它。另一方面,这更冗长,所以为了简单起见,您可能更喜欢保留它的方式。
https://codereview.stackexchange.com/questions/41568
复制相似问题