text: require 'active_support/dependencies'
  
  module AbstractController
    module Helpers
      extend ActiveSupport::Concern
  
      included do
        class_attribute :_helpers
        self._helpers = Module.new
  
        class_attribute :_helper_methods
        self._helper_methods = Array.new
      end
  
      class MissingHelperError < LoadError
        def initialize(error, path)
          @error = error
          @path  = "helpers/#{path}.rb"
          set_backtrace error.backtrace
  
          if error.path =~ /^#{path}(\.rb)?$/
            super("Missing helper file helpers/%s.rb" % path)
          else
            raise error
          end
        end
      end
  
      module ClassMethods
        # When a class is inherited, wrap its helper module in a new module.
        # This ensures that the parent class's module can be changed
        # independently of the child class's.
        def inherited(klass)
          helpers = _helpers
          klass._helpers = Module.new { include helpers }
          klass.class_eval { default_helper_module! } unless klass.anonymous?
          super
        end
  
        # Declare a controller method as a helper. For example, the following
        # makes the +current_user+ and +logged_in?+ controller methods available
        # to the view:
        #   class ApplicationController < ActionController::Base
        #     helper_method :current_user, :logged_in?
        #
        #     def current_user
        #       @current_user ||= User.find_by(id: session[:user])
        #     end
        #
        #     def logged_in?
        #       current_user != nil
        #     end
        #   end
        #
        # In a view:
        #  <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%>
        #
        # ==== Parameters
        # * <tt>method[, method]</tt> - A name or names of a method on the controller
        #   to be made available on the view.
        def helper_method(*meths)
          meths.flatten!
          self._helper_methods += meths
  
          meths.each do |meth|
            _helpers.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
              def #{meth}(*args, &blk)                               # def current_user(*args, &blk)
                controller.send(%(#{meth}), *args, &blk)             #   controller.send(:current_user, *args, &blk)
              end                                                    # end
            ruby_eval
          end
        end
  
        # The +helper+ class method can take a series of helper module names, a block, or both.
        #
        # ==== Options
        # * <tt>*args</tt> - Module, Symbol, String
        # * <tt>block</tt> - A block defining helper methods
        #
        # When the argument is a module it will be included directly in the template class.
        #   helper FooHelper # => includes FooHelper
        #
        # When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file
        # and include the module in the template class. The second form illustrates how to include custom helpers
        # when working with namespaced controllers, or other cases where the file containing the helper definition is not
        # in one of Rails' standard load paths:
        #   helper :foo             # => requires 'foo_helper' and includes FooHelper
        #   helper 'resources/foo'  # => requires 'resources/foo_helper' and includes Resources::FooHelper
        #
        # Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available
        # to the template.
        #
        #   # One line
        #   helper { def hello() "Hello, world!" end }
        #
        #   # Multi-line
        #   helper do
        #     def foo(bar)
        #       "#{bar} is the very best"
        #     end
        #   end
        #
        # Finally, all the above styles can be mixed together, and the +helper+ method can be invoked with a mix of
        # +symbols+, +strings+, +modules+ and blocks.
        #
        #   helper(:three, BlindHelper) { def mice() 'mice' end }
        #
        def helper(*args, &block)
          modules_for_helpers(args).each do |mod|
            add_template_helper(mod)
          end
  
          _helpers.module_eval(&block) if block_given?
        end
  
        # Clears up all existing helpers in this class, only keeping the helper
        # with the same name as this class.
        def clear_helpers
          inherited_helper_methods = _helper_methods
          self._helpers = Module.new
          self._helper_methods = Array.new
  
          inherited_helper_methods.each { |meth| helper_method meth }
          default_helper_module! unless anonymous?
        end
  
        # Returns a list of modules, normalized from the acceptable kinds of
        # helpers with the following behavior:
        #
        # String or Symbol:: :FooBar or "FooBar" becomes "foo_bar_helper",
        # and "foo_bar_helper.rb" is loaded using require_dependency.
        #
        # Module:: No further processing
        #
        # After loading the appropriate files, the corresponding modules
        # are returned.
        #
        # ==== Parameters
        # * <tt>args</tt> - An array of helpers
        #
        # ==== Returns
        # * <tt>Array</tt> - A normalized list of modules for the list of
        #   helpers provided.
        def modules_for_helpers(args)
          args.flatten.map! do |arg|
            case arg
            when String, Symbol
              file_name = "#{arg.to_s.underscore}_helper"
              begin
                require_dependency(file_name)
              rescue LoadError => e
                raise AbstractController::Helpers::MissingHelperError.new(e, file_name)
              end
  
              mod_name = file_name.camelize
              begin
                mod_name.constantize
              rescue LoadError
                # dependencies.rb gives a similar error message but its wording is
                # not as clear because it mentions autoloading. To the user all it
                # matters is that a helper module couldn't be loaded, autoloading
                # is an internal mechanism that should not leak.
                raise NameError, "Couldn't find #{mod_name}, expected it to be defined in helpers/#{file_name}.rb"
              end
            when Module
              arg
            else
              raise ArgumentError, "helper must be a String, Symbol, or Module"
            end
          end
        end
  
        private
        # Makes all the (instance) methods in the helper module available to templates
        # rendered through this controller.
        #
        # ==== Parameters
        # * <tt>module</tt> - The module to include into the current helper module
        #   for the class
        def add_template_helper(mod)
          _helpers.module_eval { include mod }
        end
  
        def default_helper_module!
          module_name = name.sub(/Controller$/, ''.freeze)
          module_path = module_name.underscore
          helper module_path
        rescue LoadError => e
          raise e unless e.is_missing? "helpers/#{module_path}_helper"
        rescue NameError => e
          raise e unless e.missing_name? "#{module_name}Helper"
        end
      end
    end
  end
  