config-r/config-r

Issues with shared config

adamralph opened this issue ยท 10 comments

An issue to discuss https://github.com/TheFastCat/ConfigrRepro raised by @TheFastCat.

@TheFastCat I cloned your repo and it built in VS just fine.

However, I cannot run the tests at all. The test projects simply don't receive the .csx files in their output.

My initial reaction is to question the basic approach. IMO the root config file for any app should live alongside the application entry point. This means having debug/release .csx's for each possible entry point in the repo so in your case, API, Core and Web. These .csx's can internally do whatever they like, including loading a shared csx from a well known location. This is one of the fundamental benefits of ConfigR. E.g.

// Web.Debug.csx
LoadScriptFile("path/to/shared/debug/config.csx");
// Web.Release.csx
LoadScriptFile("path/to/shared/release/config.csx");
// WebsiteStartup.cs
...
#if DEBUG
    Config.Global.LoadScriptFile("Web.Debug.csx");
#else
    Config.Global.LoadScriptFile("Web.Release.csx");
#endif
...

Regarding the tests, IMO a white box test (i.e. referencing the target code and invoking it directly) should not be relying on config. Config is an application entry point concern only. A black box test, on the other hand, should be relying on config and should be spinning up an instance of the target app from the target app output directory (which will naturally contain the config) and should invoke and make assertions about the behaviour of the app via it's external API, e.g. HTTP calls, message queue, command line args, stdin, stdout, etc.

I think if you switch to this approach, the problems you are seeing will hopefully just fall away.

To answer your questions specifically:

  1. I think the above is the elegant solution you are looking for. Configs located next to each app's entry point and internally loading shared scripts from whatever location you like.
  2. ConfigR is already very flexible about loading from arbitrary locations. Config.Global.LoadScript() can be supplied with any path you like based on whatever logic you like. You can even use Config.Global.LoadWebScript() to load configs located at HTTP endpoints! ๐Ÿ˜‰
  3. This must be some kind of Visual Studio oddity. I don't really know why this might be happening. They are just text files with nothing special about them so this isn't anything to do with ConfigR specifically.
  4. Same as point 3.

@TheFastCat I hope this helps. Feel free to flame me if you think I'm completely wrong ๐Ÿ˜‰.

Adam you are most definitely right! After a day of grinding, what appears clear to me now is that my "issues" were really just the effect of incorrectly conceptualizing (and mistreating) configR .csx configurations as static files (like .html, .js) instead of entry configurations. The nature of how they dynamically execute as scripts within the context of configuration has taken me some time to wrap my head around -- as you saw first hand from the previous code.

I remade the original git repo (and renamed it): https://github.com/TheFastCat/Configr-and-AutoFac-Playpen . I'm much happier with this solution I distilled from your advice. Though I am still scratching my head on how to integrate your whitebox/blackbox testing best practices. Testing the Website.csproj from a separate assembly while consuming the Web.Debug.csx for instance seemed kludgey (since it hard-codes /bin/ references to #loaded, depedant .csxs) - so I recreated a separate config for the test project :| at least they will pass (everytime!) now...

Your perspective helped me a great deal. Thanks for spending the time to look at a (not simplistic) repro of misuse of your code and unrelated visual studio quirks (the apparent culprit btw: http://goo.gl/80bBws). It might not seem like much effort to spot such flaws and redirect my attention but being the only .net developer on my team makes picking up new frameworks sometimes harder than it should be especially without anyone to sanity check or think aloud to. As thanks I'd like to buy you a few beers if you have a paypal account I can donate to?

Best,
Reid

also - this ConfigR functionality I piggy backed into your existing acceptance tests doesn't seem conceptual to me: https://gist.github.com/TheFastCat/8c9f81fc54aecc359133
(--maybe I am just tired.)

@TheFastCat I'm glad I could be of help!

Regarding the whitebox test, what I would do is actually spin up the real website. This is usually best done using a build script. In my day job we use Rake for this kind of thing (with an eye on switching to https://github.com/bau-build/bau in the future). We have an 'accept' task which runs the acceptance tests and takes a dependency on a 'start' task which starts the app. The 'accept' task ensures that the 'stop' task is invoked before exiting. For running tests in VS, I execut rake start from the console, run my tests in VS and then execute rake stop in the console. For debugging tests, I usually start the app in one instance of VS and then run the acceptance tests in another. With this approach the website is a real running instance of the website which uses its config in the normal manner. This, of course, isn't the only possible approach.

Regarding the loading of the nested config file, using #load foo2.csx is very different to using LoadScriptFile("foo2.csx");.

#load is built into scriptcs itself and effectively it combines the contents of foo1.csx and foo2.csx into one file. This is why you are able to access local variables from foo2.csx in foo1.csx.

LoadScriptFile() on the other hand, is a ConfigR concept. It will execute the specified file, the config it defines will be loaded, and then exits the method, unloading the specified file. This means foo1.csx would have no knowledge of the code inside foo2.csx. What this allows is 'cascading config'. I.e. I define some config in a central file and then I override that config locally. The way it works is that the values in the outermost file always override the values in the inner files which are loaded with a chain of LoadScriptFile(). Note, the rules for how the cascading config works are under scrutiny and may be changed, see #141.

You may find that using #load is good enough for you. In fact, I'm now considering deprecating LoadScriptFile() altogether and relying on #load instead as it's more intuitive once you understand how #load works. I'd probably have to override the handling of #load to allow it work with HTTP endpoints to replace LoadWebScript() but that's easy enough to do.

As an aside, I'm also considering deprecating Add() in favour of Set() (just a name change) as I think it conveys the semantics better. Each call to Add() does not in fact add the item, it just sets the value of that item, overriding it if previously set.

Donations are always gladly accepted! That's very kind of you. My PayPal account is registered under adam@adamralph.com. Is that all the info you need for it?

Adam - thanks for the clarification on #load and LoadScriptFile() - that helps conceptualize ConfigR options; I think both functionalities have merit and the afforded flexibility is valuable. However, I re-read your explanation twice and still have some confusion with the gist I linked: http://goo.gl/lDFiSC .

"Overriding" a var from a .csx using #load - the value does not have to once again be Added (set) in order for a subsequent Get to reflect the overriding changes - this would appears to render Add() moot in this circumstance.

Am I misinterpreting this? Feel free to flame me if I'm completely wrong ๐Ÿ˜‰

Best,
Reid

PS - sent paypal this AM - let me know if you haven't received it

When you add the object to config, ConfigR doesn't take a copy of the object. It's still the same object, so if you maintain a reference to it, as you do in your foo variable, and then change a property, you are changing the same object that is stored in the config.

I recieved the payment, thank you very much! It will be put to good use tonight when I go and watch the England game ๐Ÿ˜‰. In fact, it did inspire me to register the configr.org domain, so when I eventually get a website up and point that domain at it, it will be the result of your donation. Thanks again.

@TheFastCat are you happy with the results of the conversation above? Do you feel your questions have been answered?

Yes thanks !- Configr has been a part of our componentized OWIN framework for a while now.

Excellent. I'm really glad it's working out so well for you!