1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
rails--rails/actionpack/lib/action_controller/metal/helpers.rb

123 lines
4.3 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
module ActionController
# The \Rails framework provides a large number of helpers for working with assets, dates, forms,
# numbers and model objects, to name a few. These helpers are available to all templates
# by default.
#
# In addition to using the standard template helpers provided, creating custom helpers to
2011-09-03 15:13:40 -04:00
# extract complicated logic or reusable functionality is strongly encouraged. By default, each controller
# will include all helpers. These helpers are only accessible on the controller through <tt>#helpers</tt>
2011-09-03 15:13:40 -04:00
#
# In previous versions of \Rails the controller will include a helper which
# matches the name of the controller, e.g., <tt>MyController</tt> will automatically
2011-09-03 15:13:40 -04:00
# include <tt>MyHelper</tt>. To return old behavior set +config.action_controller.include_all_helpers+ to +false+.
#
# Additional helpers can be specified using the +helper+ class method in ActionController::Base or any
# controller which inherits from it.
#
# The +to_s+ method from the \Time class can be wrapped in a helper method to display a custom message if
# a \Time object is blank:
#
# module FormattedTimeHelper
# def format_time(time, format=:long, blank_message="&nbsp;")
# time.blank? ? blank_message : time.to_s(format)
# end
# end
#
# FormattedTimeHelper can now be included in a controller, using the +helper+ class method:
#
# class EventsController < ActionController::Base
# helper FormattedTimeHelper
# def index
2011-07-07 12:12:09 -04:00
# @events = Event.all
# end
# end
#
2019-03-12 11:52:09 -04:00
# Then, in any view rendered by <tt>EventsController</tt>, the <tt>format_time</tt> method can be called:
#
# <% @events.each do |event| -%>
# <p>
2010-04-05 05:51:26 -04:00
# <%= format_time(event.time, :short, "N/A") %> | <%= event.name %>
# </p>
# <% end -%>
#
# Finally, assuming we have two event instances, one which has a time and one which does not,
# the output might look like this:
#
# 23 Aug 11:30 | Carolina Railhawks Soccer Match
# N/A | Carolina Railhawks Training Workshop
#
module Helpers
extend ActiveSupport::Concern
class << self; attr_accessor :helpers_path; end
include AbstractController::Helpers
included do
class_attribute :helpers_path, default: []
class_attribute :include_all_helpers, default: true
end
module ClassMethods
# Declares helper accessors for controller attributes. For example, the
# following adds new +name+ and <tt>name=</tt> instance methods to a
# controller and makes them available to the view:
# attr_accessor :name
# helper_attr :name
#
# ==== Parameters
# * <tt>attrs</tt> - Names of attributes to be converted into helpers.
def helper_attr(*attrs)
attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
end
2016-05-07 11:15:23 -04:00
# Provides a proxy to access helper methods from outside the view.
def helpers
Freeze string literals when not mutated. I wrote a utility that helps find areas where you could optimize your program using a frozen string instead of a string literal, it's called [let_it_go](https://github.com/schneems/let_it_go). After going through the output and adding `.freeze` I was able to eliminate the creation of 1,114 string objects on EVERY request to [codetriage](codetriage.com). How does this impact execution? To look at memory: ```ruby require 'get_process_mem' mem = GetProcessMem.new GC.start GC.disable 1_114.times { " " } before = mem.mb after = mem.mb GC.enable puts "Diff: #{after - before} mb" ``` Creating 1,114 string objects results in `Diff: 0.03125 mb` of RAM allocated on every request. Or 1mb every 32 requests. To look at raw speed: ```ruby require 'benchmark/ips' number_of_objects_reduced = 1_114 Benchmark.ips do |x| x.report("freeze") { number_of_objects_reduced.times { " ".freeze } } x.report("no-freeze") { number_of_objects_reduced.times { " " } } end ``` We get the results ``` Calculating ------------------------------------- freeze 1.428k i/100ms no-freeze 609.000 i/100ms ------------------------------------------------- freeze 14.363k (± 8.5%) i/s - 71.400k no-freeze 6.084k (± 8.1%) i/s - 30.450k ``` Now we can do some maths: ```ruby ips = 6_226k # iterations / 1 second call_time_before = 1.0 / ips # seconds per iteration ips = 15_254 # iterations / 1 second call_time_after = 1.0 / ips # seconds per iteration diff = call_time_before - call_time_after number_of_objects_reduced * diff * 100 # => 0.4530373333993266 miliseconds saved per request ``` So we're shaving off 1 second of execution time for every 220 requests. Is this going to be an insane speed boost to any Rails app: nope. Should we merge it: yep. p.s. If you know of a method call that doesn't modify a string input such as [String#gsub](https://github.com/schneems/let_it_go/blob/b0e2da69f0cca87ab581022baa43291cdf48638c/lib/let_it_go/core_ext/string.rb#L37) please [give me a pull request to the appropriate file](https://github.com/schneems/let_it_go/blob/b0e2da69f0cca87ab581022baa43291cdf48638c/lib/let_it_go/core_ext/string.rb#L37), or open an issue in LetItGo so we can track and freeze more strings. Keep those strings Frozen ![](https://www.dropbox.com/s/z4dj9fdsv213r4v/let-it-go.gif?dl=1)
2015-07-19 17:19:15 -04:00
@helper_proxy ||= begin
proxy = ActionView::Base.empty
proxy.config = config.inheritable_copy
proxy.extend(_helpers)
end
end
# Overwrite modules_for_helpers to accept :all as argument, which loads
# all helpers in helpers_path.
#
# ==== Parameters
# * <tt>args</tt> - A list of helpers
#
# ==== Returns
# * <tt>array</tt> - A normalized list of modules for the list of helpers provided.
def modules_for_helpers(args)
args += all_application_helpers if args.delete(:all)
super(args)
end
2015-03-03 15:27:17 -05:00
# Returns a list of helper names in a given path.
#
# ActionController::Base.all_helpers_from_path 'app/helpers'
# # => ["application", "chart", "rubygems"]
def all_helpers_from_path(path)
helpers = Array(path).flat_map do |_path|
names = Dir["#{_path}/**/*_helper.rb"].map { |file| file[_path.to_s.size + 1..-"_helper.rb".size - 1] }
names.sort!
end
helpers.uniq!
helpers
end
private
# Extract helper names from files in <tt>app/helpers/**/*_helper.rb</tt>
def all_application_helpers
all_helpers_from_path(helpers_path)
end
end
2016-05-07 11:15:23 -04:00
# Provides a proxy to access helper methods from outside the view.
def helpers
@_helper_proxy ||= view_context
end
end
end