Is there a way I could pass an argument to a controller action call from middleware?
This is the action in controller code
# in my_controller.rb
#
def print_name(name)
render :text => "Hello, #{name}!"
end
Here’s the code from my middleware that calls this action
# in middleware
#
def call(env)
env['action_controller.instance'].class.action(:print_name).call(env)
end
This of course raises ArgumentError.
I’m unaware how can I pass an argument to action call. Here’s the source:
# in action_pack/lib/action_controller/metal.rb
#
def self.action(name, klass = ActionDispatch::Request)
middleware_stack.build(name.to_s) do |env|
new.dispatch(name, klass.new(env))
end
end
As you can see, it returns a rack endpoint from provided controller action. I see no way I could pass an argument here.
I ended up changing the controller dynamically with class_eval and then calling the controller method from that proxy method.
# in middleware
#
def define_proxy_method(klass)
klass.class_eval do
def call_the_real_method
print_name(request.env['fake_attribute'])
end
end
end
def call(env)
define_proxy_method(env['action_controller.instance'].class)
env['fake_attrbute'] = "Yukihiro"
env['action_controller.instance'].class.action(:call_the_real_method).call(env)
end
This seems dirty and I would like to know of a better way. Thanks.
You don’t want to put arguments on your action. Middleware should only interact with the request environment, not try to call into the controller directly.
You can pass the same value in the params hash:
and read from the params hash in the controller: