2. Variables and Contexts

Orthogonal to the module/class/method request routing mechanism are the concepts of configuration contexts and variables. ModRuby allows you to define your own custom configuration variables and Apache allows you to merge and override variables within various scopes (or contexts) within the configuration file. In this section we will explore these ideas and their implications.

2.1. Configuration Contexts

In Apache terms, there are basically two configuration contexts: server configuration and directory configuration. That is, you can specify configuration variables in server (global) scope as well as specifically within directory (local) scope. What this amounts to is that server scope is sort of a global default: if nothing else is specified, this is the information the ModRuby module will use to route requests (module/class/method). But if there are directory configurations specified, they will override and or complement the server scope.

Consider the following example:

<IfModule ruby_module>

# Define a sheepdip handler
RubyHandlerDeclare SHEEPDIP
RubyHandlerModule  SHEEPDIP sheepdip/handler
RubyHandlerClass   SHEEPDIP Sheepdip::Handler
RubyHandlerMethod  SHEEPDIP handle

# Define a jujyfruit handler
RubyHandlerDeclare JUJIFRUIT
RubyHandlerModule  JUJIFRUIT jujifruit
RubyHandlerClass   JUJIFRUIT JujiFruit::Coolness
RubyHandlerMethod  JUJIFRUIT handle

# Ruby files go to framework handler
AddHandler ruby-handler .rb

# By default, the SHEEPDIP framework handler should to be used
# as the framework handler matches
RubyHandler SHEEPDIP

# But for requests in the /fruity directory
<Location /fruity>
    RubyHandler JUJIFRUIT
</Location>

</IfModule>

We have defined two framework handlers: SHEEPDIP and JUJIFRUIT. Further, we’ve made it so that if a request comes in for any file with a .rb extension, the ruby-handler framework handler will be called to service the request. Further, when this handler runs, it will by default route all requests to the SHEEPDIP handler as the RubyHandler has been set to use it in the server (global) scope. So for example a request for /jujifruit.rb will be handled by the SHEEPDIP handler (regardless of its name to the contrary).

However, if a request for /fruity/sheepdip.rb comes in, the JUJIFRUIT handler will be used to handle the request. This is because the Location block — which is considered as directory scope — overrides global scope. So its RubyHandler directive overrides the global one.

Basically all we are doing here is fixing phase 1 to point to the framework handler and then messing around with phase 2 — specifying what framework handlers are fired. So we know that no matter what, we’ve fixed the ruby-handler to fire for any request for files that have a .rb extension. The point is that the rules for defining phase 2 can be layered into different scopes: a global default which can then be overridden by directory level settings.

Directory and Variable Merging

The only thing you need to be careful about when configuing Directory, Location, and File blocks is the order in which they are processed. This is described in detail here.

2.2. Configuration Variables

ModRuby provides a way for you to store you own configuration variables. You use the RubyConfig directive to do so. It takes two arguments: a key and a value. It then stores it in the server (global) settings table, which you can retreive within Ruby.

For example say that you wanted to set a global configuration setting named GEM_PATH that which is accessible to all your scripts. You would simply do the following in your Apache configuration file:

<IfModule ruby_module>

RubyConfig GEM_PATH /var/lib/gems/2.4.0/

</IfModule>

Like other directives, this variable is subject to server and directory contexts. For example, say you had the following:

<IfModule ruby_module>

RubyConfig GEM_PATH /var/lib/gems/2.4.0/

# But for requests in the /fruity directory
<Location /fruity>
  RubyConfig GEM_PATH /var/lib/jujifruit/
</Location>

# But for requests in the /sheepdip directory
<Directory /var/www/mysite/sheedip>
  RubyConfig GEM_PATH /var/lib/sheepdip/
</Location>

</IfModule>

For all requests within the /fruity location, the global GEM_PATH will be overriden with the value in the /fruity Location block. The same is true for the /var/www/mysite/sheedip directory. Thus directory contexts always complement and override server contexts.

2.2.1. Configuration Variables for Handlers

As touched upon earlier, there is also a way for you to set specific configuration variables for framework handlers. For example, say you wanted to set the GEM_PATH only for the SHEEPDIP handler. You would use the RubyHandlerConfig which works the same way but takes a handler name as the first argument. For example, in this case you would have something like:

<IfModule ruby_module>

RubyHandlerConfig SHEEPDIP GEM_PATH /var/lib/gems/2.4.0/

# But for requests in the /fruity directory
<Location /fruity>
  RubyHandlerConfig SHEEPDIP GEM_PATH /var/lib/jujifruit/
</Location>

</IfModule>

And just like before, the values in directory contexts — even for handler configurations — will be merged with the server contexts, overriding them wherever there are collisions.

Now the question is: how do you get to these values? Well, internally there are two configuration sources: one for the server and one for the directory corresponding to the active request. Unfortunately, these are C structs in Apache, and you cannot easily get to them from Ruby. But as it turns out, you don’t really need to. In every request you have access to a general purpose APR table that will contain everything you need. Internally the ModRuby module will take all ModRuby directives and merge them into a single APR table which you can get to at runtime. This table is the the "notes" table which is accessible via the Apache::Request::notes() method. For every request, ModRuby automatically does the server and directory merging for you, giving you the final merged values for everything in the notes table.

So, for example, to get the value for the GEM_PATH from within Ruby, you would just do the following:

gem_path = @request.notes()['GEM_PATH']

APR tables are given full treatment in Chapter 3, Programming.

2.3. Environmental Variables

The last feature in configuration is setting environmental variables. You can do this using the RubyEnv directory which works exactly like RubyConfig. So, for example if you wanted to set your Ruby RUBYPATH environmental variable, you could do the following:

<IfModule ruby_module>

RubyEnv RUBYPATH /var/lib/gems/2.4.0/

</IfModule>

This will mody the RUBYPATH for the server environment. Unlike the other cases, there is no concept of merging here. The directive will fire whenever called and modify the environmental variables as requested. All environmental variables can be access from within Ruby via the Apache::Request::cgi() method which returns and APR table of all CGI params (which consist of environmental variables).