I am trying to figureout how to use honeysql DSL with migratus. https://github.com/yogthos/migratus#modify-sql-fn has an option to format the SQL but I have to write edn style dsl and name that as *.sql.
@munichlinux You're beyond the edge of what these libraries are intended to provide -- but it's an interesting idea. In general, I don't find a DSL worthwhile for DDL for SQL migrations but I'm interested to hear what value you are getting from it?
@seancorfield Thank you.
1. A level of abstraction over the database (less important, in real use I never had to swap the database).
2. I kinda like the brevity of DDL that makes it really simple rather than writing a raw SQL..
I use to write nodejs for web. I used knex
, here is an example:
const tableName = 'session';
exports.up = (knex) =>
knex.schema.createTable(tableName, (table) => {
table.string('sid').primary().notNullable();
table.json('sess').notNullable();
table.timestamp('expire').notNullable();
table.timestamp('created_at').defaultTo(knex.fn.now());
table.timestamp('updated_at').defaultTo(knex.fn.now());
});
exports.down = (knex) => knex.schema.dropTable(tableName);
Hmm, I find plain SQL easier to read but I can see how, with a few helper functions for common columns or column types, you could use more concise code to describe tables… and migratus does let you use code-based migrations although the format is a bit verbose. Maybe @yogthos would be open to providing a more concise version that leveraged HoneySQL to format the result of calling DDL-generating functions? Not sure what that would look like tho’… just spit-balling an idea…
I'd be open to making it easier using HoneySQL, it shouldn't be too hard to add a layer that could parse HoneySQL markup and generate SQL strings that would then be consumed. Could leverage existing edn migrations. Currently, there are :up-fn
and :down-fn
keys, so perhaps could have :up-honey
and :down-honey
that would point to HoneySQL markup.
The “benefits” would likely come from having helper functions that generated common DDL fragments so it would need to be actual code — similar to how the EDN/code migrations work today — except that the functions would produce the ["SQL"]
vector that migratus
would then run honey.sql/format
on.
Allowing :up-fn
/ :up-honey
/ etc to be a fully-qualified symbol so the :ns
could be omitted might also be nice.
And there would need to be some way to specify options for the honey.sql/format
call I guess, either globally, or on a per-migration basis.
I mean, maybe there’s also an avenue for providing HoneySQL DSL as EDN but I don’t think that’s as valuable as having functions to generate the ["SQL"]
vector — the DSL is pretty verbose for DDL stuff, especially table/column specs.
@yogthos IMO, Here are my two options. 1. Let people write a clj file for migration. It will be foo-123.up.clj foo-123.down.clj 2. you already have a way to run a preprocessor, I can say the default preprocessor is SQL or honeysql or tomorrow's new thing. I personally like 1. As long as I have a public method up that returns string, I can do whatever I want.
what do you guys thing?
@munichlinux The .edn
version is effectively 1. It’s declarative and based on a specific function for up and another for down.
How would an arbitrary .clj
file even be used? The namespace should match the filename for a start so you can’t have .
in that part.
right, the current .edn
version is seems very similar to option 1, it sounds like the biggest win would be in having a DSL for describing DDL fragments
@seancorfield since these files are generated. we can generate migrations.123
as the ns.
I am trying to keep this independent of edn or honeysql.
(-> (all-ns)
(filter (starts with migrations)
(sort by timestamp)
(doseq [ns all-ns]
(ns/up)))
@munichlinux The migrations need to follow a specific naming patterns so they can be run in order and all checked off in the tracking table. (right @yogthos)
Oh, are you proposing not even using migratus
and writing your own custom migrations stuff?
nope, I proposing to use migratus
. I was just outlining how the clj will work.
ATM, migratus can generate the file names. I propose to generate the file with an ns if we have to take the clj approach.
yeah, migratus needs to have a numeric pattern in the migration name to track the order of migrations
it might also make sense to make a separate library that leverages migratus for this
I am getting a
Encountered error when macroexpanding cljs.core.async/go.
Encountered error when macroexpanding cljs.core/loop.
Syntax error macroexpanding.
And I have no idea what to do about it, it should work as far as I understand it. I tried to wrap it in macroexpand which expanded it but where to go from there? It looks fine.I don't know what this might be, but generally it's helpful to include the code reproducing the problem
I am trying to recreate the minimal example.
I think I got it, I had <! in my recur, I now put that inside the loop with a let binding and it doesn't say it's bad
Why is it that I only get these ideas after publicly asking for help? I tried to figure out for hours alone.
Rubber ducking
same here! i will often type out a question to the slack or to stack overflow and just typing it out to explain it often gives me more insight into the issue. First I hear of "rubber ducking" now I might have to get a comically large one for the desk...
reasonable guess, I guess 🙂 although this particular time it felt different. I already removed most of the code before writing and I just randomly figured maybe it's the <! in recur
A <! In a recur should in theory work fine, but the go macro on the cljs side of core.async isn't as a robust, and sometimes the way cljs macroexpands code makes it almost impossible for the go macro to rewrite it (some macros expand to a blob of js which the go macro can't rewrite)
So you may have hit a bug in core.async, an annoying tricky bit in how core.async and clojurescript interact, or actually done something incorrectly yourself
If it happens lots I would guess other than the last and provide actual examples, right now I am just extremely happy I can move on. Need to finish this stuff and start other things because ... well, life.
Hello everyone, I have a doubt. Can specs be defined such that only their generators are included in the development environment? not in production
@pablog Do you mean custom generators? Those are provided as functions, so they are only called if you actually do something generative to a Spec. And you can write functions such that they only load their dependencies if they are called so you can omit those dependencies from a production build if you want.
We have generators that depend on test.check
which is only available at dev/test time for us.
Yes, custom generators. For example, if I define something like:
(s/def ::name
(s/with-gen string?
#(clojure.test.check.generators/elements #{"John" "Marie"})))
And in my application I use :name
in order to validate some data, I don’t want that clojure.test.check.generators
stuff gets loaded on production. Even compiled and packed.The problem is that spec.alpha
uses an older version of test.check
(s/def ::name
(s/with-gen string?
#((requiring-resolve 'clojure.test.check.generators/elements) #{"John" "Marie"})))
isn't it the case that you can use clojure.spec.gen.alpha
safely, and if you call it it auto-loads test.check?
IOW it errors if used and check.test isn't provided, but otherwise compiles without complaint
Yes
Right, but @pablog wants a more up-to-date test.check
then depend on the version you want?
(which it would use if it was specified as a test dep, right?)
You can specify a dep on a newer version if you like
spec just (dyna)loads what's on the classpath
Yeah, the key thing is not referring to clojure.test.check
statically in your code. Use requiring-resolve
and it won’t try to load/compile it unless that code is evaluated.
Sorry, I think that I missed some important fact. How can I use test.check/let
?
dynamically
you can't do that with spec
the simplest thing is to only use it in code that is run locally, and just require test.check
the file referencing test.check can be kept out of your jar entirely
Ok, thanks a lot! 🙂
test check's let is a macro that expands from something like (check/let [a b] c)
to something like (check/bind b (fn [a] c))
and bind is a function, so using it dynamically is a lot easier than a macro
(test.check generators are a monad, and check/let is a monad comprehension for it)
bind is even one of the "lazy combinators" that spec sets up https://github.com/clojure/spec.alpha/blob/master/src/main/clojure/clojure/spec/gen/alpha.clj#L109-L111