12

I am using Ruby on Rails 3 and I am trying to use middlewares in order to set a variable @variable_name accessible later in controllers.

For example my middleware is

  class Auth

    def initialize(app)
      @app = app
    end

    def call(env)
      @account ||= Account.find(1)

      @app.call(env)
    end
  end

The above code set properly the @account variable, but that isn't available in my application (in controllers, models, views, ...). So, how can I accomplish that?


I seen this answer that is a way to do what I need, but I would like to have the @account variable "directly accessible". That is, without use that way but making that available, for example in my views, like this:

<%= debug @account %>
Community
  • 1
  • 1
user502052
  • 14,803
  • 30
  • 109
  • 188

5 Answers5

25

You can use 'env' for that. So in your middleware you do this:

def call(env)
  env['account'] = Account.find(1)
  @app.call(env)
end

You can get the value by using 'request' in your app:

request.env['account']

And please don't use global variables or class attributes as some people suggest here. That's a sure way to get yourself into troubles and really is a bad habit.

wanderfalke
  • 878
  • 8
  • 8
  • Thanks for a good answer. After your answer I instantly remembered the omniauth and it use the same practice. Also I noticed that you can do the same thing from the routes constraint (it's especially helpful to low down number of requests to the database in my case)... you can pass any value to the request.env and catch it inside the action or controller. – Dmitry Polushkin Aug 10 '11 at 12:24
2

I don't know if this can be done with a Middelware. My suggestion would be this:

class ApplicationController < ActionController::Base

  protect_from_forgery

  before_filter :set_my_var

private
  def set_my_var
    @account ||= Account.find(1)
  end

end

This way all your controllers and views have access to @account

Wukerplank
  • 4,156
  • 2
  • 28
  • 45
  • A problem with this approach is when you are calculating something in the middleware itself for assignment. – Ashish Mar 04 '11 at 10:14
0

Rails 5.2 added ActiveSupport::CurrentAttributes for this kind of globally relevant request context data. You inherit from it to create a singleton class accessible from anywhere, which is automatically reset between requests. So now you can do something like:

class RequestContext < ActiveSupport::CurrentAttributes
  attribute :account
end

and then...

class Auth
  ...
  def call(env)
    RequestContext.account ||= Account.find(1)

    @app.call(env)
  end
end

I've never used it from a middleware - you may have to experiment with when the CurrentAttributes instance gets reset for each request (somewhere in the middleware stack? after the middleware stack?)

mltsy
  • 6,598
  • 3
  • 38
  • 51
-4

You can have a cattr_accessor :my_var in any model and set this variable from middleware by

  MyModel.my_var = 'something'

And you can access this anywhere in the application.

Ashish
  • 5,723
  • 2
  • 24
  • 25
-6

Have you tried creating a Ruby global variable?

def call(env)
  $account ||= Account.find(1)

  @app.call(env)
end

and

<%= debug $account %>