ruby-on-railsrubyactioncontrolleractiondispatch

Dispatch Rails Controller Action Outside of HTTP context (not in a unit test)


I maintain a mature codebase that uses Rails as an API. The Rails app also does some things with MQTT, a realtime, non-web, non-HTTP protocol.

Some users have requested the ability to simulate the same REST commands over the MQTT protocol (avoids the need to auth twice and have two different protocol clients).

I have seen some projects, such as Facebook's API "simulate" HTTP by putting parts of the HTTP request into a request using JSON (eg: have a "header" and "method" property on a JSON payload). This is precisely what I want to do but outside of the HTTP context entirely. It will occur in a background worker that handles MQTT messages.

Is it possible to use ActionDispatch and friends to simulate a controller dispatch in isolation?

I'm having a hard time finding documentation on how to instantiate an ActionDispatch::Request object in isolation outside of RSpec.

MyController.dispatch("create",
                     ActionDispatch::Request.new({"???" => "Can't find any docs on this one."}),
                     ActionDispatch::Response.new("?"))
# => RuntimeError: Missing rack.input

Solution

  • Ow very interesting problem!

    My first idea was to approach it backwards: so create a fake Rack::Request or ActionDispatch::Request and then force it to be handled? Assuming it would go to the right controller and action automatically (so you do not have to call it explicitly). But of course the problem remains: how to create the Request :)

    So I was browsing the rails tests and I found the following snippet which might get you started:

    SimpleController.action("hello").call(
      "REQUEST_METHOD" => "GET",
      "rack.input" => -> {}
    )
    

    Source: https://github.com/rails/rails/blob/85fcb663363cd27220e8bd3136973cc3408cf7d7/actionpack/test/controller/metal_test.rb

    From my understanding this means you can give a entire fake request environment as a hash with keys being the strings, right? So you could dump the env from a valid request and transpose accordingly.

    Not sure if this is really helpful.