Giter Club home page Giter Club logo

joplin's Introduction

Joplin

Joplin 0.3 contains many breaking changes over the 0.2 releases, please see here for details.

Joplin is a library for flexible datastore migration and seeding.

Joplin tries to solve the problems that arise when dealing with complicated systems consisting of multiple datastores. It lets you define and reason about environments (for instance dev, staging, UAT, prod).

Joplin lets you declare your databases, migrators, seed functions up front and combine them into different environments. It can be used from the REPL, via a leiningen aliases or be called programatically.

Joplin comes with plugins for SQL/JDBC databases, Datomic, ElasticSearch, Cassandra, DynamoDB, Hive and Zookeeper. It is built with extensibility in mind, adding more stores is done by a handful of multi-methods.

Joplin is built on top of ragtime.

Libraries

  • joplin.core - database independent tools and functions for managing migrations and seeds
  • joplin.cassandra - migrate and seed Cassandra clusters
  • joplin.dynamodb - migrate and seed DynamoDB clusters
  • joplin.datomic - migrate and seed Datomic databases
  • joplin.elasticsearch - migrate and seed Elasticsearch clusters
  • joplin.hive - migrate and seed Hive tables using Avro
  • joplin.jdbc - migrate and seed SQL databases with jdbc
  • joplin.zookeeper - seed Zookeeper clusters
  • clj-rethinkdb-migrations - migrate and seed RethinkDB clusters
  • joplin.mongodb - migrate and seed MongoDB

Installation

Add joplin.core as a dependency if you just want the database-independent core:

:dependencies [[joplin.core "0.3.11"]]

If you are not using Leiningen, add a dependency for the plugins of the databases you want to migrate;

:dependencies [[joplin.cassandra "0.3.11"]
               [joplin.dynamodb "0.3.11"]
               [joplin.datomic "0.3.11"]
               [joplin.elasticsearch "0.3.11"]
               [joplin.hive "0.3.11"]
               [joplin.jdbc "0.3.11"]
               [joplin.zookeeper "0.3.11"]]

Using Joplin

Defining the configuration map

The joplin configuration map contains the keys :databases, :migrators, :seeds and :environments. The first three are pure declarations and :environments is where these declarations gets combined. For more details on these 4 keys see the Concepts wiki page.

Typically you would define the configuration in a .edn file that you put somewhere on your classpath.

Please note that the folders with migrators and seed-var namespaces needs to be in the classpath in order for joplin to access them. Joplin will also look into any jar files on the classpath for resource folders with matching name.

Example of a joplin definition;

{
:migrators {:sql-mig "joplin/migrators/sql"}  ;; A path for a folder with migration files

:seeds {:sql-seed "seeds.sql/run"             ;; A clojure var (function) that applies the seed
        :es-seed "seeds.es/run"}

:databases {:sql-dev {:type :jdbc, :url "jdbc:h2:mem:dev"}
            :es-prod {:type :es, :host "es-prod.local", :port "9300", :cluster "dev"}
            :sql-prod {:type :jdbc, :url "jdbc:h2:file:prod"}}

;; We combine the definitions above into different environments
:environments {:dev [{:db :sql-dev, :migrator :sql-mig, :seed :sql-seed}]
               :prod [{:db :sql-prod, :migrator :sql-mig}
                      {:db :es-prod}, :seed :es-seed]}
}

Hiding secrets from the configuration map

Once the config map has been written to a .edn file you'll need to read that file and pass the datastructure to joplin's migration and seed functions. JDBC connection strings typically include username and password that you don't want to put into source control.

The joplin.repl namespace provides a function called load-config that take the name (or URL) of a file on the classpath (joplin.edn) and reads the contents turning it into Clojure datastructures. This function implements 2 tagged literals to solve the problem with secrets in the configuration; env and envf

:es-dev      {:type :es, :host #env TARGET_HOST, :port 9200}
:psql-prod   {:type :sql, :url #envf ["jdbc:postgresql://psq-prod/prod?user=%s&password=%s"
                                      PROD_USER PROD_PASSWD]}
  • env takes a single name of an environment variable that is put in its place in the result of the load-config call.
  • envf takes a vector of data, the first item is a string passed to Clojure's format function. The rest of the items in the vector are names of environment variables that will be looked up and passed along with the format string to the format call.

Overriding the migration table

For jdbc / sql migrators the migration table is called ragtime_migrations by default. You can change this by adding a key called :migrations-table to the database map.

Running migrations

The joplin.repl namespace defines a function for each of Joplin's 5 basic operations. It's common to use these functions to setup a 'migration and seed REPL' from which you can control your databases.

  • migrate [conf env & args]

Run all pending (up) migrations on either all databases in the environment or a single if the database is provided as the first argument after env. This operation is idempotent.

  • seed [conf env & args]

Run the seed functions on either all databases in the environment or a single if the database is provided as the first argument after env. This operation is NOT idempotent, running seed functions multiple time can lead to duplication of data.

Joplin will only run seed functions on databases that have been fully migrated, i.e. have no pending migrations.

  • rollback [conf env database amount-or-id & args]

Rollback N migrations (down migrate) from a single database in an environment. N can either be a number (the number to migration steps you want to roll back, or an id of a migration you want to roll back to). This command requires that you specify a db.

  • reset [conf env database & args]

Rollback ALL migrations, apply ALL up migrations and call the seed function for a single database in an environment. Intended to reset a database to a good state. This command require that you specify a db.

Note that reset will only work properly if all down migrations do a complete cleanup of the action taken by the up migration.

  • pending [conf env database & args]

Print pending migrations for a database in an environment.

  • create [conf env database & args]

Create a new migration scaffold file for a single database in an environment. This command requires that you specify both a database and identification string of the migrations.

Running migrations from the command line

When you want to script your migrations requiring a REPL is not convenient. In this case you can use Leiningen aliases to provide command-line commands for the 5 operation above. The example project shows to do do this in the project.clj and the code the aliases uses.

Migration conflict strategies

The extra args of the joplin.repl functions are used to pass extra options to ragtime, this is currently used for the migrate operation where different strategies for dealing with migration conflicts can be specified.

(migrate config :dev :cass-dev {:strategy ragtime.strategy/rebase})

Read here for info on available strategies.

Writing migrators

Joplin migrators defaults to be 'code driven'. They are basic Clojure files and allows the migrators to be as simple or complicated as needed. If you are working with a SQL database, there is a second flavor of migrators at your disposal; ragtime-style sql migrators.

