1

According to the Pragmatic Programmers, code should be for abstractions, while configuration and details should live in metadata. Rails seems to diverge dramatically from this pattern. Much config is done in code files such as application.rb, routes.rb and the various environment.rb files, while other types of config is done in yaml files (e.g. database.yml). What is the thinking behind this design decision?

3 Answers 3

6

We get a different picture when we read the whole page you linked to, and not just the headline.

program for the general case, and put the specifics somewhere else - outside the compiled code base.

We don't have a compiled code base, my Ruby is interpreted. There is no recompilation step, changing the configuration is as easy as changing the source file and restarting the program.

There are several benefits to this approach:

  • It forces you to decouple your design, which results in a more flexible and adaptable program.

The RoR framework is already sufficiently decoupled.

  • It forces you to create a more robust, abstract design by deferring details—deferring them all the way out of the program.

Parts of the implementation may also be details that should be deferred out of the main program. In dynamic languages, there simply isn't this great dichotomy between code and data – code is just another kind of data. This has been shown with great success by the Lisp languages, and Ruby stands firmly in this Lispish tradition via its Smalltalk heritage.

  • You can customize the application without recompiling it. You can also use this level of customization to provide easy work-arounds for critical bugs in live production systems.

Again, no recompiling is necessary with Ruby (or other dynamic languages like Python or Perl). In fact, we can monkey-patch new code into the system while it's still running.

  • Metadata can be expressed in a manner that's much closer to the problem domain than a general-purpose programming language might be (see Domain Languages).

Ruby's syntax is flexible enough that it's easy to whip up half a DSL with it. In fact, the Ruby implementation gives us a parser and compiler for free, all we have to do is to implement the semantics.

  • You may even be able to implement several different projects using the same application engine, but with different metadata.

Exactly. The RoR framework takes application-specific code from your configuration files. It's easy to swap out these files for another website.

4

In Rails those boundaries always have been a bit blurred. There are several reasons for this, the first one maybe the simple fact that the Ruby language allows it to write code that looks like configurations. For example you can use methods without putting the arguments in braces, which leads to the common usage of things like this:

has_many :orders

in the model definitions. Looks like a configuration, actually is a method call with the param :orders.

Starting from this point it is often easier to chain method calls for complex configuration options. This especially in files like routes.rb. Also this allows for very dynamic configurations (say you want to use regular expressions in your routing). Also the routing can in general be a much more dynamic thing, so having code that's executed is often more viable.

Actually in many places those .rb files do not generate 'static' configurations, but move anonymous functions in place that later get called, eg when a route has to be evaluated.

In case of the very basic files like environment.rb and application.rb it is a bit misleading to call them configuration files. They are more a kind of anchor points where you can add require calls to be executed while the Rails system is bootstrapping its class hierarchy.

So there is a mix of reasons active here, some simply based on the 'Ruby culture', others for practical purposes. (Not every single one necessarily the best decision, but most of the time they make sense once you see the advanced possibilities you get from them).

0

Executable configs are a tradeoff between expressibility and safety/predictability.

Using a turing-complete programming language for configuration gives you many useful features "for free": Variables make let you avoid writing a same value over and over, loops avoid repeating yourself, if statements do conditionals, etc.

The disadvantage of using an executable config or a config-generator is that you can't know what the config will do without executing it. Will it execute quickly or will it get stuck in an infinite loop? What properties will it set in the end? There are also security issues it you have user-created configs - how do you sandbox the executino environment? Can they DDOS you by consuming too many resoutrces and so on...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.