For larger blobs of text (like documentation) I would like to store those in separate files and merge them into the duct config, rather than having all in duct.edn. Integrant's expand-key comes to mind. In case of reitit routes I need to "merge" into a list.
I can pass :duct.module/web into my module and manipulate the routes myself. Is this the way to go? Or are there better options?
I went down this path and ended up in circular dependencies. I guess there must be a better way.
Which version of Duct are you using? The current stable version that uses Leiningen, or the upcoming version that's its own tool?
Bleeding edge. :) Sorry, I missed to mention that.
Can you tell me a little more about the blobs of text?
Like, are there lots of little strings of text that might benefit from being in a single mapping file, or are there relatively few but long pieces of text, which might be best put in separate files?
Think of extensive documentation that is displayed in a Swagger UI to document an API.
The more general question is: How can I use a duct module, specifically expand-key to manipulate the initial configuration provided by duct.edn.
So the expand-key method returns some map that is then merged into the outer configuration map. There's more documentation on it in the Integrant README, but essentially you might have a configuration:
{:your.module/example {}}
And then an expand-key defined as:
(defmethod ig/expand-key :your.module/example
[_ _options]
{:some/a {}, :some/b {}})
Then this would expand out to create a configuration:
{:some/a {}
:some/b {}}You could also supply a resource or file path to the documentation. I've been planning on adding a #duct/import "filepath" reader tag to facilitate this, but I've yet to fully decide on the specifics.
Like it might be useful to allow something like #duct/import ["extra.edn" :inner :key] .
To export some key inside a map.
I created a simple module to read the additional files. But in order to merge them into the reitit routes I also gave it :duct.module/web as a dependency, in order to augment what was already in duct.edn . This created a "Circular dependency between :duct.module/web and :duct.module/web" presumably because my module also provides :duct.module/web .
;; in myapp/duct.clj
(defmethod ig/expand-key ::config [_ {:keys [paths web]}]
(let [input (fsdb/ingest paths)]
;; TODO: merge input into web
{:duct.module/web web}))
;; in duct.edn
:myapp.duct/config {:paths ["duct"]
:web #ig/ref :duct.module/web}Yes, that won't work. References are applied after expansions take place, in a similar way to how macros don't have access to variables - they just see them as symbols.
Would a #duct/import reader key solve your particular use-case?
i.e. one that can read data from a separate file?
Alternatively, you could still use a module, but rather than altering the module directly, you add keys to the router that the module spits out.
So you'd merge the extra data into :duct.router/reitit . As long as your module and the Duct web module hit different keys, that should be fine.
After exploring the 2nd options I must say I'm quite happy with the solution. Thanks for the advice.
That said, I think #duct/import would also be useful, in other cases.
Yes, I guess that would work. And it would make it more obvious where things come from. On the downside of a bit more to type.