Joplin default migrators

A migrator consist of a single clojure source file. This file must contain (at least) 2 function definitions, one called up and one called down. The migrators will be called with one argument, which is the Ragtime DataStore record created by the joplin plugin. These record will contain the information you need to make a connections to the database and inflict a change.

Example of migrator;

$ ls -1 migrators/cass
20140717174605_users.clj
$ cat migrators/cass/20140717174605_users.clj
(ns migrators.cass.20140717174605-users
  (:use [joplin.cassandra.database])
  (:require [clojurewerkz.cassaforte.cql    :as cql]
            [clojurewerkz.cassaforte.query  :as cq]))

(defn up [db]
  (let [conn (get-connection (:hosts db) (:keyspace db))]
    (cql/create-table conn "users"
                      (cq/column-definitions {:id :varchar
                                              :email :varchar
                                              :primary-key [:id]}))))

(defn down [db]
  (let [conn (get-connection (:hosts db) (:keyspace db))]
    (cql/drop-table conn "users")))

Read the project.clj file for the corresponding joplin plugin to see what clojure database libraries can be used in the migrators.

SQL migrators

When migrating SQL databases you have 2 flavors of migrators at your disposal. You may specify your migrations with two text files (one up, one down) as shown below:

$ ls -1 migrators/sql
20120903221323-add-test-table.down.sql
20120903221323-add-test-table.up.sql
$ cat migrators/sql/20120903221323-add-test-table.up.sql
CREATE TABLE test_table (id INT);

For this type of migration, use the Joplin database type :sql.

You may also specify your migrations as Clojure namespaces, like the example for Cassandra above, by using the Joplin database type :jdbc. See the example project for details.

Please note as of Joplin 0.3 if you have multiple SQL statements in a single .sql file, you need to insert a marker --;; between them.

Writing seed functions

Seed functions are always placed in a clojure source file. The source file can contain any number of functions, since a seed definition is the name of a clojure var referencing a function.

The seed function are called with the target map (see below) as its first parameter and any number of additional parameter that were passed into the lein plugin invocation (or to the multi-method if called programatically).

Example of a seed function;

(ns seeds.dt
  (:require [datomic.api :as d]))

(defn run [target & args]
  (let [conn (d/connect (-> target :db :url))]
    @(d/transact conn [{:db/id #db/id [:db.part/user -100]
                        :foobar/id 42}])))

The target map will consist of 3 keys; :db, :migrator, :seed. The contents of these keys will be the (looked up) values of the keys specified in the environment map in the joplin definition.

Calling Joplin from your code

Calling joplin from your code requires that you have loaded the joplin.core namespace and also the namespaces of the database plugins you intend to use. Then you can call one of 5 multi-methods to perform the same 5 actions as described above in the leiningen plugin section. The multi-methods are; joplin.core/migrate-db, joplin.core/rollback-db, joplin.core/seed-db, joplin.core/pending-migrations, joplin.core/create-migration.

All the multi-methods takes a target map as its first argument and additional optional arguments.

The target map must have this shape;

{:db {:type DB-TYPE, DB-SETTING1: "foo", :DB-SETTING2: "bar", ...}
 :migrator "path to a folder on the classpath"
 :seed "name of a var in a namespace on the classpath"
 }

