2013/9/11 Softaddicts <lprefonta...@softaddicts.ca>: > We load configuration data once from zookeeper and conceal it in a name space. > Changing the context is quite simple, we reload resources in this name space > using a minimal list of properties which says where the configuration data > should be > pulled from in zookeeper. > > This is doable from the REPL at any time. No other name space keeps > this stuff in vars. Any external resource is pulled at runtime from the > configuration name space where they are cached except for some very short > lived > structures (requests to storage, locks, queues, channels, ... for the > duration of > the request). > > Test configuration data is also contained in zookeeper. Tests > pull from this configuration tree the configuration they need. > > Part of the test stubbing is kept in this configuration. > > No mutations occur in the configuration data from zookeeper during the > "normal" > app life cycle. We allow config changes while the app is running but in a > special > context of its life cycle. Namely the app has to enter a state that allows it > to > reconfigured itself. This means that workers have been stopped, .... > > We can test a new configuration without mutating the previous one in zookeeper > using a versioning scheme. > > This requires some discipline enforced by the configuration name space API. > So far it's been a charm to work with.
Sure, and adding a binding call providing the same config as the one found in root would allow for the whole thread to consistently read the same value. > > Luc P. > >> As far as possible, I think it is best to try and minimise mutable global >> state (like mutable configuration data) and implicit context (like dynamic >> vars). My preferred approach is to pass the configuration data (as a value) >> to a higher order function that constructs the configurable object / >> function appropriately. I regard this is more of a "functional" style. So >> you might do something like: >> >> (def my-ring-application (create-application config-map)) >> >> Once constructed, the ring application is fully configured and doesn't need >> any extra parameters, i.e. it works just like a regular ring application. >> You can treat the ring application as being effectively immutable. If you >> want to reconfigure, then just create a new one! >> >> This approach has several advantages: >> - It's highly composable. Your components can easily be plugged together, >> since they aren't bound to any external configuration state. >> - You can perform some significant optimisations in the construction phase >> (depending on the nature of the configuration you might be able to >> eliminate validation checks, optimise the size of buffers etc.) >> - It's highly testable. Just create as many differently-configured >> instances as you like. >> >> The main downside, of course, is that you need to be thoughtful and do a >> bit more work in designing the constructor function. But I think that's a >> worthwhile activity - it can often lead to a better design overall. >> >> Note that this technique can apply to much more than web applications. You >> can even use it to construct objects that themselves contain mutable state. >> For example, I use this method to construct the entire GUI + running game >> instance for my little Clojure roguelike game "Alchemy". There is no >> mutable global state at all - you can launch several totally independent >> game instances from the same REPL. If you are interested, see the "launch" >> code at the bottom of this file: >> https://github.com/mikera/alchemy/blob/master/src/main/clojure/mikera/alchemy/main.clj >> >> On Tuesday, 10 September 2013 15:19:35 UTC+8, Alexandr Kurilin wrote: >> > >> > I'm trying to determine how to best deal with the concept of globals in >> > Clojure. Say I have a map of configuration values for my Ring app, populate >> > at app startup from disk or env, and I need to reference the contents of >> > this map from all over the project. Assuming MVC, models and controllers >> > all would be interested in its contents. I just want to clarify that the >> > question is not so much about "configuration" as it is about dealing with >> > state that many different components in an application might be all >> > interested in. This is an issue that seems to arise very often. >> > >> > I'm seeing a couple of options here: >> > >> > - Pass the configs map along with each Ring request with some >> > middleware, and then down every subsequent function call that might >> > eventually need it. It's a bit of a hassle since now you're passing more >> > data, potentially increasing the # of parameters, or forcing you to use >> > a >> > parameter map everywhere where you might have passed along just 1 >> > primitive. Nonetheless, this is as pure as it gets. >> > - Def the configs map in a namespace somewhere and refer to it from >> > wherever, just like global state. The trick is now that now you're no >> > longer certain what e.g. your model functions are expecting there to be >> > in >> > the system and testing becomes trickier. Now you have to either lein >> > test >> > with a separate set of configurations (which doesn't actually give you >> > much >> > per-test granularity) or use with-redefs everywhere to make sure the >> > right >> > test config state is being used. >> > - Something in the middle where perhaps you agree to never directly >> > reference the configs map from your models (again, thinking MVC here) >> > and >> > instead only ever access it from controllers, and pass the necessary >> > options along down to the model functions. This way you can test both >> > controllers and models in isolation in purity. >> > >> > Ultimately I want to contain the chaos of having to know internal >> > implementation details of my functions at different layers and want them to >> > be easily testable in isolation. It does seem like the first option is the >> > most straightforward, but I'd be curious to find out what those of you who >> > have deal with the problem for a while would recommend. Purity all the way, >> > or are there patterns that can give you the best of both worlds? Or what >> > else? >> > >> > >> > >> >> -- >> -- >> You received this message because you are subscribed to the Google >> Groups "Clojure" group. >> To post to this group, send email to clojure@googlegroups.com >> Note that posts from new members are moderated - please be patient with your >> first post. >> To unsubscribe from this group, send email to >> clojure+unsubscr...@googlegroups.com >> For more options, visit this group at >> http://groups.google.com/group/clojure?hl=en >> --- >> You received this message because you are subscribed to the Google Groups >> "Clojure" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to clojure+unsubscr...@googlegroups.com. >> For more options, visit https://groups.google.com/groups/opt_out. >> > -- > Softaddicts<lprefonta...@softaddicts.ca> sent by ibisMail from my ipad! > > -- > -- > You received this message because you are subscribed to the Google > Groups "Clojure" group. > To post to this group, send email to clojure@googlegroups.com > Note that posts from new members are moderated - please be patient with your > first post. > To unsubscribe from this group, send email to > clojure+unsubscr...@googlegroups.com > For more options, visit this group at > http://groups.google.com/group/clojure?hl=en > --- > You received this message because you are subscribed to the Google Groups > "Clojure" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to clojure+unsubscr...@googlegroups.com. > For more options, visit https://groups.google.com/groups/opt_out. -- -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.