Last Updated: February 25, 2016
·
1.149K
· miguelff

Dynamically define and mount rails controller once the application has started

# Install controller action in the given route
# implementing the behavior in the block provided
#
# install_controller! 'auth/callback' do
#   render text: params[:code]
# end
#
# the former is equivalent to mounting in 'auth/callback' 
# route the following controller:
#
# class AuthController < ApplicationController
#   def callback
#     render text: params[:code]
#   end
# end
#
# To avoid collisions, you can also specify the controller 
# and action to use:
#
# install_controller! 'auth/callback', 'sessions#inspect' do
#   sessions[:code] = params[:code]
#   head :ok
# end
#
# Which will be equivalent as mounting in the 'auth/callback' 
# route this controller:
#
# class SessionsController < ApplicationController
#   def inspect
#     sessions[:code] = params[:code]
#     head :ok
#   end
# end
#
def install_controller!(route, controller_action = nil, &b)
  controller_action ||= route.sub('/','#')
  controller, action = controller_action.split('#')

  klass = Class.new(ApplicationController) do
    define_method action.to_sym, &b
  end

  self.class.const_set "#{controller.classify}Controller", klass

  Rails.application.routes.tap do |routes|
    begin
      routes.disable_clear_and_finalize = true
      routes.clear!
      Rails.application.routes_reloader.paths.each { |path| load(path) }
      routes.draw do
        match route, to: controller_action, via: [:get, :post]
      end
      ActiveSupport.on_load(:action_controller) { routes.finalize! }
    ensure
      routes.disable_clear_and_finalize = false
    end
  end
end