The :migrator and/or :seed keys are optional (joplin can't do much if both are missing). The :DB-SETTING keys vary for the different database types.

For example;

(ns example
  (:require [joplin.core :as joplin]
            [joplin.dt.database]))

(joplin/migrate-db
  {:db {:type :dt,
        :url "datomic:mem://test"}
   :migrator "joplin/migrators/datomic"
   :seed "seeds.dt/run"})

Provided database types and their respective plugins;

{:db {:type ? ... :require [?]
:jdbc joplin.jdbc.database
:sql joplin.jdbc.database
:es joplin.elasticsearch.database
:zk joplin.zookeeper.database
:dt joplin.dt.database
:cass joplin.cassandra.database
:dynamo joplin.dynamodb.database

Note that it's easy to extend joplin to handle more database types and thus introduce more valid database types.

Hacking joplin

Joplin uses the lein-sub plugin, this makes it easy to install all projects locally with lein sub install.

Documentation

Wiki

License

Copyright © 2015 Martin Trojer

Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.

joplin's People

Contributors

bhb avatar brendanyounger avatar briprowe avatar cameronverrency avatar elisehuard avatar emil0r avatar frankiesardo avatar gfredericks avatar hanshuebner avatar htmfilho avatar jonpither avatar jonspalding avatar madvas avatar malcolmsparks avatar martintrojer avatar michaelklishin avatar nberger avatar nha avatar oliyh avatar razum2um avatar rbxbx avatar stathissideris avatar stuarth avatar sveri avatar tangrammer avatar thedavidmeister avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

joplin's Issues

Migration generation

Is there any helper or a way to generate migration with timestamp within the lib ? Or we have to have our own util script to do it in shell ?

Thanks

Keyspace does not exist?

I am trying to create some cassandra migrations. But when I run the migrations I get a Keyspace 'test' does not exist exception.

Does joplin require the keyspace to already exist? Is it not possible to create the keyspace from the migration it's self?

migration

(ns migrators.20181107120605-project
  (:require [qbits.alia :as alia]))

(defn up [conn]
  (alia/execute conn "CREATE KEYSPACE IF NOT EXISTS test WITH REPLICATION = {'class' : 'SimpleStrategy', 'replication_factor' : 1};")
  (alia/execute conn "USE test;")
  (alia/execute conn "CREATE TABLE IF NOT EXISTS person (uuid uuid, name varchar, PRIMARY KEY (uuid));"))

(defn down [conn]
  (alia/execute conn "USE test;")
  (alia/execute conn "DROP TABLE IF EXISTS person;")
  (alia/execute conn "DROP KEYSPACE IF EXISTS test;"))

config.edn

{:migrators    {:cass-mig "resources/migrators"}
 :seeds        {:cass-seed "seeds.cass/run"}
 :databases    {:cass-dev {:type :cass, :hosts ["::1"], :keyspace "test"}}
 :environments {:dev [{:db :cass-dev, :migrator :cass-mig, :seed :cass-seed}]}}

project.clj

(defproject app "0.1.0-SNAPSHOT"

            :dependencies [
                           [org.clojure/clojure "1.8.0"]
                           [joplin.core "0.3.11"]
                           [joplin.cassandra "0.3.11"]
                           ]

            :resource-paths ["resources"]

            :profiles {:default [:user]
                       :user {:plugins [
                                        [lein-sub "0.3.0"]
                                        ]
                              }}

            :aliases {"migrate"  ["run" "-m" "joplin.alias/migrate" "config.edn"]
                      "seed"     ["run" "-m" "joplin.alias/seed" "config.edn"]
                      "rollback" ["run" "-m" "joplin.alias/rollback" "config.edn"]
                      "reset"    ["run" "-m" "joplin.alias/reset" "config.edn"]
                      "pending"  ["run" "-m" "joplin.alias/pending" "config.edn"]
                      "create"   ["run" "-m" "joplin.alias/create" "config.edn"]}
            )

Exception:

Exception in thread "main" com.datastax.driver.core.exceptions.InvalidQueryException: Keyspace 'test' does not exist, compiling: (/private/var/folders/w_/yt926bqs21g44f257yz05ctsjbv948/T/form-init5453081474670989587.clj:1:125)
at clojure.lang.Compiler.load(Compiler.java:7391)
at clojure.lang.Compiler.loadFile(Compiler.java:7317)
at clojure.main$load_script.invokeStatic(main.clj:275)
at clojure.main$init_opt.invokeStatic(main.clj:277)
at clojure.main$init_opt.invoke(main.clj:277)
at clojure.main$initialize.invokeStatic(main.clj:308)
at clojure.main$null_opt.invokeStatic(main.clj:342)
at clojure.main$null_opt.invoke(main.clj:339)
at clojure.main$main.invokeStatic(main.clj:421)
at clojure.main$main.doInvoke(main.clj:384)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at clojure.lang.Var.invoke(Var.java:383)
at clojure.lang.AFn.applyToHelper(AFn.java:156)
at clojure.lang.Var.applyTo(Var.java:700)
at clojure.main.main(main.java:37)
Caused by: com.datastax.driver.core.exceptions.InvalidQueryException: Keyspace 'test' does not exist
at com.datastax.driver.core.exceptions.InvalidQueryException.copy(InvalidQueryException.java:50)
at com.datastax.driver.core.DriverThrowables.propagateCause(DriverThrowables.java:37)
at com.datastax.driver.core.Cluster.connect(Cluster.java:286)
at qbits.alia$connect.invokeStatic(alia.clj:183)
at qbits.alia$connect.invoke(alia.clj:178)
at joplin.cassandra.database$get_connection.invokeStatic(database.clj:26)
at joplin.cassandra.database$get_connection.invoke(database.clj:25)
at joplin.cassandra.database$eval3099$fn__3100.doInvoke(database.clj:55)
at clojure.lang.RestFn.invoke(RestFn.java:410)
at clojure.lang.MultiFn.invoke(MultiFn.java:229)
at clojure.lang.AFn.applyToHelper(AFn.java:154)
at clojure.lang.AFn.applyTo(AFn.java:144)
at clojure.core$apply.invokeStatic(core.clj:648)
at clojure.core$apply.invoke(core.clj:641)
at joplin.repl$run_op.invokeStatic(repl.clj:43)
at joplin.repl$run_op.invoke(repl.clj:43)
at joplin.repl$migrate.invokeStatic(repl.clj:55)
at joplin.repl$migrate.doInvoke(repl.clj:52)
at clojure.lang.RestFn.invoke(RestFn.java:425)
at joplin.alias$migrate.invokeStatic(alias.clj:21)
at joplin.alias$migrate.doInvoke(alias.clj:17)
at clojure.lang.RestFn.invoke(RestFn.java:425)
at clojure.lang.Var.invoke(Var.java:383)
at user$eval5.invokeStatic(form-init5453081474670989587.clj:1)
at user$eval5.invoke(form-init5453081474670989587.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:6927)
at clojure.lang.Compiler.eval(Compiler.java:6917)
at clojure.lang.Compiler.load(Compiler.java:7379)
... 14 more
Caused by: com.datastax.driver.core.exceptions.InvalidQueryException: Keyspace 'test' does not exist
at com.datastax.driver.core.Responses$Error.asException(Responses.java:136)
at com.datastax.driver.core.DefaultResultSetFuture.onSet(DefaultResultSetFuture.java:179)
at com.datastax.driver.core.RequestHandler.setFinalResult(RequestHandler.java:174)
at com.datastax.driver.core.RequestHandler.access$2600(RequestHandler.java:43)
at com.datastax.driver.core.RequestHandler$SpeculativeExecution.setFinalResult(RequestHandler.java:793)
at com.datastax.driver.core.RequestHandler$SpeculativeExecution.onSet(RequestHandler.java:627)
at com.datastax.driver.core.Connection$Dispatcher.channelRead0(Connection.java:1012)
at com.datastax.driver.core.Connection$Dispatcher.channelRead0(Connection.java:935)
at com.datastax.shaded.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:105)
at com.datastax.shaded.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:342)
at com.datastax.shaded.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:328)
at com.datastax.shaded.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:321)
at com.datastax.shaded.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:266)
at com.datastax.shaded.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:342)
at com.datastax.shaded.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:328)
at com.datastax.shaded.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:321)
at com.datastax.shaded.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:102)
at com.datastax.shaded.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:342)
at com.datastax.shaded.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:328)
at com.datastax.shaded.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:321)
at com.datastax.shaded.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:293)
at com.datastax.shaded.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:267)
at com.datastax.shaded.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:342)
at com.datastax.shaded.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:328)
at com.datastax.shaded.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:321)
at com.datastax.shaded.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1280)
at com.datastax.shaded.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:342)
at com.datastax.shaded.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:328)
at com.datastax.shaded.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:890)
at com.datastax.shaded.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:131)
at com.datastax.shaded.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:564)
at com.datastax.shaded.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:505)
at com.datastax.shaded.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:419)
at com.datastax.shaded.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:391)
at com.datastax.shaded.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:112)
at java.lang.Thread.run(Thread.java:748)

ElasticSearch database record should have a key for index name

I'd like to use different index names between dev, test, and certainly production environments. I can configure the name with Joplin for JDBC or Cassandra but not ElasticSearch. This means that I have to create ad-hoc ways of detecting the environment in order to name my indices.

If joplin.elasticsearch.database.ElasticSearchDatabase allowed specifying an index, I would be able to rely on that.

How does Joplin figure out the correct jdbc driver to use?

I was using Joplin with MySQL, but now I'm moving to SQLite. While using MySQL, Joplin was working perfectly. After changing the configuration to access SQLite the following error occurs:

