Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reloading with component *without* introducing global state #189

Open
codeasone opened this issue Jul 5, 2017 · 6 comments
Open

Reloading with component *without* introducing global state #189

codeasone opened this issue Jul 5, 2017 · 6 comments

Comments

@codeasone
Copy link

codeasone commented Jul 5, 2017

Firstly, thanks for your amazing work for the Clojure community.

I've been using lein-ring for some time now and in particular the auto-reload facility which I find very helpful.

However, I'm just getting started with component, and have reached an impasse where I cannot see a way of getting the reloading benefits of lein ring without introducing global state.

I've created a minimal re-production of where I have got to and was hoping someone could advise on whether what I'm aiming for is possible.

https://github.com/codeasone/component-reload-issue

@weavejester
Copy link
Owner

You don't want to use Lein-Ring with Component (except possibly for creating war files). Instead, prefer the reloaded workflow pattern, perhaps with a library such as reloaded.repl.

@codeasone
Copy link
Author

codeasone commented Jul 5, 2017

I should have provided a little more context concerning my particular motivation and use-case.

Whilst I like the reloaded workflow in cider - based on a variation of Stuart Sierra's user.clj - I also have a use-case where there's a bunch of micro-services starting in a Docker composition with command: lein ring server-headless

This has been working tremendously well over the past couple of months for me, prior to seeking a dependency-injection solution to improve my tests/mocking.

With host volume mounts to the various projects, when I change the code for any service in the composition, lein-ring takes care of reloading, allowing me to execute integration-level and exploratory tests as I go (no restarts really helps with momentum).

I suppose I could introduce something "Heath Robinson" like a /reset route for each of those services and building some tooling in Emacs to poke each /reset end-point on saves, but I'd rather not.

I've just finished porting the project over to mount which is behaving well so far, I may just keep going on that line of work if I cannot find another solution.

@weavejester
Copy link
Owner

weavejester commented Jul 6, 2017

Sorry, I'm not completely clear on your use case. Is this Docker composition in a development or production environment? Is there a reason you can't just open an nREPL connection to it?

@codeasone
Copy link
Author

codeasone commented Jul 6, 2017

I'm the one who should apologise, I should have given the use-case straight away and less ambiguously.

My concern is rapid acceptance-test driven development. I have 8-10 micro-services from our portfolio (if you will) and bring up a docker composition with host volumes mounting each micro-service project directory. I built some tooling called island for this in Ruby actually.

It's been working great, but in my most recent project I want to introduce some proper dependency injection and component looked ideal.

So as you say I could run an nrepl in development mode, but having all the relevant namespaces auto-reload on a save is so effortless I was hoping I could reconcile component with my existing use of lein-ring.

As I mentioned before, mount is proving a better partner and giving me all I need so far.

I suggest we close this issue unless you have a further interest in the approach I'm taking and want to discuss that more.

Many thanks for your time and consideration in reply to the issue.

@weavejester
Copy link
Owner

Perhaps it would help if I described my typical setup.

I use reloaded.repl, which gives me useful REPL functions like (go) for starting the system and (reset) for suspending the system, reloading all changed files, then resuming the system.

I connect to my application through Cider, and Cider has a useful function called cider-refresh that can integrant the functionality of reset into the editor. To do this, we first need to set "before" and "after" function. I typically do this by putting a .dir-locals.el file in my project directory that contains:

((nil . ((cider-refresh-before-fn . "reloaded.repl/suspend")
         (cider-refresh-after-fn  . "reloaded.repl/resume"))))

In my ~/.emacs/init.el file, I have a small convenience function that combines saving a file and reloading the system in one, when I press ⌘R:

(defun cider-save-and-refresh ()
  (interactive)
  (save-buffer)
  (call-interactively 'cider-refresh))

(global-set-key (kbd "s-r") 'cider-save-and-refresh)

This setup is more nuanced that reloading files on save, as it allows me to save temporary work without messing up the running application. However, if you want to skip the Cider integration, you can still have reset-on-save functionality by hooking up a file watcher to reset.

For example, using Hawk something like this should work:

(hawk.core/watch! [{:paths ["src" "test"], :handler (fn [ctx _] (reset) ctx)}])

@codeasone
Copy link
Author

Hmm, the extra control sounds appealing and hawk looks a useful addition.

I'll give that a try over the weekend. Thanks for the advice.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants