Closed kanwei closed 9 years ago
Thanks for the offer :)
The only concern is maintaining multiple templates for all the different options. Originally, I was supporting both hiccup and clabango, but I found that it was a lot of work to keep things in sync when I made updates.
I was thinking of reducing the number of profiles in the future. For example bootstrap and ClojureScript could be included by default and if somebody didn't want them they could remove the files from the project.
Another idea I'm toying with is to restructure how profiles are managed to make it easier to keep track of different features and how they interact with one another.
So, I'd probably hold off on adding Stencil until I clean up the template a bit. I'll leave the issue open for now, and once things are in a better state I'll give you a shout :)
@clojens
I agree with what you're saying and I'll try to explain the rationale for some of the decisions below. :)
Yes it's newbie friendly, but how newbie is someone who ends up with Clojure really?
I think it's more of a case of people being familiar with web development in other languages, such as Ruby or Python, and being newbies in Clojure web development. This is where the rationale for using Clabango comes from.
If somebody has done development in Rails or Django, the HTML templating will look very familiar. In my experience it makes it much easier for people to get started.
I like Hiccup, but it also requires discipline to use. If you're not careful it's very easy for client logic to start bleeding into the handlers. It also means that people have to be familiar with Clojure to work on the templates.
Again, if a team is interested in experimenting with Clojure then it minimizes the number of people who have to know it. A designer can continue making templates like they always have for example.
That said Hiccup is included as a lib-noir dependency, so it is there out of the box.
Right now your extension code probably is tied too much to the framework itself and you need to abstract out the logic to make it more drop-in friendly for potential plugin developers.
I definitely agree with you there. I feel that even in terms of the way it's used in the framework it's not sufficiently flexible to facilitate what I'd ultimately like to do.
The key thing I found that's needed is to maintain a dependency tree for all the components. Then adding new components, such as different templating engines would be relatively simple.
Unfortunately, I simply haven't had time to sit down and rewrite it with the lessons learned in mind.
but perhaps we could define the plugins/extensions as a protocol to allow for easier development and support of 1001 ideas/wishes
I think that's a great idea. This would allow people to create plugins that can be maintained separately from the core framework.
So, I'd definitely love to revisit the current approach and if you have any ideas and interested in collaborating on this I'm very much open to that as well.
Also, with Hiccup something like formlets might be interesting to explore as well.
Heh, I didn't make it clear, but I wasn't actually referring to Leiningen dependencies. I think clojars is definitely the de facto repository nowadays and most projects that are in use tend to stay up to date fairly consistently in my experience. There could obviously be improvements, but they all have trade offs and the current system works fairly well in my opinion.
I was actually referring to Luminus specific dependencies between the components. For example, if you add a database component, it needs to be initialized in the handler/init
. So the component should inject its initializer there. If a component defines a new route, then the route needs to be added the the routes table, and so on.
Then some components may depend on other components. For example, currently +site
requires that at least one database component is selected.
I think that the plugin architecture for the framework should allow the individual plugins to specify what components they depend on and how they will modify the base template to add things like initializers or routes.
If a component is selected, then the template needs to know what other components should be activated by default.
This would also provide the protocol for creating plugins that you mentioned. A plugin would specify what files it creates, what files it modifies and how it modifies them, and what other plugins it requires to work.
I see you've been rather busy, I'll have to look through the proof of concept a bit more in depth. :) Just to address some general points. I think the hot/cold extension swapping is definitely a good idea.
Injecting/modifying code into an existing project would be quite tricky though. I'd rather not go down that route. I think a plugin should be able to create files and update project dependencies. There could be a reserved namespace that wouldn't be user editable where plugin could register things like custom routes.
I also think that each category of plugins should have a protocol associated with it. Then the plugin could simply implement the API. For example, if it's a database plugin, then it would have to implement the common db init function and so on.
Finally, we'd need to figure out what the required attributes for each plugin are. I like your idea with having id, version, description, and group. I think we'd also need dependencies. So if you install a plugin x and it needs a plugin from group y, it can specify that.
Each group should in turn specify the default plugin. If the user already selected something from the group then we're good and if not then the default plugin should be selected.
Also, don't know if you've looked at Racket formlets, I think they're a neat idea and something that could be done with hiccup fairly easily.
I'm thinking we might want to step back a bit and identify each of the issues we'd like to address and then focus on them one at a time.
So, for the big ones to me are the ability to add/remove extension after the project is created and being able to track the interaction and dependencies between each of the individual extension.
For the first problem, I don't think it would be viable to use a heuristic to figure out if the extension is modifying generated code or user code.
Instead, it would probably be better to have some reserved initialization/configuration namespace that the user doesn't edit. The code there would be guaranteed to be generated and then it would follow our protocol.
We could also have a config map (maybe in project.clj
so we can just make a lein plugin) that's used by the handler during initialization. The map could specify what plugins are active and their entry points, eg:
:luminus-extensions
{:db {:id :postgres
:init db/init}
:template {:id :hiccup
:layout hiccup/render}}
This way the plugin could could be run via leiningen and hook up the extensions as needed. I think each extension should have separate files and support a common protocol for hooking into the application. For example, if we have a render
function in the layout that is responsible for rendering the HTML templates then
each type of template should conform to it and you could simply swap the renderer to the one provided by the template.
I think that each extension could in turn specify what group/bundle it depends on and provide a default if none is provided.
I would say the first order of business would be to decide what the extensions will be allowed to do.
Some of the things I can think of would be:
The framework would be then responsible for providing a common API that all the plugins conform to and then loading up the selected plugins and binding the supplied implementations to the API functions. As with the example of the render function, where each plugin could do different things for rendering its templates, but would all conform to a common protocol.
So, let's think of what we think the extensions need to be able to accomplish and how we can facilitate that in a clean and structured manner.
So, just to comment on where I think lein
would fit in and why it's a good idea to build around it. The first issue is that, as you pointed out managing Java classpath by hand is no picnic. :)
Lein already provides all the functionality for tracking dependency hierarchy for the libraries. I think it makes a lot of sense to piggy back on that. For example, if an extension depends on some library, it can specify it and then we can track it through the standard dependency mechanism in lein
and if it has dependencies of its own and so on that just works.
For our purposes tracking jars is only part of the solution. We'd also like to be able to add/update/remove files directly in the project. My understanding is that a lein plugin can pretty much do whatever it likes. So, once lein calls our plugin we can easily do all of the above.
I think it would make sense if plugins were packaged as jars and deployed to Clojars. However, instead of setting them as dependencies directly, the user would specify them under extension in our plugin.
Then the plugin could download the jar, extract it, read the config map and do whatever it needs to do.
Sorry for shorter responses, I'm a bit tied up for the next little while so I'll add comments as I have time. Also, I don't mind this thread being an idea dump. :)
Currently exploring and moving over some code, setting up some more protocols and taking a look at this project to see if we can find some useful strategies there
Sounds like a plan. I'm currently a bit tied up with this, but I'll keep checking in. :)
Chiba does look rather promising as well.
Dmitri, I noticed the pushes to the repo, hadn't checked it out yet though. Preoccupied with some different interesting 'problems' to tackle to achieve some effects. Heh.
So I found a nice illustration of one of the purposed possible features a la formlet
like:
https://github.com/clojens/formative-demo/blob/master/src/formative_demo/web.clj
Clearly this is an example of what Racket does better. But Racket also has some significant downsides to it. Basically it just shows a good reason to do what we would like to do... although pretty darn elegant in language, compared to other solutions (imho, I find Clojure a lot more aesthetically pleasing then Racket and I'm glad we don't have to miss out on laziness, to name one thing I believe Clojure does ultimately a better job). Ok ok, this stuff is pretty insane http://docs.racket-lang.org/htdp/ I have to agree... its a very compelling story, but not ours to tell.
One thing particular about the stuff I've been theorizing about was that I kept coming up with the notion that there are some very distinct elements at work in a potentially 'umbrella' like (super)project with a rich extension system.
E.g. in order to achieve some more complicated tasks in a dynamic fashion, I've mind crafted the use of a 'bundle' like container with some basic controls/fields related to it, so we may do stuff in a sequential order, based on mutual exclusiveness, parameterize/constrain the whole plugin install/setup/bootstrap/hooking into framework events (open problem still) really. Now we may have several essential features of the framework be supplied by our own plugins from start. This still seems like a good plan. We immediately notice this way that Clojure with immutable types, any Lisp with data as code, begins to work in your favor. Vars have global scope, we can just use the fully qualified names 'abc/some-fn' and it works throughout the JVM. But it becomes more complicated when trying to figure out what functions might be available through the :use
or :required
(prefered.....) method. This is something I haven't figured out yet. If plugin x
uses hiccup
and only creates 2 new functions on top of it, how can we transparently share the hiccup functions. I believe all we can do is just create the Var in the new namespace. Or copy over entirely the solution (seems not the way to go and what we like to avoid in cljs).
So there is this path we could take where we decide that really every plugin is a self-contained container (directory) with the following conventional
setup perhaps for a simple-plugin
v1.
src/luminus/environment/plugins/simple-plugin/1.0.0
├── config.clj
├── project.clj
├── resources
│ └── public
│ ├── app.js
│ ├── dashboard.html
│ └── main.css
└── src
└── core.clj
We have a few options here, without tying down too much. One is that we just basically slurp the conventional lein project.clj
files which have a :luminus
key holding details on the package/plugin. This may limit us a bit too much I fear. Perhaps a config.clj
file as additional/minimal requirement would work. Imagine some provides a very simple .sh
interpreter of language foo, someone could simply make a config.clj
file with some manifest-like key holding a perhaps a later entry point or chainstrap script (since the .sh actually just downloads curl the latest release and unpacks it etc). Whatever complex situation you try to imagine: we don't care as long as at some point its said that files with some extension like bazbara.rb
needs to be ran with executable plugin/ruby/ruby
we should be fine. But this method is even a bit crude perhaps, relying on .sh
to do stuff, it would be better to properly wrap some things in Clojure functions e.g. (render :rb "some ruby code...)
and although we don't really plan for this, it might be a good way of trying to look at it from a neutral/generic viewpoint. Perhaps we will end up with a generic Code
type, which holds some information by itself on how to identify its own content (regex I'd figure) and thus know which renderer to select but that may be a bit too much black magic for some :)
But ok the basic steps, as we've been outlining them so far more or less are:
lein luminus new my.app
but really this becomes a fundamental stance I feel we might need to take, its my personal conviction that the terminal is essentially flawed for displaying this kind of highly interacting and dynamic componentsStep out for a sec to emphasize my standpoint: the chances that someone successfully, without having prior knowledge (e.g. reading the f* manual), configures a solution to the desired end-result, even if the solution is of even moderate complexity, are second to none. Often its a lot of guess-work that becomes involved "what would they mean with the system tray" might a total newb ask, up to the seasoned professionals "object? what object?". I've been through the entire process at least a thousand times, perhaps 2, 3 so many solutions I've tried: there is always a learning curve. Some more steep then others. Imho it illustrates your problem so narrowly: of course you are limited to switches as +db
or +site
but really need to rely on people reading the manual and you can't get any sufficiently 'deep' enough level of tweakage through a single readline/command. Unless you use pipes,... I'm not scared of the shell (zsh proficient) but this doesn't seem great. So your dilemma is clear: either keep config commands simple (and when malconfigured deleted the folder, start again) or resort to something else.
The something else could be a file, as we traditionally solved this problem in the computer world: setup/configuration through /etc
and the likes - in some way or another. Again, to me at least, this seems very 1960
all over again. I'm no propagandist of 'future' movement per se, I very much value the ancient tools on my system but really I only use e.g. awk
when I absolutely need to. But I have to admit: this is a strict matter of personal preference: creating a GUI for the sole purpose of configuring ourselves might not make so much sense, if it weren't that it would be a slick chance to work out fundamental building blocks that we'd like to have anyway (or be made available to other plugin developers) which really begins to make sense when you consider you want to be able enforce a certain 'standard' style throughout the project, not just pages. CSS really only superficially covers this: I'm talking the whole shebang: distributed logic of framework moving components (perhaps even backed by some langohr like infrastructure of messaging because 'events' are something we can/still would need to define as well).
I'll refactor this text somewhat later, as these things sink in a bit more.
For the rest, adding to namespaces, finding stuff under it, copying Vars/macros is easy but often may not be needed, code to inject in forms I got somewhere as well, extend-protocols works like a charm and is easy too, I'm no fan of the regular defrecord
but defrecord2
needs a little work too (has a few bugs/undesired effects and updated/forked/published/shared somewhere since its really outdated). Hot loading new dependencies is easy too using pomgranate
and so on. Really we have a lot to work here with. Some fs i/o we might look at pallet
for this, also configuration and add-service
(add-extension?) came from there with the chiba
solution came from there. Also they use a central ~/.pallet/
like folder we might like at some point as well so had some good examples. The NLP solution hotel-nlp
had quite some nice examples of protocols, including a component
one that I decided to look later at.
One thing that myself and others are a bit undecided about yet are typed values, solutions like dire
, slingshot
and typed
are great but still face their own problems still. https://github.com/MichaelDrogalis/dire/issues/9 Understanding core.logic
on a more proficient level than my current one might help too.
The reason I look at them is because I have some desire to idiot-proof the whole extension system and probably here and there would need to reside to graceful-fallbacks but also rigid enforcing. Dynamic typing in Clojure is great, but a optional typed system would have been better perhaps. That way some hypothetical half-arsed plugin can't, and believe me it would, ruin your entire project. Definitely one role we might want to adapt regardless, is to safeguard the user from irreversible effects e.g. adding a plugins renders all files to a single 0 byte one, destroying any source :) I guess thats a big nono that git might be able to ensure protection against, somewhat at least. No protection against our own stupidity possible I guess.
To conclude this little post, one thing I haven't really broken my head on is events. Perhaps I'd do good to watch http://www.infoq.com/presentations/Event-Driven-Programming-in-Clojure lateron this weekend.
Cheers.
To have something more like formlets we'll need to start off with quasi-quoting I think, due to the way Clojure/JVM expands the forms, it hardly readable anymore.
Something like that anyway. But this project might actually could solve this so I'm gonna look into it a bit more and sharpen my macro-writing skills :P
So one thing the formidable
demo has emphasized to me is, the more or less intermediate (quasiquoted) forms we'd like to end up with (or at least an approximate of it).
Firing up the jetty server is easy, just use the single adapter
(ns luminus.plugins.serve
(:use ring.adapter.jetty))
(defn handler [request]
{:status 200
:headers {"Content-Type" "text/html"}
:body "quasiquoted forms would expand here, then rendered?"})
(defonce server (run-jetty #'handler {:port 8080 :join? false}))
;(.start server) (.stop server)
Which really more or less does the same. But one thing to look at still is routing, and the logic of formlets itself but I had a hard time with some Cljs and LT issues today, especially when I tried to run the form demo but it was good since I was trying to fix ClojureScript One project for current releases, so I had to manually walk by all the lein-cljsbuild map options etc. Also how I needed to symlink against my git cloned version of cljs in the project dir etc.
I was thinking, I like the concept of auto named fields I guess but we might do a bit cleaner job at the whole
,{ "Stuff" . => . stuff}
just utilizing (name :foo)
like tricks and make some mapping optional. I guess this is also where typing starts to stick its neck up. What we'd really need to do, is what Chris Granger is doing with LightTable. If we want dynamic forms -> server, I don't think we have a viable option besides cljs and node.js websockets. Or we'd be limited to "POST" req/res but this would really kill any socket based trickery we'd like to pull e.g. evaluate as you type without generating a ton of client code to contain the logic. And you'd really have nothing more than client-side pre-post form validation stuff, while what we seek after is the computation of optionally (dependency graph) related nodes and values coming from plugins, database, server file slurps and what not. For the whole hot-reloading idea we'd probably very much need such an approach as well. I should give this some more thought.
Oh, and quasiquoting looks nice :)
Btw this should probably be the starting point especially taking notice of the prerequisites for reloading without restarting the entire vm instance:
c.t.n.repl/refresh will destroy those Vars when it reloads the namespace (even if you used defonce).
(def state-of-world (ref {}))
(def object-handle (atom nil))
Store it locally in an object that represents the running state of your application. Then provide a constructor function to initialize that state:
(defn create-application []
{:state-of-world (ref {})
:object-handle (atom nil)})
Well we'll need a place to store the extensions and configurations anyhow, so this seems like a good one. Imho the luminus framework might be a very simple project in essence, piggyback-riding on both leiningen
and namespace tools. After which I feel it might be best to start thinking about any additional stuff as soon as possible in terms of "core" plugins. That will offer a great chance to further think out the rationale.
I found the article on STM a good starting point to get some use-cases and examples going. The whole io!
for side-effects / platform operations might be good to keep in mind.
The second rule for making your application reload-safe is having a consistent way to start and stop the entire system. I like to do this with a protocol implemented by each major component in the system, but smaller applications can probably get along fine with just a pair of functions.
The point is that you need a convenient way to destroy all the built-up state of your application and then recreate it from scratch. The "stop" function should stop any running processes, release all external resources, and zero-out any internal state. The "start" function should create internal state, acquire resources, and start processes.
It might take a few tries to get it right, but once you have a working start/stop you can have a workflow like this:
Step 1. Start up a REPL. Step 2. Load the app:
user=> (use 'clojure.tools.namespace.repl)
user=> (refresh)
user=> (def my-app (create-application))
user=> (start my-app)
Step 3. Test it out.
Step 4. Modify some source files.
Step 5. Restart:
user=> (stop my-app)
user=> (refresh)
user=> (def my-app (create-application))
user=> (start my-app)
See tools.namespace for some more information.
Step 5 may be skipped to support a watcher/recompile
like environment a la Guard/Livereload. But once we go Cljs, its a compilation process anyway - and although very much a newb with cljs/external libs / google closure library components, it might be food for thought to offer a more 'complete' cljs setup to start with... I gotta think some more about this but the way I perceive it now more or less:
And then we have the new world, a whole different kind of dynamic page, that of streaming sockets which connect the front and back real-time like this bad boy.
Perhaps this is why I initially called the experimental repo luminus.dashboard
because I'd like one of the final stages to involve a more sophisticated browser-based control panel like system. So stuff like this bidirectional communication project might be useful as well.
Just archived another long-arsed post, this clearly isn't working if I type out every thought since sometimes I reach conclusions but more often, have to assume things. I feel it might be valuable if we could do a conference call sometime in the future and in say 30 minutes hash through a session trying to determine some courses of action, (re)define/solidify mission/vision statement and outline some directions before anything. I believe we both are pretty much comfortable with the namespaces as mechanism for structural sharing/isolation of code/data but other issues remain unsolved. Perhaps a Skype session will work, or something with a drawing board, but the idea being I can throw ideas at you and ask some more meaningful questions instead of assuming stuff half the time. Let me know anyway if you'd be game.
Some primary architectural stuff:
Some things we may find useful as initial core-plugin functionality to support further developments:
As I mentioned, I'm a bit tied up already with a couple of other projects and I think the scope here grew substantially from what we originally discussed. As such I'm not sure I can actually commit the time this project at the moment.
Also, I think it would be good to take a step back and decide how the core functionality will be achieved and experiment with a few implementations to see what works best.
As I recall, the original problem the we wanted to solve was to allow better handling of extension and the ability to manage them in an existing project.
I would recommend focusing on this goal first and actually seeing how well it works. I wouldn't worry about managing libraries at runtime and adding things like formlets just yet. You need to have a simple working core before moving onto these things.
The initial plan we discussed was to possibly provide a reserved namespace and a protocol that can be used by extensions. This would limit the interaction with user code and allow adding and removing these extension in an existing project. The extensions also need to track their dependencies and categories, so that an extension could know what other extensions it relies on and what extensions it conflicts with.
You're right of course. This is the closest I got to actually finding the cleanest solution out of the box. Lesser dependencies and a tad better features then pomegranate. Warns on conflicts etc.
The extensions also need to track their dependencies and categories, so that an extension could know what other extensions it relies on and what extensions it conflicts with.
So we have Clojure / lein dependencies. If each plugin/extension is a project in their own right (e.g. a git/clojars published lein project.clj
with :dependencies
key, we can easily build that tree. Name and version number and a push to a repo leiningen knows is all you'd need. The categories can just be keys, either in defproject or elsewhere (e.g. config.clj) then gather/group them is probably the easiest.
I don't think lein dependencies serve the same purpose conceptually. We would specifically care about categories not individual dependencies. For example, if an extension depends on a database any database provider would satisfy it. We wouldn't care whether it was Postgres MySQL, etc.
I think each extension should take care of managing what categories it depends on and what category it satisfies in turn.
Have you ever checked out Immutant? I just came across the name again and decided to check it out, it seems like a rich ecosystem around JBoss, anything for/against using it? The level of interaction looks sophisticated and allows for modules to be added, custom types, domains, messaging, events prolly, multiple dbs etc. This already looks like stuff I initially had in mind in terms of control over nodes and GUI over terminal.
Immutant is a full blown app server and there's nothing wrong with using it, but it's somewhat different from what I have in mind for luminus. Currently, most people prefer to have light weight apps running on jetty or http-kit and tying the platform to a particular app server would exclude majority of the users.
It sounds like you're interested in providing a full end to end platform for applications. My idea for luminus is primarily to provide a curated set of libraries that work well together.
Ultimately, I want the user to have as much control as possible over their application in terms what libraries they use, how they deploy it and what components make sense for them.
@yogthos
Immutant is a full blown app server and there's nothing wrong with using it, but it's somewhat different from what I have in mind for luminus.
That's cool, kinda figures and now I know there is already something covering it, I'm fine. Luminus is probably more up my ally as well, not so full blown :P But I like to learn and try to overcome some challenges, so lets try.
So back to business, I did a little work on some ideas I had and inspired by this and this articles, I've tried to make a beginning to understand everything in it.
Instead of the application being a singleton tied to a JVM process, I write code to construct instances of my application, possibly many of them within one JVM.
@yogthos
Not sure if you tried pedestal yet, I'm doing the tutorial right now and some part on how they deal with templates was perhaps something you and I talked about earlier with the formlet
idea. Perhaps best explained in the tutorial. If you are interested. Seems like a lot of trouble they go through to decouple stuff was in line with what I was experimenting with for luminus, at least for a great deal the whole start-up from dev/user etc. to spawn instances.
Cheers Rob
I played around with it a bit, I haven't really seen enough of it to make an opinion on it. I think I'd have to try using it for a project first. It might be an idea to try and build on top of it though.
Alright. Just checking, since you never know.
That's the thing, I was, without knowing myself at first, working towards the same goals pedestal has. The reasoning also being much the same, primarily that reloading namespaces of modified code was possible, but only once you'd abolished all global state from the app and contained in a single object. The reason I mentioned a message framework earlier was that I already realized you'd have a huge problem in communication through the layers and that a formalized message format would be the solution to that. They took it a few steps further with the function names correlating to the message keys and such, but the ideas remain quite robust and not too much black magic goes on imho. While I was no way as far as pedestal, I also was unaware of its existence. It's hard seeing the end of the maze through opaque walls.
Now I've had a bit better look, its very promising indeed, perhaps I'd wait a little longer since there are concrete plans to implement service channels as part of the infrastructure as well as some other changes. Although recently a stable (non-SNAPSHOT) version was released, if I recall correctly the first as well.
Some other project that has had some great success in nailing that part down, is Bruce Haumans dot game, its interesting because mainly the way core.async
got used, but I'm sure you learned about this project through the irc channel since it got talked about for a while.
There is though, at least in my book, one significant downside. It is considerably more complex than anything luminus does right now. I had no problem getting up to speed on familiar ground with luminus, Pedestal definitely took a few days. Let stuff sink in and experiment before I only moderately 'got it'. The todo-app had a nice beginners tutorial though, high-quality. Of course, this partially has to do with it being relatively new, and admittedly stuff could be made much easier/friendlier if some future project would perhaps abstract the commonalities of a web framework in the reactive message framework. For one, concepts such as components for pedestal are mentioned, but no one has yet produced a project demonstrating this. It would, at least for people getting started, make a world of difference if e.g. routes, pages, markdown, plugins what not, would be in place for the user without having the need for them to work on the (possibly huge) vector of vectors of sets of maps that the data flow is, instantly and offering a perhaps more gentle step to a more plug-and-play experience.
Another interesting angle was the guy who took client-side routing a la History.js to pedestal example client-routing. This would be something candidate to plumbing the routes a bit better. Also, the canonical MVC todo-app sample is a nice example of some of the advanced stuff you can do in the GUI with templates. The advantage overall of using something like pedestal to build on-top as you said, is all the plumbing (tools/middleware/renderers) that has already been done. Especially the 'record actions in browser' feature is very appealing next-gen stuff but it all requires insight in the framework messaging as a whole, else you'd have no idea how to use it :)
I figured I'd play with it a bit more this week, there's a lot I still don't fully grasp yet, and it's really something else programming an app this way, I at least found it quite hard to get started and reason about an app in terms of those messages being passed around. Some things that haven't really been done yet, is for example interop with markdown for example (how to compare/use with pedestal templates?). On the other hand, pedestal does make it great to easily isolate problems so any luminus 'plugin' could essentially encapsulate their own data model / flow and work with the rest fully optional (basically a plugin would expose its functionality by putting messages on a input queue, providing renderers, templates and such by itself, then merging those with the main tree. That all seems quite viable.
sorry for the long-ass post again, I tried to keep it short... ><
Pedestal is definitely promising, but I agree that the complexity there is substantial. It's also fairly immature at this point, so I'd take a look and see approach with it. It does look like it will allow a lot of the things like modules to work well though. I think it's worth waiting to see how it develops.
I'm building a Stencil app... is there any interest in doing a stencil template, or is someone already working on it? I can probably do that if no one else is.