CompilerException java.sql.SQLException: No suitable driver found for jdbc:sqlite:db.dub

And then I realized Joplin never asked me for a JDBC driver class for MySQL. How does Joplin figure out the driver and how can I make SQLite's driver visible for it?

https://github.com/EsmerilProgramming/dev-utility-belt/blob/issue%2316/project.clj
https://github.com/EsmerilProgramming/dev-utility-belt/blob/issue%2316/src/jub/datasource.clj

Cassandra Configuration Does Not Work With Given Readers

The cassandra configuration requires there be a vector of hosts:

{:migrators {:cass "migrations/migrators/cass"}
 :seeds {:cass "migrations.seeds.cass/run"}
 :databases {:cass-dev {:type :cass :hosts ["localhost"] :keyspace "foo"}
             :cass-stage {:type :cass :hosts ["10.10.10.10", "10.10.10.11", "10.10.10.12"]
                          :keyspace "foo"
                          :credentials {:user "something"
                                        :password "maybe-something-else?"}}}
 :environments {:dev [{:db :cass-dev :migrator :cass :seed :cass}]
                :stage [{:db :cass-stage :migrator :cass :seed :cass}]}}

But the readers for the tagged literals #env and #envf can only return strings.

This makes it hard to define migrations that need to discover the IPs dynamically (eg. when cassandra's running in a kubernetes cluster).

A possible solution would be to create a reader that lets client code supply a function that can fill in the data necessary. Suppose that reader's tag is #foo. Then the configuration data might look like:

{:migrators {:cass "migrations/migrators/cass"}
 :seeds {:cass "migrations.seeds.cass/run"}
 :databases {:cass-dev {:type :cass :hosts #foo my.dev.namespace/minikube-config :keyspace "foo"}
             :cass-stage {:type :cass :hosts #foo my.stage.namespace/lookup-cassandra-endpoints
                          :keyspace "foo"
                          :credentials {:user "something"
                                        :password "maybe-something-else?"}}}
 :environments {:dev [{:db :cass-dev :migrator :cass :seed :cass}]
                :stage [{:db :cass-stage :migrator :cass :seed :cass}]}}

The joplin reader function would then look something like:

(defn load-config [r]
  (edn/read {:readers {'env (fn [x] (System/getenv (str x)))
                       'envf (fn [[fmt & args]]
                               (apply format fmt
                                      (map #(System/getenv (str %)) args)))
                       'foo (fn [fn-symbol]
                              (require (symbol (namespace fn-symbol)))
                              ((ns-resolve *ns* fn-symbol)))}}
(PushbackReader. (io/reader r))))

Of course, #foo's not the best tag for this reader, but I can't think of a great one at the moment.

Thoughts?

Rollback fails when parsing string id

When performing a rollback with a migration id (and not a number of migrations to revert) the parsing of the id throws a "java.lang.NumberFormatException".

It looks like ragtime should handle this rollback scenario by performing an integer check. If the amount-or-id is an integer then the rollbacks perfoms x rollback iterations else the specific migration id is rolled back.

see joplin.alias rollback func:

(defn rollback [config-file env & [db num]]
  (let [conf (*load-config* config-file)]
    (when (and db num)
      (repl/rollback conf
                     (keyword env) (keyword db)
                     (Long/parseLong num)))) ;;<<---- is this needed??
  (System/exit 0))

load-config with alternate "environment" maps?

I'd like to propose a modification to "load-config" that optionally allows the caller to supply a function to look up the environment variables. Doing so would support use cases where the developer has adopted adzerk-oss/env or weavejester/environ or whatever is their favorite "12 factor app" style environment configuration lib.

So essentially, something like this

(defn load-config
  ([r] (load-config r System/getenv))
  ([r lookup]
   (edn/read {:readers {'env (fn [x] (lookup (str x)))
                        'envf (fn [[fmt & args]]
                                (apply format fmt
                                       (map #(lookup (str %)) args)))}}
             (PushbackReader. (io/reader r)))))

allowing the user to optionally supply the lookup as a lambda like so

(load-config r #(get (adzerk.env/env) %))

I haven't thought through the joplin.alias use case... Ideally the same "lookup" substitution would work there too.

Refactor to use ragtime 0.4.0

Will introduce a number of breaking changes.

  • Remove the lein plugin
  • Move config from project.clj
  • SQL plumming changes
  • Expose conflict strategies

Bump joplin version to 0.4.0 when done.

joplin.jdbc should expose more db-spec types

Ragtime uses clojure.java.jbdc/get-connection to get a connection to a jdbc database.

You'll need to build a configuration map that will tell Ragtime how to connect to your database, and where the migrations are.

This function exposes several options for getting a connection.

Sojoplin.jdbc.database/append-uri injects a connection-uri into the db-spec, forcing library users to use the Raw implementation. This seems pointless, shouldn't the user be able to decide what db-spec type ragtime uses to migrate their database?

Use ClojureWerkz fork of Ragtime

I'm not suggesting this with a light heart but it feels like the right thing to do. Ragtime seems to not get a lot of attention from the maintainer recently, and issues such as weavejester/ragtime#34 have been
open for a few months.

I've fixed a couple of issues we've discovered during our Joplin migration in my fork. Perhaps Joplin can depend on it until the Ragtime situation
improves. We bumped the version and do releases to Clojars.

HashedWheelTimer resource leak

When migrating many databases (1 SQL db, 8 Cassandra keyspaces, 1 Elasticsearch cluster) with many migrations (about 30 migrations per db) I get a report of resource leaking:

Apr 06, 2016 9:49:52 AM io.netty.util.ResourceLeakDetector reportLeak
SEVERE: LEAK: You are creating too many HashedWheelTimer instances.  
HashedWheelTimer is a shared resource that must be reused across the JVM,
so that only a few instances are created.

Upgrade Ragtime to newer version to play well with newer `org.clojure/java.jdbc`

Hi,

The version of Ragtime included in joplin at the moment does not play well with the latest org.clojure/java.jdbc (version 0.6.1) because it uses a deprecated syntax for create-table-ddl that has been removed in java.jdbc.

This only causes a problem if you bring in another library that requires this newer version of org.clojure/java.jdbc like Conman.

The error is not helpful (cannot turn a Keyword into an ISeq), so if someone pops in here saying they can't migrate their databases with that error, then this might be the reason.

Upgrading my ragtime.core/ragtime.jdbc to 0.6.0 fixed the issue.

Thanks for the work on this library, I love how flexible it is!

Caleb

No method in multimethod 'reset-db' for dispatch value: :sql

Hello,

I'm trying to reset a database before a test run by calling reset-db as defined in joplin.core. However, I'm receiving the following error:

IllegalArgumentException No method in multimethod 'reset-db' for dispatch value: :sql  clojure.lang.MultiFn.getFn (MultiFn.java:160)

My project.clj file's :dependencies looks like this:

:dependencies [[com.h2database/h2 "1.3.170"] [joplin "0.2.2"]]

The code that results in aforementioned error looks like this:

(require '[joplin.core :refer :all])
(reset-db {:db {:type :sql :url "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"}})

My assumption is that since I've listed "joplin" as a dependency that I should have the required implementation. It's entirely possible that I've messed something up, though. Would appreciate any direction.

Erin

Any interest in a code-driven JDBC migrator?

Using plain SQL is very convenient for defining the schema but production migrations can involve modifying data in less-than-trivial ways.

Do you have any thoughts on having a separate JDBC-powered migrator that uses Clojure code instead of .sql files, à la Cassandra?

And a related question: any interest in a file-driven CQL migrator?

Listing DB/migrator/seed definitions in a resource file (or other sources)

When using Joplin as a library (e.g. to reset the databases between integration test runs) I find myself
with a bit of DB definition duplication. Perhaps a good way of dealing with it would be some sort of
support for resource files (à la ActiveRecord's config/database.yml).

If we want to make things even more flexible, we can create a new protocol for definition loading and
retrieve them from maps, files, and services such as ZooKeeper, etcd, etc.

Any thoughts on this? I'd be happy to contribute this if there's interest in having this in joplin.core.

No method in multimethod 'migrate-db' for dispatch value: :sql

Hello, I'm trying to use Joplin from code but I'm getting the following exception:

IllegalArgumentException No method in multimethod 'migrate-db' for dispatch value: :sql clojure.lang.MultiFn.getFn (MultiFn.java:160)

I've added joplin.jdbc in my dependencies:

:dependencies [[org.clojure/clojure "1.6.0" ]
             [org.clojure/java.jdbc "0.3.6" ]
             [mysql/mysql-connector-java "5.1.25"]
             [joplin.core "0.2.9" ]                 
             [joplin.jdbc "0.2.9" ]]

I wrote datasource.clj as follows:

(ns jub.datasource
  (:require [clojure.java.jdbc :as jdbc]
                 [joplin.core  :as joplin]
                 [joplin.sql.database]))

(def mysql-db {:subprotocol "mysql"
               :subname     "//localhost:3306/jub"
               :user        "jub_user"
               :password    "senhas"})

(def joplin-target {:db {:type :sql 
                    :url (str "jdbc:mysql:" (get mysql-db :subname)
                              "?user="      (get mysql-db :user)
                              "&password="  (get mysql-db :password))}
                    :migrator "resources/migrators/sql"})

(defn migrate-mysql-db []
   (joplin/migrate-db joplin-target))

The readme is not very clear about using joplin from code. It says that I have to load the joplin.core namespace (done) and also the namespaces of the database plugins (I guess joplin.sql.database, but not sure). I've tried to refer the namespaces joplin.jdbc.database and joplin.sql.database, but the error persists.

The code is open source and available in this branch: https://github.com/htmfilho/javaee-utility-belt/tree/Issue%237

Thanks in advance!

Seeder exceptions don't bubble up

If any sort of exception (including syntax error) occurs in the seed function's namespace, it is silenced. Joplin prints Seeding [db] and then exits (does not print Applying seed function [x]).

Migrations that touch multiple data sources

Consider the following case. You're integrating with a 3rd party API (e.g. Stripe) and need to connect a user account there with account in your own database. You have existing users. You thus need to
run a migration that will touch more than one data source (and can only be done in Code).

You can abuse a code-driven migrator to do this, of course. But in case you need to perform a migration
that touches a relational database and an API, using Cassandra migrator for it feels really awkward and confusing.

Perhaps coming up with a code-driven JDBC migrator is the easiest thing to do but it's an interesting case that doesn't fit Joplin's view of the world very well.

migrate-db from test does not find migrators

Hi,

I am trying to migrate a database from a test. This is my folder layout

project
-> test/clj/namespace/user_test.clj
-> resources/migrators/sqlite/user.[up|down].sql

my user_test looks like this (plus some tests):

(j/migrate-db
  {:db {:type :jdbc,
        :url "jdbc:sqlite::memory:"}
   :migrator "migrators/sqlite"})

When I run lein test I get this stacktrace:
Warning, no migrators found!
Migrating #joplin.sql.database.SqlDatabase{:db {:type :jdbc, :url jdbc:sqlite::memory:}, :migrator migrators/sqlite, :connection-uri jdbc:sqlite::memo
ry:}
Exception in thread "main" java.sql.SQLException: [SQLITE_ERROR] SQL error or missing database (no such table: ragtime_migrations), compiling:(foo/bar
/db/user_test.clj:19:1)
at clojure.lang.Compiler.load(Compiler.java:7249)
at clojure.lang.RT.loadResourceScript(RT.java:371)
at clojure.lang.RT.loadResourceScript(RT.java:362)
at clojure.lang.RT.load(RT.java:446)
at clojure.lang.RT.load(RT.java:412)
at clojure.core$load$fn__5446.invoke(core.clj:5862)
at clojure.core$load.doInvoke(core.clj:5861)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invoke(core.clj:5667)
at clojure.core$load_lib$fn__5395.invoke(core.clj:5707)
at clojure.core$load_lib.doInvoke(core.clj:5706)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invoke(core.clj:630)
at clojure.core$load_libs.doInvoke(core.clj:5745)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invoke(core.clj:630)
at clojure.core$require.doInvoke(core.clj:5828)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invoke(core.clj:630)
at user$eval136.invoke(form-init1675116507402864902.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:6792)
at clojure.lang.Compiler.eval(Compiler.java:6782)
at clojure.lang.Compiler.load(Compiler.java:7237)
at clojure.lang.Compiler.loadFile(Compiler.java:7175)
at clojure.main$load_script.invoke(main.clj:275)
at clojure.main$init_opt.invoke(main.clj:280)
at clojure.main$initialize.invoke(main.clj:308)
at clojure.main$null_opt.invoke(main.clj:343)
at clojure.main$main.doInvoke(main.clj:421)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at clojure.lang.Var.invoke(Var.java:383)
at clojure.lang.AFn.applyToHelper(AFn.java:156)
at clojure.lang.Var.applyTo(Var.java:700)
at clojure.main.main(main.java:37)
Caused by: java.sql.SQLException: [SQLITE_ERROR] SQL error or missing database (no such table: ragtime_migrations)
at org.sqlite.core.DB.newSQLException(DB.java:890)
at org.sqlite.core.DB.newSQLException(DB.java:901)
at org.sqlite.core.DB.throwex(DB.java:868)
at org.sqlite.core.NativeDB.prepare(Native Method)
at org.sqlite.core.DB.prepare(DB.java:211)
at org.sqlite.core.CorePreparedStatement.(CorePreparedStatement.java:40)
at org.sqlite.jdbc3.JDBC3PreparedStatement.(JDBC3PreparedStatement.java:29)
at org.sqlite.jdbc4.JDBC4PreparedStatement.(JDBC4PreparedStatement.java:18)
at org.sqlite.jdbc4.JDBC4Connection.prepareStatement(JDBC4Connection.java:47)
at org.sqlite.jdbc3.JDBC3Connection.prepareStatement(JDBC3Connection.java:251)
at org.sqlite.jdbc3.JDBC3Connection.prepareStatement(JDBC3Connection.java:223)
at clojure.java.jdbc.deprecated$prepare_statement.doInvoke(deprecated.clj:479)
at clojure.lang.RestFn.invoke(RestFn.java:425)
at clojure.lang.AFn.applyToHelper(AFn.java:156)
at clojure.lang.RestFn.applyTo(RestFn.java:132)
at clojure.core$apply.invoke(core.clj:632)
at clojure.java.jdbc.deprecated$with_query_results_STAR_.invoke(deprecated.clj:675)
at joplin.sql.database.SqlDatabase$fn__2560.invoke(database.clj:51)
at clojure.java.jdbc.deprecated$with_connection_STAR_.invoke(deprecated.clj:307)
at joplin.sql.database.SqlDatabase.applied_migration_ids(database.clj:49)
at ragtime.core$applied_migrations.invoke(core.clj:31)
at ragtime.core$migrate_all.invoke(core.clj:57)
at ragtime.core$migrate_all.invoke(core.clj:53)
at joplin.core$do_migrate.invoke(core.clj:140)
at joplin.jdbc.database$eval2726$fn__2727.doInvoke(database.clj:84)
at clojure.lang.RestFn.invoke(RestFn.java:410)
at clojure.lang.MultiFn.invoke(MultiFn.java:229)
at foo.bar.db.user_test$eval2764.invoke(user_test.clj:19)
at clojure.lang.Compiler.eval(Compiler.java:6792)
at clojure.lang.Compiler.load(Compiler.java:7237)
... 33 more

These are the deps I have:

             [org.xerial/sqlite-jdbc "3.8.7"]
             [joplin.core "0.2.11"]
             [joplin.jdbc "0.2.11"]

Running lein joplin migrate... with the same path given everything works as expected.

Log a warning when there are no migrations to run

I've upgraded to 0.2.0 without realising that the JDBC migrator was renamed to SQL. I have only noticed when my services started failing with obscure errors.

If there are no migrations to run, joplin should log an easy to notice warning because it rarely will be a normal condition.

joplin.lein has a hardcoded list of databases and does not allow any others

joplin.lein hardcodes the list of databases and any others are discarded, meaning custom databases won't work with the leiningen plugin.

The temporary solution I've used to allow RethinkDB migrations in my own code is to amend this line

to instead be (keep (conj libs extra-libs)) where extra-libs is (-> project :joplin :libs). Then in my projects project.clj I've added :libs {:rethinkdb "rethinkdb"} and now I can use :type :rethinkdb in my :databases and the driver is loaded from rethinkdb.database.

I'm not sure if there is a better solution, but if interested, I can do a pull request. Thoughts?

ragtime and joplin loads resources differently

This can lead to confusing differences in the config where joplin migrators needs a folder prefix but ragtime migrators does not.

A nice solution would be for joplin to use resource just like ragtime (if possible).

Using sensitive information in Joplin with putting it version control

Apart from having to duplicate your database URIs/environments (also mentioned in issue #14), one definitely does not want to commit database credentials to version control.

It's not clear to me how I might go about reusing previously define database environments and credentials without putting them in my project definition.

Unable to run migrations with joplin 0.2.0

Hi

With the following configuration :

(defproject thm "0.1.0-SNAPSHOT"
  :description "FIXME: write this!"
  :url "http://example.com/FIXME"

  :dependencies [[org.clojure/clojure "1.6.0"]
                 [org.clojure/clojurescript "0.0-2268"]
                 [org.clojure/core.async "0.1.267.0-0d7780-alpha"]
                 [om "0.6.5"]
                 [ring/ring-core "1.3.0"]
                 [ring/ring-jetty-adapter "1.3.0"]
                 [compojure "1.1.1"]
                 [com.cognitect/transit-clj "0.8.247"]
                 [cljs-http "0.1.16"]
                 [org.postgresql/postgresql "9.2-1002-jdbc4"]
                 [korma "0.3.0"]
                 [clj-time "0.8.0"]
                 [joplin "0.2.0"]]

  :plugins [[lein-cljsbuild "1.0.4-SNAPSHOT"]
            [lein-bower "0.5.1"]
            [lein-ring "0.8.11"]
            [joplin.lein "0.2.0"]
            [cider/cider-nrepl "0.7.0"]]

  :source-paths ["src/clj"]

  :bower-dependencies [["react" "0.11.0"]
                       ["bootstrap" "3.2.0"]]

  :bower { :directory "resources/public/bower"}

  :ring {:handler thm.core/app}

  :cljsbuild {
    :builds [{:id "thm"
              :source-paths ["src/cljs"]
              :compiler {
                :output-to "resources/public/js/thm.js"
                :output-dir "resources/public/out"
                :optimizations :none
                :source-map true}}]}

  :joplin {:migrators {:sql-mig "src/joplin/migrators/sql"}
           :seeds {:sql-seed "seeds.sql/run"}
           :databases {:sql-dev {:type :jdbc
                                 :url "jdbc:postgresql:thm-dev?user=phtrivier&password=phtrivier"
                                 :db "thm-dev"
                                 :user "phtrivier"
                                 :password "phtrivier"
                                 }}
           :environments {:dev [{:db :sql-dev
                                 :migrator :sql-mig
                                 :seed :sql-seed
                                 }]}})

And the following source files layout :

.
|____clj
| |____seeds
| | |____sql.clj
| |____thm
| | |____core.clj
| | |____db.clj
|____cljs
| |____thm
| | |____core.cljs
| | |____ui
| | | |____index.cljs
|____joplin
| |____migrators
| | |____sql
| | | |____201409192155-init.down.sql
| | | |____201409192155-init.up.sql

init.up.sql is a migration that creates a few tables. I can not get it to run. I get no error when running lein joplin :

> lein joplin migrate dev sql-dev
Migrating #ragtime.sql.database.SqlDatabase{:connection-uri jdbc:postgresql:thm-dev?user=phtrivier&password=phtrivier, :db {:db thm-dev, :password phtrivier, :type :jdbc, :url jdbc:postgresql:thm-dev?user=phtrivier&password=phtrivier, :user phtrivier}, :seed seeds.sql/run, :migrator joplin/migrators/sql}

But then in my db, the tables are not really created :

thm-dev=# \dt
                List of relations
 Schema |        Name        | Type  |   Owner   
--------+--------------------+-------+-----------
 public | ragtime_migrations | table | phtrivier
(1 row)

Migrating works if I use joplin 0.1.11 (but the "reset" command does not work, I kept getting NPEs.) :

> lein joplin reset dev sql-dev
Exception in thread "main" java.lang.NullPointerException, compiling:(/tmp/form-init3641566923530311972.clj:1:90)
    at clojure.lang.Compiler.load(Compiler.java:7142)
    at clojure.lang.Compiler.loadFile(Compiler.java:7086)
    at clojure.main$load_script.invoke(main.clj:274)
    at clojure.main$init_opt.invoke(main.clj:279)
    at clojure.main$initialize.invoke(main.clj:307)
    at clojure.main$null_opt.invoke(main.clj:342)
    at clojure.main$main.doInvoke(main.clj:420)
    at clojure.lang.RestFn.invoke(RestFn.java:421)
    at clojure.lang.Var.invoke(Var.java:383)
    at clojure.lang.AFn.applyToHelper(AFn.java:156)
    at clojure.lang.Var.applyTo(Var.java:700)
    at clojure.main.main(main.java:37)
Caused by: java.lang.NullPointerException
    at ragtime.core$rollback.invoke(core.clj:44)
    at ragtime.core$rollback_last.invoke(core.clj:70)
    at joplin.core$do_reset.invoke(core.clj:158)
    at joplin.jdbc.database$eval1051$fn__1052.doInvoke(database.clj:47)
    at clojure.lang.RestFn.invoke(RestFn.java:410)
    at clojure.lang.MultiFn.invoke(MultiFn.java:227)
    at clojure.lang.AFn.applyToHelper(AFn.java:154)
    at clojure.lang.AFn.applyTo(AFn.java:144)
    at clojure.core$apply.invoke(core.clj:626)
    at joplin.main$_main.doInvoke(main.clj:76)
    at clojure.lang.RestFn.invoke(RestFn.java:1289)
    at clojure.lang.Var.invoke(Var.java:501)
    at user$eval5.invoke(form-init3641566923530311972.clj:1)
    at clojure.lang.Compiler.eval(Compiler.java:6703)
    at clojure.lang.Compiler.eval(Compiler.java:6693)
    at clojure.lang.Compiler.load(Compiler.java:7130)
    ... 11 more

Did anything changed in 0.2.0 that might cause my issue ?
Thanks.

Please cut a new release

Something I work on could use a new Joplin release with updated dependencies. Also, time for 0.2.0, maybe?

Use Aero?

Hi there,

I'm quite a fan of Aero, which appears to be a superset of Joplin's config facility.

Wondering if it could substitute it.

So far I have no big use case, other than avoiding env vars when #user or #or could do. But it's easy to imagine production users can have arbitrarily sophisticated needs.

Thanks - V

[Not a Issue] New Feature: Will it support multiple users in the future?

Hi there:
Thank you very much for making such a good app!
May I ask a question: Will it support multiple users in the future? the multiple users thing I mean that a device(cell phone or PC) can have multiple sync profile, people can switch to different profile to manage different note sets, for example, one profile for myself, the other profile for my daughter(she is too young to keep her own diary, the adults have to do it for her, and one day the whole diary note set will be handed over to her).
Sorry for my poor English.
Thanks a lot.

Running any joplin repl command always tries to create "ragtime_migrations" table

Whenever I run a command such as create, migrate, seed, etc. the command executes correctly but it seems that it tries to create the "ragtime_migrations" table each command. Is this normal?

example:
=>(def config (load-config "joplin.edn"))

'test.core/config

=>(reset config :dev :psql-dev)
Rolling back #ragtime.jdbc.SqlDatabase{:db-spec {:connection-uri jdbc:postgresql://127.0.0.1/test?user=postgres}, :migrations-table ragtime_migrations, :url jdbc:postgresql://127.0.0.1/test?user=postgres, :connection-uri jdbc:postgresql://127.0.0.1/test?user=postgres, :db {:type :sql, :url jdbc:postgresql://127.0.0.1/test?user=postgres}, :migrator joplin/migrators/sql, :seed seeds.sql/run}
ERROR: relation "ragtime_migrations" already exists
STATEMENT: CREATE TABLE ragtime_migrations (id varchar(255), created_at varchar(32))
Rolling back 20151231232425-create-tests
ERROR: relation "ragtime_migrations" already exists
STATEMENT: CREATE TABLE ragtime_migrations (id varchar(255), created_at varchar(32))
Migrating #ragtime.jdbc.SqlDatabase{:db-spec {:connection-uri jdbc:postgresql://127.0.0.1/test?user=postgres}, :migrations-table ragtime_migrations, :url jdbc:postgresql://127.0.0.1/test?user=postgres, :connection-uri jdbc:postgresql://127.0.0.1/test?user=postgres, :db {:type :sql, :url jdbc:postgresql://127.0.0.1/test?user=postgres}, :migrator joplin/migrators/sql, :seed seeds.sql/run}
ERROR: relation "ragtime_migrations" already exists
STATEMENT: CREATE TABLE ragtime_migrations (id varchar(255), created_at varchar(32))
Applying 20151231232425-create-tests
ERROR: relation "ragtime_migrations" already exists
STATEMENT: CREATE TABLE ragtime_migrations (id varchar(255), created_at varchar(32))
Seeding #ragtime.jdbc.SqlDatabase{:db-spec {:connection-uri jdbc:postgresql://127.0.0.1/test?user=postgres}, :migrations-table ragtime_migrations, :url jdbc:postgresql://127.0.0.1/test?user=postgres, :connection-uri jdbc:postgresql://127.0.0.1/test?user=postgres, :db {:type :sql, :url jdbc:postgresql://127.0.0.1/test?user=postgres}, :migrator joplin/migrators/sql, :seed seeds.sql/run}
ERROR: relation "ragtime_migrations" already exists
STATEMENT: CREATE TABLE ragtime_migrations (id varchar(255), created_at varchar(32))
Applying seed function seeds.sql$run@2ba57027
({:id 42, :name "test42"})

my joplin.edn:

{ :migrators { :sql-mig "joplin/migrators/sql" :sql-mig-extra "joplin/migrators/sql/extrapath" } :seeds {:sql-seed "seeds.sql/run"} :databases { :psql-dev {:type :sql, :url #envf ["jdbc:postgresql://%s/test?user=%s" TARGET_HOST DEV_PGUSER]} :psql-prod {:type :sql, :url #envf ["jdbc:postgresql://psq-prod/prod?user=%s&password=%s" PROD_PGUSER PROD_PGPASSWD]} } :environments { :dev [{:db :psql-dev, :migrator :sql-mig, :seed :sql-seed}] :prod [{:db :sql-prod, :migrator :imported-sql-mig, :seed :imported-sql-seed}] } }

Java 9 and migrator discovery failure

folder-on-classpath (->> (classpath-directories)

Swithing to Java 9 stopped Migrators from working :-(

Our migrators are on classpath but in J9

(clojure.java.classpath/classpath-directories) returns ()

whereas in J8 it returns something like

(#object[java.io.File 0x24581963 "/home/matt/src/github/mastodonc/kixi.search/test"]
 #object[java.io.File 0x59969d1f "/home/matt/src/github/mastodonc/kixi.search/dev"]
 #object[java.io.File 0x7e3ac75f "/home/matt/src/github/mastodonc/kixi.search/src"]
 #object[java.io.File 0x3fe13f9f "/home/matt/src/github/mastodonc/kixi.search/resources"]
 #object[java.io.File 0x5b16aa01 "/home/matt/src/github/mastodonc/kixi.search/target/classes"])```

Make println statements configurable

Hi,

I am using joplin and also do so in my integration tests. The problem with this is, that the console is cluttered with joplins println statements and it is hard to find any meaningful error inbetween.
It would be nice if I could configure them, just like I do with standard java logging libraries.

es client only allows specific keys

Hi,

I have a custom es client that i'm dynamically binding with other keys than those specified in joplin.elasticsearch.database ([:host :port :index :migration-index :cluster :native-port]). Joplin is removing those extra keys, could it ignore them instead?

Thanks,

Tim

"Could not find environment" on one machine

Hi,

I have this project: https://github.com/sveri/mct/tree/ad6d5cc0e84713f3b9e54a8b0c911a535c8c59aa

Now, when I clone it on one machine (where I developed for some time already), I have to create the folder "db" and then run: lein joplin reset sqlite-dev-env sqlite-dev it creates the database file as expected.

Now, when I do the same (clone, create "db" run: lein joplin reset sqlite-dev-env sqlite-dev) on a different machine I get this error:

Could not find environment ':sqlite-dev-env'

Not sure what exactly is causing this issue, it is really weird. The code of the project is the same (clean github checkout).

Not working machine:

lein version
Leiningen 2.5.2 on Java 1.8.0_25 Java HotSpot(TM) 64-Bit Server VM
Windows 8

Working machine:

Leiningen 2.5.0 on Java 1.8.0_25 Java HotSpot(TM) 64-Bit Server VM
Windows 7

So the only obvious difference is the leiningen version and the windows version. Not sure what would cause this -.-

Joplin version used is 0.2.11

Source path assumptions should be documented

Joplin's "create migration" task uses fairly naïve assumptions about source directory layout for code-driven migrators (e.g. Cassandra). It strips off the first path part and turns the rest into a namespace name. This means if you try to use src/joplin instead of joplin, you'll get compilation errors that
are not very pleasant to debug.

I think this at least should be documented, and maybe even made optional configurable with a function defined with a migrator attribute.

lein install could not find artifact ....

Hi folks!
I tried a lein install and lein sub install but no way in both cases

 tangrammers-Mac-mini:joplin tangrammer$ lein install
Retrieving lein-sub/lein-sub/0.3.0/lein-sub-0.3.0.pom from clojars
Retrieving lein-sub/lein-sub/0.3.0/lein-sub-0.3.0.jar from clojars
Retrieving org/clojure/java.jdbc/0.2.3/java.jdbc-0.2.3.jar from central
Could not find artifact joplin.core:joplin.core:jar:0.2.2-SNAPSHOT in clojars (https://clojars.org/repo/)
Could not find artifact joplin.jdbc:joplin.jdbc:jar:0.2.2-SNAPSHOT in clojars (https://clojars.org/repo/)
Could not find artifact joplin.elasticsearch:joplin.elasticsearch:jar:0.2.2-SNAPSHOT in clojars (https://clojars.org/repo/)
Could not find artifact joplin.zookeeper:joplin.zookeeper:jar:0.2.2-SNAPSHOT in clojars (https://clojars.org/repo/)
Could not find artifact joplin.datomic:joplin.datomic:jar:0.2.2-SNAPSHOT in clojars (https://clojars.org/repo/)
Could not find artifact joplin.cassandra:joplin.cassandra:jar:0.2.2-SNAPSHOT in clojars (https://clojars.org/repo/)
This could be due to a typo in :dependencies or network issues.
If you are behind a proxy, try setting the 'http_proxy' environment variable.


tangrammers-Mac-mini:joplin tangrammer$ lein sub install
Reading project from joplin.core
Retrieving joda-time/joda-time/2.3/joda-time-2.3.jar from central
Retrieving clj-time/clj-time/0.8.0/clj-time-0.8.0.jar from clojars
Could not find artifact org.clojure:java.classpath:jar:0.2.2-SNAPSHOT in clojars (https://clojars.org/repo/)
This could be due to a typo in :dependencies or network issues.
If you are behind a proxy, try setting the 'http_proxy' environment variable.

Update README.md to reflect :jdbc -> :sql change

The docs need to be updated to reflect the change from :jdbc for SQL file migrations to :sql. Right now, you only get a warning that no migrations could be found and you have to either read the source code in joplin.jdbc or explicitly track down the mention in CHANGES.md to learn why.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.