mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
e58b2769cf
* The helper module adds a new _helper_serial property onto AbstractController subclasses * When #helper is used to add helpers to a class, the serial number is updated * An ActionView subclass is created and cached based on this serial number. * That subclass includes the helper module from the controller * Subsequent requests using the same controller with the same serial will result in reusing that subclass, rather than being forced to take an action (like include or extend) that will result in a global method cache flush on MRI and a flush of the entire AV class' cache on JRuby. * For now, this optimization is not applied to the RJS helpers, which results in a global method cache flush in MRI and a flush of the JavaScriptGenerator class in JRuby only when using RJS. * Since the effects are limited to using RJS, and would only affect JavaScriptGenerator in JRuby (as opposed to the entire view object), it seems worthwhile to apply this now. * This resulted in a significant performance improvement. I will have benchmarks in the next day or two that show the performance impact of the last several commits. * There is a small chance this could break existing code (although I'm not sure how). If that happens, please report it immediately.
92 lines
2.7 KiB
Ruby
92 lines
2.7 KiB
Ruby
module AbstractController
|
|
module Helpers
|
|
extend ActiveSupport::Concern
|
|
|
|
include RenderingController
|
|
|
|
def self.next_serial
|
|
@helper_serial ||= 0
|
|
@helper_serial += 1
|
|
end
|
|
|
|
included do
|
|
extlib_inheritable_accessor(:_helpers) { Module.new }
|
|
extlib_inheritable_accessor(:_helper_serial) do
|
|
AbstractController::Helpers.next_serial
|
|
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 }
|
|
|
|
super
|
|
end
|
|
|
|
# Declare a controller method as a helper. For example, the following
|
|
# makes the +current_user+ controller method 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
|
|
# meths<Array[#to_s]>:: The name of a method on the controller
|
|
# to be made available on the view.
|
|
def helper_method(*meths)
|
|
meths.flatten.each do |meth|
|
|
_helpers.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
|
|
def #{meth}(*args, &blk)
|
|
controller.send(%(#{meth}), *args, &blk)
|
|
end
|
|
ruby_eval
|
|
end
|
|
end
|
|
|
|
# Make a number of helper modules part of this class' default
|
|
# helpers.
|
|
#
|
|
# ==== Parameters
|
|
# *args<Array[Module]>:: Modules to be included
|
|
# block<Block>:: Evalulate the block in the context
|
|
# of the helper module. Any methods defined in the block
|
|
# will be helpers.
|
|
def helper(*args, &block)
|
|
self._helper_serial = AbstractController::Helpers.next_serial + 1
|
|
|
|
args.flatten.each do |arg|
|
|
case arg
|
|
when Module
|
|
add_template_helper(arg)
|
|
end
|
|
end
|
|
_helpers.module_eval(&block) if block_given?
|
|
end
|
|
|
|
private
|
|
# Makes all the (instance) methods in the helper module available to templates
|
|
# rendered through this controller.
|
|
#
|
|
# ==== Parameters
|
|
# mod<Module>:: The module to include into the current helper module
|
|
# for the class
|
|
def add_template_helper(mod)
|
|
_helpers.module_eval { include mod }
|
|
end
|
|
end
|
|
end
|
|
end
|