petitviolet blog

    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