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.
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.
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.
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.
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.
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).