duct

2018-11-23T00:13:50.128400Z

I got it. Thanks.

2018-11-23T00:16:36.128600Z

If I want to use logger in ig/halt-key, then return {:server server :logger logger} is the way?

2018-11-23T00:17:17.128800Z

Yep

👍 1
2018-11-23T00:18:02.129100Z

Thank you very much!

2018-11-23T00:18:31.129300Z

In Integrant 0.8.0-alpha2 I've added a resolve-key multimethod that might be useful if you just want to use the server in references.

2018-11-23T00:18:49.129500Z

Though it's still experimental functionality.

2018-11-23T00:19:08.129700Z

That sounds great.

flowthing 2018-11-23T07:43:38.131400Z

I don't understand how to properly separate dev config from prod config in Duct 0.11. I'm trying to introduce Duct into an existing (minimal) project sort of piece by piece. Is there an example project somewhere I could look at? For example, I'd like to run :duct.compiler/sass only in dev.

flowthing 2018-11-23T09:35:29.131900Z

Well, this is probably one problem, at least:

Syntax error (ClassCastException) compiling at (dev.clj:35:3).
class duct.logger.timbre.TimbreLogger cannot be cast to class clojure.lang.IFn (duct.logger.timbre.TimbreLogger is in unnamed module of loader clojure.lang.DynamicClassLoader @36f39f6a; clojure.lang.IFn is in unnamed module of loader 'app')
                 clojure.core/eval   core.clj: 3214
                               ...                 
      shadow.user$eval29750.invoke           :    1
shadow.user$eval29750.invokeStatic           :    1
                               ...                 
                     dev/eval29754    dev.clj:   35
                 integrant.repl/go   repl.clj:   54
               integrant.repl/prep   repl.clj:   16
                               ...                 
       clojure.core/alter-var-root   core.clj: 5505
       clojure.core/alter-var-root   core.clj: 5510
                               ...                 
            integrant.repl/prep/fn   repl.clj:   16
                  dev/eval29741/fn    dev.clj:   28
             duct.core/prep-config   core.clj:  193
            duct.core/build-config   core.clj:  182
            duct.core/fold-modules   core.clj:  145
               integrant.core/fold  core.cljc:  282
               clojure.core/reduce   core.clj: 6827
                               ...                 
            integrant.core/fold/fn  core.cljc:  282
         duct.core/fold-modules/fn   core.clj:  145

flowthing 2018-11-23T11:14:15.132100Z

The same error is actually reproducible if you just add this into the config.edn of a clean Duct Leiningen template:

:duct.logger/timbre {:level     :info
                      :appenders {:duct.logger.timbre/println #ig/ref :duct.logger.timbre/println}}

 :duct.logger.timbre/println {}

flowthing 2018-11-23T11:15:08.132300Z

@weavejester Do you want an issue for this, too? I guess it belongs in duct-framework/logger.timbre?

flowthing 2018-11-23T11:48:19.132500Z

I think the same error occurs whenever ig/init-key returns something that satisfy ifn?.

2018-11-23T12:44:13.132700Z

Hi

2018-11-23T12:44:39.132900Z

Have you moved all of your non-module keys into profiles? It sounds like you haven't done that step.

flowthing 2018-11-23T12:46:19.133100Z

Well, it's unclear to me which keys are module keys and which are non-module keys.

2018-11-23T12:46:49.133300Z

Anything beginning with :duct.module or :duct.profile is a module key.

flowthing 2018-11-23T12:47:22.133500Z

Okay. So :duct.logger/timbreshould go under :duct.profile/prod?

2018-11-23T12:47:26.133700Z

In 0.10 and below, modules were mixed with non-module keys. In 0.11, there separation is explicit.

2018-11-23T12:47:31.133900Z

Yes, that's right.

flowthing 2018-11-23T12:47:52.134100Z

Or can it also go under :duct.profile/base?

2018-11-23T12:47:56.134300Z

Assuming you want to change the default :duct.module/logging settings.

2018-11-23T12:48:25.134500Z

It depends if you want the configuration in all profiles, in which case put it in base, or just in production, in which case put it in prod.

flowthing 2018-11-23T12:48:48.134700Z

Okay. I think I gave that a try already, but I probably messed something up.

flowthing 2018-11-23T12:48:59.134900Z

Let me give it a go.

flowthing 2018-11-23T13:01:31.135100Z

Well, I have a config.edn like this:

{:duct.profile/base            {:duct.core/project-ns       <http://foobar.server.app|foobar.server.app>

                                :duct.logger/timbre         {:set-root-config? true
                                                             :level            :info
                                                             :appenders        {:duct.logger.timbre/println #ig/ref :duct.logger.timbre/println}}

                                :duct.logger.timbre/println {}}

 :duct.profile/dev             #duct/include "dev"
 :duct.profile/local           #duct/include "local"
 :duct.profile/prod            {}

 :duct.module/logging          {}}

flowthing 2018-11-23T13:02:16.135300Z

And then I do (repl/set-prep! #(duct/prep-config (read-config) [:duct.profile/dev])) and (go) and I get Assert failed: (map? config) from ig/prep.

flowthing 2018-11-23T13:02:34.135500Z

But I guess there must still be something wrong with my config...

2018-11-23T13:03:05.135700Z

Does the assert have a stacktrace associated with it?

flowthing 2018-11-23T13:04:07.136Z

Assert failed: (map? config)
Syntax error compiling at (/foobar/dev/src/dev.clj:45:3).
  at clojure.lang.Compiler.load(Compiler.java:7647)
  at shadow.user$eval30043.invokeStatic(Unknown Source)
  at shadow.user$eval30043.invoke(Unknown Source)
  at clojure.lang.Compiler.eval(Compiler.java:7176)
  at clojure.lang.Compiler.eval(Compiler.java:7131)
  at clojure.core$eval.invokeStatic(core.clj:3214)
  at clojure.core$eval.invoke(core.clj:3210)
  at clojure.main$repl$read_eval_print__9068$fn__9071.invoke(main.clj:414)
  at clojure.main$repl$read_eval_print__9068.invoke(main.clj:414)
  at clojure.main$repl$fn__9077.invoke(main.clj:435)
  at clojure.main$repl.invokeStatic(main.clj:435)
  at clojure.main$repl.doInvoke(main.clj:345)
  at clojure.lang.RestFn.invoke(RestFn.java:1523)
  at nrepl.middleware.interruptible_eval$evaluate$fn__20103.invoke(interruptible_eval.clj:87)
  at clojure.lang.AFn.applyToHelper(AFn.java:152)
  at clojure.lang.AFn.applyTo(AFn.java:144)
  at clojure.core$apply.invokeStatic(core.clj:665)
  at clojure.core$with_bindings_STAR_.invokeStatic(core.clj:1973)
  at clojure.core$with_bindings_STAR_.doInvoke(core.clj:1973)
  at clojure.lang.RestFn.invoke(RestFn.java:425)
  at nrepl.middleware.interruptible_eval$evaluate.invokeStatic(interruptible_eval.clj:85)
  at nrepl.middleware.interruptible_eval$evaluate.invoke(interruptible_eval.clj:54)
  at nrepl.middleware.interruptible_eval$interruptible_eval$fn__20146$fn__20149.invoke(interruptible_eval.clj:218)
  at nrepl.middleware.interruptible_eval$run_next$fn__20141.invoke(interruptible_eval.clj:186)
  at clojure.lang.AFn.run(AFn.java:22)
  at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
  at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
  at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.AssertionError: Assert failed: (map? config)
  at integrant.core$prep.invokeStatic(core.cljc:412)
  at integrant.core$prep.invoke(core.cljc:412)
  at integrant.core$prep.invokeStatic(core.cljc:417)
  at integrant.core$prep.invoke(core.cljc:412)
  at duct.core$prep_config.invokeStatic(core.clj:195)
  at duct.core$prep_config.invoke(core.clj:184)
  at dev$eval30027$fn__30028.invoke(dev.clj:41)
  at integrant.repl$prep$fn__29833.invoke(repl.clj:16)
  at clojure.lang.AFn.applyToHelper(AFn.java:154)
  at clojure.lang.AFn.applyTo(AFn.java:144)
  at clojure.lang.Var.alterRoot(Var.java:308)
  at clojure.core$alter_var_root.invokeStatic(core.clj:5510)
  at clojure.core$alter_var_root.doInvoke(core.clj:5505)
  at clojure.lang.RestFn.invoke(RestFn.java:425)
  at integrant.repl$prep.invokeStatic(repl.clj:16)
  at integrant.repl$prep.invoke(repl.clj:14)
  at integrant.repl$go.invokeStatic(repl.clj:54)
  at integrant.repl$go.invoke(repl.clj:53)
  at dev$eval30047.invokeStatic(dev.clj:45)
  at dev$eval30047.invoke(dev.clj:45)
  at clojure.lang.Compiler.eval(Compiler.java:7176)
  at clojure.lang.Compiler.load(Compiler.java:7635)
  ... 27 more

2018-11-23T13:04:45.136200Z

What does your dev.edn file look like?

flowthing 2018-11-23T13:05:00.136400Z

{:shadow.cljs/watch  {:id :app}

 :duct.compiler/sass {:source-paths  ["resources/sass"]
                      :include-paths ["node_modules"]
                      :output-path   "resources/public/css"}}

flowthing 2018-11-23T13:05:38.136600Z

Hmm... does dev.edn need to have duct.profile/dev as the top-level key?

2018-11-23T13:06:04.136800Z

No, because it's included via #duct/include

flowthing 2018-11-23T13:06:09.137Z

OK.

2018-11-23T13:06:18.137200Z

The resource is literally inserted verbatim where the reader tag is.

flowthing 2018-11-23T13:06:25.137400Z

Gotcha.

flowthing 2018-11-23T13:08:50.137600Z

It must be something odd in my project because if I copy-paste those same config files into a clean Leiningen Duct template, I don't get that error.

flowthing 2018-11-23T13:08:57.137800Z

I'll keep digging. Many thanks for the help.

2018-11-23T13:09:02.138Z

Okay, weird, I'm not seeing anything obviously wrong here. Would you mind running a couple of commands for me?

flowthing 2018-11-23T13:09:09.138200Z

Not at all.

flowthing 2018-11-23T13:09:11.138400Z

Shoot.

flowthing 2018-11-23T13:09:52.138600Z

FWIW, this is not a Leiningen project I'm working on. It's a deps.edn project. Not sure whether it matters.

2018-11-23T13:10:29.138800Z

(-&gt; (read-config) (doto ig/load-namespaces) (build-config [:duct.profile/dev]))

2018-11-23T13:11:31.139Z

Oh, another thing to try is to shut down your REPL, clean your target directory, and restart your REPL.

flowthing 2018-11-23T13:11:41.139200Z

OK.

2018-11-23T13:11:43.139400Z

Since multimethods can be missed by reloads.

flowthing 2018-11-23T13:11:46.139600Z

That yields nil.

flowthing 2018-11-23T13:12:04.139800Z

(Assuming build-config is duct.core/build-config.)

2018-11-23T13:12:11.140Z

Yep

2018-11-23T13:12:51.140200Z

Does the same thing occur when you restart your repl?

flowthing 2018-11-23T13:16:47.140400Z

Yes. I cleared everything I can think of.

2018-11-23T13:17:24.140600Z

Are the dependencies the same as the ones in the template?

2018-11-23T13:17:45.140800Z

:dependencies [[org.clojure/clojure "1.10.0-beta4"]
                 [duct/core "0.7.0-beta2"]
                 [duct/module.logging "0.4.0-beta1"]]

flowthing 2018-11-23T13:20:27.141Z

No! I had the wrong version (0.3.1) of duct/module.logging. :picard-facepalm:

2018-11-23T13:20:39.141200Z

Aha!

flowthing 2018-11-23T13:20:41.141400Z

That solved the assertion error. Many thanks!

2018-11-23T13:20:56.141600Z

No problem 🙂

2018-11-23T13:21:33.141800Z

Actually, let me open an issue for this to give this a clearer error message.

👍 1
flowthing 2018-11-23T13:23:17.142100Z

As a bonus, I now understand much better how the whole thing works. The only thing I'm still not 100% clear on is the module/non-module distinction. For example, this page https://github.com/duct-framework/duct/wiki/Modules lists both logger.timbre and module.logging as modules, but logger.timbre actually isn't…?

2018-11-23T13:25:57.142400Z

Oh, that page wasn't added by me 🙂

2018-11-23T13:26:20.142600Z

I'll make a note to fix that page before release.

flowthing 2018-11-23T13:26:24.142800Z

Great!

2018-11-23T13:27:35.143Z

With regard to modules/non-modules, anything beginning with :duct.module or :duct.profile is a module. In 0.11, a module is a function that transforms a configuration, so config -&gt; config.

flowthing 2018-11-23T13:28:27.143200Z

That latter explanation was what I was looking for — thanks!

2018-11-23T13:28:47.143400Z

In 0.11, a configuration of modules is initiated into transformation functions, and then applied in dependency order to an empty map. This produces a new configuration, which is then initiated.

2018-11-23T13:29:20.143600Z

You can think of Duct's configuration as a "higher-order configuration", in the same way that we have higher-order functions.

2018-11-23T13:29:46.143800Z

Duct's config initiates into an Integrant config, which is then initiated into a system.

flowthing 2018-11-23T13:29:48.144Z

Right, so modules transform configurations and non-modules are things that contain stateful resources etc.

2018-11-23T13:30:00.144300Z

Right.

flowthing 2018-11-23T13:30:06.144500Z

Gotcha.

flowthing 2018-11-23T13:30:14.144700Z

Also, everything works great now :bananadance:

2018-11-23T13:30:42.144900Z

The idea is that you can write a module that adds a whole bunch of configuration so you can abstract common patterns of config.

2018-11-23T13:31:14.145100Z

In the same way that higher-order functions allow abstraction of code patterns, like map, filter, reduce, etc.

flowthing 2018-11-23T13:31:26.145300Z

Yes, that makes sense.

flowthing 2018-11-23T13:33:39.145500Z

Might be useful to add at least one non-module key in the example here https://github.com/duct-framework/duct/blob/master/UPGRADING.md to make things clearer.

flowthing 2018-11-23T13:34:19.145800Z

Where it says ;; ... more non-module keys, maybe have e.g. :duct.logger/timbre to make it clear you have to put it there.

flowthing 2018-11-23T13:34:53.146Z

Also, that page could maybe also make the module/non-module distinction explicit.

2018-11-23T13:35:37.146200Z

Those are good ideas.

flowthing 2018-11-23T07:50:26.131500Z

I have read https://github.com/duct-framework/duct/blob/master/UPGRADING.md and I've managed to set up things so that when I do (duct/read-config "config.edn"), it includes the contents of my dev.edn under the duct.profile/dev key, but none of that is being executed.