How to Access the Rack Environment from Within Rails

How do I access the Rack environment from within Rails?

I'm pretty sure you can use the Rack::Request object for passing request-scope variables:

# middleware:
def call(env)
request = Rack::Request.new(env) # no matter how many times you do 'new' you always get the same object
request[:foo] = 'bar'
@app.call(env)
end

# Controller:
def index
if params[:foo] == 'bar'
...
end
end

Alternatively, you can get at that "env" object directly:

# middleware:
def call(env)
env['foo'] = 'bar'
@app.call(env)
end

# controller:
def index
if request.env['foo'] == 'bar'
...
end
end

What is the env variable in Rack middleware?

env is just a hash. Rack itself and various middlewares add values into it.

To understand what the various keys are in the hash, check out the Rack Specification.

And here is a sample env hash:

{
"GATEWAY_INTERFACE" => "CGI/1.1",
"PATH_INFO" => "/index.html",
"QUERY_STRING" => "",
"REMOTE_ADDR" => "::1",
"REMOTE_HOST" => "localhost",
"REQUEST_METHOD" => "GET",
"REQUEST_URI" => "http://localhost:3000/index.html",
"SCRIPT_NAME" => "",
"SERVER_NAME" => "localhost",
"SERVER_PORT" => "3000",
"SERVER_PROTOCOL" => "HTTP/1.1",
"SERVER_SOFTWARE" => "WEBrick/1.3.1 (Ruby/2.0.0/2013-11-22)",
"HTTP_HOST" => "localhost:3000",
"HTTP_USER_AGENT" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0",
"HTTP_ACCEPT" => "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"HTTP_ACCEPT_LANGUAGE" => "zh-tw,zh;q=0.8,en-us;q=0.5,en;q=0.3",
"HTTP_ACCEPT_ENCODING" => "gzip, deflate",
"HTTP_COOKIE" => "jsonrpc.session=3iqp3ydRwFyqjcfO0GT2bzUh.bacc2786c7a81df0d0e950bec8fa1a9b1ba0bb61",
"HTTP_CONNECTION" => "keep-alive",
"HTTP_CACHE_CONTROL" => "max-age=0",
"rack.version" => [1, 2],
"rack.input" => #<StringIO:0x007fa1bce039f8>,
"rack.errors" => #<IO:<STDERR>>,
"rack.multithread" => true,
"rack.multiprocess" => false,
"rack.run_once" => false,
"rack.url_scheme" => "http",
"HTTP_VERSION" => "HTTP/1.1",
"REQUEST_PATH" => "/index.html"
}

And to make it easier to use, checkout Rack::Request which makes it easier to access the values inside the env hash.

Ruby variable scope: access rack.env from inside an existing ruby class?

Personally, I think having a variable that changes like that inside a class method is asking for trouble, it breaks the Law of Demeter by reaching across boundaries. Instead, I'd wrap it in a Sinatra helper which then passes the second argument by default.

helpers do
def find( s )
Repository.find( s, env['org'] )
end
end

and modify the Repository's find method to take the second argument.

How to access specific instances of the Rack middlewares?

You could have the middleware add itself to the rack environment, as in this example:

require 'rack'

class MyMiddleware
attr_accessor :add_response
def initialize app
@app = app
end
def call env
env['my_middleware'] = self # <-- Add self to the rack environment
response = @app.call(env)
response.last << @add_response
response
end
end

class MyApp
def call env
env['my_middleware'].add_response = 'World!' # <-- Access the middleware instance in the app
[200, {'Content-Type'=>'text/plain'}, ['Hello']]
end
end

use MyMiddleware
run MyApp.new

does a ruby on rails rack class get access to the entire rails environment?

It appears from http://www.ruby-on-rails-outsourcing.com/articles/2010/05/28/how-to-create-your-own-rack-middleware-class-in-ruby-on-rails/ that "env" is part of what rack "uses" for its requests, so you can't get around that (but I guess you can use it to avoid running the full rails stack, if you so desire).

There was once a "metal" option in rails 2 that basically avoided all loading. Apparently it was removed in rails 3. http://tektastic.com/2010/07/rails3-rack-and-where-did-my-metal-go.html you have to use a rack middleware instead. I'm unsure if this causes much performance difference (having to use rack) or not.

Can I get the base URL of my Rack service outside of a request handler?

I don't believe it's possible for a Rack application can know ahead of time the "mount point". For example, this config.ru mounts the same app at multiple mount points:

require 'rack'

app = proc { |env|
[200, {'Content-Type' => 'text/plain'}, ['hello, world!']]
}

run Rack::URLMap.new('/myapp' => app,
'/' => app)

Rack also does not provide any standard method that is called at initialization time. I suspect this is because Rack tries to support plain CGI, where a whole Ruby process may be created to handle each request, with the process exiting at each request. In that situation, there isn't much use for an "init" method.

Rails environment variables with Rack-Mini-Profiler

Miniprofiler does not actually have do anything since Ruby exposes the environmental variables through the ENV constant which is a hash like class.

dump_env just iterates over them:

body << "\n\nEnvironment\n---------------\n"
ENV.each do |k, v|
body << "#{k}: #{v}\n"
end


Related Topics



Leave a reply



Submit