Advanced Rails - Building Industrial-Strength Web Apps in Record Time

(Tuis.) #1

44 | Chapter 1: Foundational Techniques


Because of the speed needed in both generation and recognition, the routing code
modifies itself at runtime. TheActionController::Routing::Routeclass represents a
single route (one entry inconfig/routes.rb). TheRoute#recognizemethod rewrites
itself:


class Route
def recognize(path, environment={})
write_recognition
recognize path, environment
end
end

Therecognizemethod callswrite_recognition, which processes the route logic and
creates a compiled version of the route. Thewrite_recognitionmethod then over-
writes the definition ofrecognizewith that definition. The last line in the original
recognizemethod then callsrecognize(which has been replaced by the compiled
version) with the original arguments. This way, the route is compiled on the first call
torecognize. Any subsequent calls use the compiled version, rather than having to
reparse the routing DSL and go through the routing logic again.


Here is the body of thewrite_recognition method:


def write_recognition
# Create an if structure to extract the params from a match if it occurs.
body = "params = parameter_shell.dup\n#{recognition_extraction * "\n"}\nparams"
body = "if #{recognition_conditions.join(" && ")}\n#{body}\nend"

# Build the method declaration and compile it
method_decl = "def recognize(path, env={})\n#{body}\nend"
instance_eval method_decl, "generated code (#{__FILE_ _}:#{_ _LINE_ _})"
method_decl
end

The local variablebodyis built up with the compiled route code. It is wrapped in a
method declaration that overwritesrecognize. For the default route:


map.connect ':controller/:action/:id'

write_recognition generates code looking like this:


def recognize(path, env={})
if (match = /(long regex)/.match(path))
params = parameter_shell.dup
params[:controller] = match[1].downcase if match[1]
params[:action] = match[2] || "index"
params[:id] = match[3] if match[3]
params
end
end
Free download pdf