blog.petitviolet.net

Call Another Controller Action In Rails

2020-11-16

Rails

Sometimes we want to reuse a controller’s action in another controller for some reasons. We know redirect is an answer of that, however, there is no way to touch the response when using redirect, and it might be necessary to modify the response, such as transforming response body to another format.

To solve this, use XxxController.dispatch to call an another controller’s action.

class ItemsController < ActionController::Base
  include Rails.application.routes.url_helpers

  def show
    request.params[:item_id] = request.params[:id]
    res = AnotherController.dispatch(:get, request, response)
    render json: { msg: "OK", body: JSON.parse(res[2].body) }, status: 200
  end
end

class AnotherController < ActionController::Base
  include Rails.application.routes.url_helpers

  def get
    item = {
      "1" => "hello",
      "2" => "world",
    }[params[:item_id]]

    render json: { item: item }, status: 200
  end
end

You can get AnotherController#get results via ItemsController#show like

$ curl "localhost:3000/items/1" | jq .
{
  "body": {
    "item": "hello"
  },
  "msg": "OK"
}

Furthermore, if you want to get instance variables that are assigned in the dispatched controller, using #dispatch instead of .dispatch will be a way to do it.

+   ac = AnotherController.new
-   res = AnotherController.dispatch(:get, request, response)
+   res = ac.dispatch(:get, request, response)
+   instance_var = ac.instance_variable_get(:@whatever_you_want)

Reproducible code is available in gist.

https://gist.github.com/petitviolet/1f7965e2c3624f475282d71df6b36c58