All Files (53.43% covered at 537.76 hits/line)
62 files in total.
2098 relevant lines.
1121 lines covered and
977 lines missed
#--
# Copyright (c) 2005-2017 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
- 1
require "securerandom"
- 1
require "active_support/dependencies/autoload"
- 1
require "active_support/version"
- 1
require "active_support/logger"
- 1
require "active_support/lazy_load_hooks"
- 1
require "active_support/core_ext/date_and_time/compatibility"
- 1
module ActiveSupport
- 1
extend ActiveSupport::Autoload
- 1
autoload :Concern
- 1
autoload :Dependencies
- 1
autoload :DescendantsTracker
- 1
autoload :ExecutionWrapper
- 1
autoload :Executor
- 1
autoload :FileUpdateChecker
- 1
autoload :EventedFileUpdateChecker
- 1
autoload :LogSubscriber
- 1
autoload :Notifications
- 1
autoload :Reloader
- 1
eager_autoload do
- 1
autoload :BacktraceCleaner
- 1
autoload :ProxyObject
- 1
autoload :Benchmarkable
- 1
autoload :Cache
- 1
autoload :Callbacks
- 1
autoload :Configurable
- 1
autoload :Deprecation
- 1
autoload :Gzip
- 1
autoload :Inflector
- 1
autoload :JSON
- 1
autoload :KeyGenerator
- 1
autoload :MessageEncryptor
- 1
autoload :MessageVerifier
- 1
autoload :Multibyte
- 1
autoload :NumberHelper
- 1
autoload :OptionMerger
- 1
autoload :OrderedHash
- 1
autoload :OrderedOptions
- 1
autoload :StringInquirer
- 1
autoload :TaggedLogging
- 1
autoload :XmlMini
- 1
autoload :ArrayInquirer
end
- 1
autoload :Rescuable
- 1
autoload :SafeBuffer, "active_support/core_ext/string/output_safety"
- 1
autoload :TestCase
- 1
def self.eager_load!
super
NumberHelper.eager_load!
end
- 1
cattr_accessor :test_order # :nodoc:
- 1
def self.halt_callback_chains_on_return_false
Callbacks.halt_and_display_warning_on_return_false
end
- 1
def self.halt_callback_chains_on_return_false=(value)
Callbacks.halt_and_display_warning_on_return_false = value
end
- 1
def self.to_time_preserves_timezone
DateAndTime::Compatibility.preserve_timezone
end
- 1
def self.to_time_preserves_timezone=(value)
- 1
DateAndTime::Compatibility.preserve_timezone = value
end
end
- 1
autoload :I18n, "active_support/i18n"
- 1
require "active_support/concern"
- 1
require "active_support/descendants_tracker"
- 1
require "active_support/core_ext/array/extract_options"
- 1
require "active_support/core_ext/class/attribute"
- 1
require "active_support/core_ext/kernel/reporting"
- 1
require "active_support/core_ext/kernel/singleton_class"
- 1
require "active_support/core_ext/module/attribute_accessors"
- 1
require "active_support/core_ext/string/filters"
- 1
require "active_support/deprecation"
- 1
require "thread"
- 1
module ActiveSupport
# Callbacks are code hooks that are run at key points in an object's life cycle.
# The typical use case is to have a base class define a set of callbacks
# relevant to the other functionality it supplies, so that subclasses can
# install callbacks that enhance or modify the base functionality without
# needing to override or redefine methods of the base class.
#
# Mixing in this module allows you to define the events in the object's
# life cycle that will support callbacks (via +ClassMethods.define_callbacks+),
# set the instance methods, procs, or callback objects to be called (via
# +ClassMethods.set_callback+), and run the installed callbacks at the
# appropriate times (via +run_callbacks+).
#
# Three kinds of callbacks are supported: before callbacks, run before a
# certain event; after callbacks, run after the event; and around callbacks,
# blocks that surround the event, triggering it when they yield. Callback code
# can be contained in instance methods, procs or lambdas, or callback objects
# that respond to certain predetermined methods. See +ClassMethods.set_callback+
# for details.
#
# class Record
# include ActiveSupport::Callbacks
# define_callbacks :save
#
# def save
# run_callbacks :save do
# puts "- save"
# end
# end
# end
#
# class PersonRecord < Record
# set_callback :save, :before, :saving_message
# def saving_message
# puts "saving..."
# end
#
# set_callback :save, :after do |object|
# puts "saved"
# end
# end
#
# person = PersonRecord.new
# person.save
#
# Output:
# saving...
# - save
# saved
- 1
module Callbacks
- 1
extend Concern
- 1
included do
- 1
extend ActiveSupport::DescendantsTracker
- 1
class_attribute :__callbacks, instance_writer: false
- 1
self.__callbacks ||= {}
end
- 1
CALLBACK_FILTER_TYPES = [:before, :after, :around]
# If true, Active Record and Active Model callbacks returning +false+ will
# halt the entire callback chain and display a deprecation message.
# If false, callback chains will only be halted by calling +throw :abort+.
# Defaults to +true+.
- 2
mattr_accessor(:halt_and_display_warning_on_return_false, instance_writer: false) { true }
# Runs the callbacks for the given event.
#
# Calls the before and around callbacks in the order they were set, yields
# the block (if given one), and then runs the after callbacks in reverse
# order.
#
# If the callback chain was halted, returns +false+. Otherwise returns the
# result of the block, +nil+ if no callbacks have been set, or +true+
# if callbacks have been set but no block is given.
#
# run_callbacks :save do
# save
# end
#
#--
#
# As this method is used in many places, and often wraps large portions of
# user code, it has an additional design goal of minimizing its impact on
# the visible call stack. An exception from inside a :before or :after
# callback can be as noisy as it likes -- but when control has passed
# smoothly through and into the supplied block, we want as little evidence
# as possible that we were here.
- 1
def run_callbacks(kind)
- 2
callbacks = __callbacks[kind.to_sym]
- 2
if callbacks.empty?
- 2
yield if block_given?
else
env = Filters::Environment.new(self, false, nil)
next_sequence = callbacks.compile
invoke_sequence = Proc.new do
skipped = nil
while true
current = next_sequence
current.invoke_before(env)
if current.final?
env.value = !env.halted && (!block_given? || yield)
elsif current.skip?(env)
(skipped ||= []) << current
next_sequence = next_sequence.nested
next
else
next_sequence = next_sequence.nested
begin
target, block, method, *arguments = current.expand_call_template(env, invoke_sequence)
target.send(method, *arguments, &block)
ensure
next_sequence = current
end
end
current.invoke_after(env)
skipped.pop.invoke_after(env) while skipped && skipped.first
break env.value
end
end
# Common case: no 'around' callbacks defined
if next_sequence.final?
next_sequence.invoke_before(env)
env.value = !env.halted && (!block_given? || yield)
next_sequence.invoke_after(env)
env.value
else
invoke_sequence.call
end
end
end
- 1
private
# A hook invoked every time a before callback is halted.
# This can be overridden in ActiveSupport::Callbacks implementors in order
# to provide better debugging/logging.
- 1
def halted_callback_hook(filter)
end
- 1
module Conditionals # :nodoc:
- 1
class Value
- 1
def initialize(&block)
@block = block
end
- 1
def call(target, value); @block.call(value); end
end
end
- 1
module Filters
- 1
Environment = Struct.new(:target, :halted, :value)
- 1
class Before
- 1
def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter)
halted_lambda = chain_config[:terminator]
if user_conditions.any?
halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
else
halting(callback_sequence, user_callback, halted_lambda, filter)
end
end
- 1
def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
callback_sequence.before do |env|
target = env.target
value = env.value
halted = env.halted
if !halted && user_conditions.all? { |c| c.call(target, value) }
result_lambda = -> { user_callback.call target, value }
env.halted = halted_lambda.call(target, result_lambda)
if env.halted
target.send :halted_callback_hook, filter
end
end
env
end
end
- 1
private_class_method :halting_and_conditional
- 1
def self.halting(callback_sequence, user_callback, halted_lambda, filter)
callback_sequence.before do |env|
target = env.target
value = env.value
halted = env.halted
unless halted
result_lambda = -> { user_callback.call target, value }
env.halted = halted_lambda.call(target, result_lambda)
if env.halted
target.send :halted_callback_hook, filter
end
end
env
end
end
- 1
private_class_method :halting
end
- 1
class After
- 1
def self.build(callback_sequence, user_callback, user_conditions, chain_config)
if chain_config[:skip_after_callbacks_if_terminated]
if user_conditions.any?
halting_and_conditional(callback_sequence, user_callback, user_conditions)
else
halting(callback_sequence, user_callback)
end
else
if user_conditions.any?
conditional callback_sequence, user_callback, user_conditions
else
simple callback_sequence, user_callback
end
end
end
- 1
def self.halting_and_conditional(callback_sequence, user_callback, user_conditions)
callback_sequence.after do |env|
target = env.target
value = env.value
halted = env.halted
if !halted && user_conditions.all? { |c| c.call(target, value) }
user_callback.call target, value
end
env
end
end
- 1
private_class_method :halting_and_conditional
- 1
def self.halting(callback_sequence, user_callback)
callback_sequence.after do |env|
unless env.halted
user_callback.call env.target, env.value
end
env
end
end
- 1
private_class_method :halting
- 1
def self.conditional(callback_sequence, user_callback, user_conditions)
callback_sequence.after do |env|
target = env.target
value = env.value
if user_conditions.all? { |c| c.call(target, value) }
user_callback.call target, value
end
env
end
end
- 1
private_class_method :conditional
- 1
def self.simple(callback_sequence, user_callback)
callback_sequence.after do |env|
user_callback.call env.target, env.value
env
end
end
- 1
private_class_method :simple
end
end
- 1
class Callback #:nodoc:#
- 1
def self.build(chain, filter, kind, options)
if filter.is_a?(String)
ActiveSupport::Deprecation.warn(<<-MSG.squish)
Passing string to define callback is deprecated and will be removed
in Rails 5.1 without replacement.
MSG
end
new chain.name, filter, kind, options, chain.config
end
- 1
attr_accessor :kind, :name
- 1
attr_reader :chain_config
- 1
def initialize(name, filter, kind, options, chain_config)
@chain_config = chain_config
@name = name
@kind = kind
@filter = filter
@key = compute_identifier filter
@if = Array(options[:if])
@unless = Array(options[:unless])
end
- 1
def filter; @key; end
- 1
def raw_filter; @filter; end
- 1
def merge_conditional_options(chain, if_option:, unless_option:)
options = {
if: @if.dup,
unless: @unless.dup
}
options[:if].concat Array(unless_option)
options[:unless].concat Array(if_option)
self.class.build chain, @filter, @kind, options
end
- 1
def matches?(_kind, _filter)
@kind == _kind && filter == _filter
end
- 1
def duplicates?(other)
case @filter
when Symbol, String
matches?(other.kind, other.filter)
else
false
end
end
# Wraps code with filter
- 1
def apply(callback_sequence)
user_conditions = conditions_lambdas
user_callback = CallTemplate.build(@filter, self)
case kind
when :before
Filters::Before.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config, @filter)
when :after
Filters::After.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config)
when :around
callback_sequence.around(user_callback, user_conditions)
end
end
- 1
def current_scopes
Array(chain_config[:scope]).map { |s| public_send(s) }
end
- 1
private
- 1
def compute_identifier(filter)
case filter
when String, ::Proc
filter.object_id
else
filter
end
end
- 1
def conditions_lambdas
@if.map { |c| CallTemplate.build(c, self).make_lambda } +
@unless.map { |c| CallTemplate.build(c, self).inverted_lambda }
end
end
# A future invocation of user-supplied code (either as a callback,
# or a condition filter).
- 1
class CallTemplate # :nodoc:
- 1
def initialize(target, method, arguments, block)
@override_target = target
@method_name = method
@arguments = arguments
@override_block = block
end
# Return the parts needed to make this call, with the given
# input values.
#
# Returns an array of the form:
#
# [target, block, method, *arguments]
#
# This array can be used as such:
#
# target.send(method, *arguments, &block)
#
# The actual invocation is left up to the caller to minimize
# call stack pollution.
- 1
def expand(target, value, block)
result = @arguments.map { |arg|
case arg
when :value; value
when :target; target
when :block; block || raise(ArgumentError)
end
}
result.unshift @method_name
result.unshift @override_block || block
result.unshift @override_target || target
# target, block, method, *arguments = result
# target.send(method, *arguments, &block)
result
end
# Return a lambda that will make this call when given the input
# values.
- 1
def make_lambda
lambda do |target, value, &block|
target, block, method, *arguments = expand(target, value, block)
target.send(method, *arguments, &block)
end
end
# Return a lambda that will make this call when given the input
# values, but then return the boolean inverse of that result.
- 1
def inverted_lambda
lambda do |target, value, &block|
target, block, method, *arguments = expand(target, value, block)
! target.send(method, *arguments, &block)
end
end
# Filters support:
#
# Symbols:: A method to call.
# Strings:: Some content to evaluate.
# Procs:: A proc to call with the object.
# Objects:: An object with a <tt>before_foo</tt> method on it to call.
#
# All of these objects are converted into a CallTemplate and handled
# the same after this point.
- 1
def self.build(filter, callback)
case filter
when Symbol
new(nil, filter, [], nil)
when String
new(nil, :instance_exec, [:value], compile_lambda(filter))
when Conditionals::Value
new(filter, :call, [:target, :value], nil)
when ::Proc
if filter.arity > 1
new(nil, :instance_exec, [:target, :block], filter)
elsif filter.arity > 0
new(nil, :instance_exec, [:target], filter)
else
new(nil, :instance_exec, [], filter)
end
else
method_to_call = callback.current_scopes.join("_")
new(filter, method_to_call, [:target], nil)
end
end
- 1
def self.compile_lambda(filter)
eval("lambda { |value| #{filter} }")
end
end
# Execute before and after filters in a sequence instead of
# chaining them with nested lambda calls, see:
# https://github.com/rails/rails/issues/18011
- 1
class CallbackSequence # :nodoc:
- 1
def initialize(nested = nil, call_template = nil, user_conditions = nil)
@nested = nested
@call_template = call_template
@user_conditions = user_conditions
@before = []
@after = []
end
- 1
def before(&before)
@before.unshift(before)
self
end
- 1
def after(&after)
@after.push(after)
self
end
- 1
def around(call_template, user_conditions)
CallbackSequence.new(self, call_template, user_conditions)
end
- 1
def skip?(arg)
arg.halted || !@user_conditions.all? { |c| c.call(arg.target, arg.value) }
end
- 1
def nested
@nested
end
- 1
def final?
!@call_template
end
- 1
def expand_call_template(arg, block)
@call_template.expand(arg.target, arg.value, block)
end
- 1
def invoke_before(arg)
@before.each { |b| b.call(arg) }
end
- 1
def invoke_after(arg)
@after.each { |a| a.call(arg) }
end
end
# An Array with a compile method.
- 1
class CallbackChain #:nodoc:#
- 1
include Enumerable
- 1
attr_reader :name, :config
- 1
def initialize(name, config)
- 2
@name = name
- 2
@config = {
scope: [:kind],
terminator: default_terminator
}.merge!(config)
- 2
@chain = []
- 2
@callbacks = nil
- 2
@mutex = Mutex.new
end
- 1
def each(&block); @chain.each(&block); end
- 1
def index(o); @chain.index(o); end
- 3
def empty?; @chain.empty?; end
- 1
def insert(index, o)
@callbacks = nil
@chain.insert(index, o)
end
- 1
def delete(o)
@callbacks = nil
@chain.delete(o)
end
- 1
def clear
@callbacks = nil
@chain.clear
self
end
- 1
def initialize_copy(other)
@callbacks = nil
@chain = other.chain.dup
@mutex = Mutex.new
end
- 1
def compile
@callbacks || @mutex.synchronize do
final_sequence = CallbackSequence.new
@callbacks ||= @chain.reverse.inject(final_sequence) do |callback_sequence, callback|
callback.apply callback_sequence
end
end
end
- 1
def append(*callbacks)
callbacks.each { |c| append_one(c) }
end
- 1
def prepend(*callbacks)
callbacks.each { |c| prepend_one(c) }
end
- 1
protected
- 1
def chain; @chain; end
- 1
private
- 1
def append_one(callback)
@callbacks = nil
remove_duplicates(callback)
@chain.push(callback)
end
- 1
def prepend_one(callback)
@callbacks = nil
remove_duplicates(callback)
@chain.unshift(callback)
end
- 1
def remove_duplicates(callback)
@callbacks = nil
@chain.delete_if { |c| callback.duplicates?(c) }
end
- 1
def default_terminator
- 2
Proc.new do |target, result_lambda|
terminate = true
catch(:abort) do
result_lambda.call if result_lambda.is_a?(Proc)
terminate = false
end
terminate
end
end
end
- 1
module ClassMethods
- 1
def normalize_callback_params(filters, block) # :nodoc:
type = CALLBACK_FILTER_TYPES.include?(filters.first) ? filters.shift : :before
options = filters.extract_options!
filters.unshift(block) if block
[type, filters, options.dup]
end
# This is used internally to append, prepend and skip callbacks to the
# CallbackChain.
- 1
def __update_callbacks(name) #:nodoc:
([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse_each do |target|
chain = target.get_callbacks name
yield target, chain.dup
end
end
# Install a callback for the given event.
#
# set_callback :save, :before, :before_method
# set_callback :save, :after, :after_method, if: :condition
# set_callback :save, :around, ->(r, block) { stuff; result = block.call; stuff }
#
# The second argument indicates whether the callback is to be run +:before+,
# +:after+, or +:around+ the event. If omitted, +:before+ is assumed. This
# means the first example above can also be written as:
#
# set_callback :save, :before_method
#
# The callback can be specified as a symbol naming an instance method; as a
# proc, lambda, or block; as a string to be instance evaluated(deprecated); or as an
# object that responds to a certain method determined by the <tt>:scope</tt>
# argument to +define_callbacks+.
#
# If a proc, lambda, or block is given, its body is evaluated in the context
# of the current object. It can also optionally accept the current object as
# an argument.
#
# Before and around callbacks are called in the order that they are set;
# after callbacks are called in the reverse order.
#
# Around callbacks can access the return value from the event, if it
# wasn't halted, from the +yield+ call.
#
# ===== Options
#
# * <tt>:if</tt> - A symbol, a string or an array of symbols and strings,
# each naming an instance method or a proc; the callback will be called
# only when they all return a true value.
# * <tt>:unless</tt> - A symbol, a string or an array of symbols and
# strings, each naming an instance method or a proc; the callback will
# be called only when they all return a false value.
# * <tt>:prepend</tt> - If +true+, the callback will be prepended to the
# existing chain rather than appended.
- 1
def set_callback(name, *filter_list, &block)
type, filters, options = normalize_callback_params(filter_list, block)
self_chain = get_callbacks name
mapped = filters.map do |filter|
Callback.build(self_chain, filter, type, options)
end
__update_callbacks(name) do |target, chain|
options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped)
target.set_callbacks name, chain
end
end
# Skip a previously set callback. Like +set_callback+, <tt>:if</tt> or
# <tt>:unless</tt> options may be passed in order to control when the
# callback is skipped.
#
# class Writer < Person
# skip_callback :validate, :before, :check_membership, if: -> { age > 18 }
# end
#
# An <tt>ArgumentError</tt> will be raised if the callback has not
# already been set (unless the <tt>:raise</tt> option is set to <tt>false</tt>).
- 1
def skip_callback(name, *filter_list, &block)
type, filters, options = normalize_callback_params(filter_list, block)
options[:raise] = true unless options.key?(:raise)
__update_callbacks(name) do |target, chain|
filters.each do |filter|
callback = chain.find { |c| c.matches?(type, filter) }
if !callback && options[:raise]
raise ArgumentError, "#{type.to_s.capitalize} #{name} callback #{filter.inspect} has not been defined"
end
if callback && (options.key?(:if) || options.key?(:unless))
new_callback = callback.merge_conditional_options(chain, if_option: options[:if], unless_option: options[:unless])
chain.insert(chain.index(callback), new_callback)
end
chain.delete(callback)
end
target.set_callbacks name, chain
end
end
# Remove all set callbacks for the given event.
- 1
def reset_callbacks(name)
callbacks = get_callbacks name
ActiveSupport::DescendantsTracker.descendants(self).each do |target|
chain = target.get_callbacks(name).dup
callbacks.each { |c| chain.delete(c) }
target.set_callbacks name, chain
end
set_callbacks(name, callbacks.dup.clear)
end
# Define sets of events in the object life cycle that support callbacks.
#
# define_callbacks :validate
# define_callbacks :initialize, :save, :destroy
#
# ===== Options
#
# * <tt>:terminator</tt> - Determines when a before filter will halt the
# callback chain, preventing following before and around callbacks from
# being called and the event from being triggered.
# This should be a lambda to be executed.
# The current object and the result lambda of the callback will be provided
# to the terminator lambda.
#
# define_callbacks :validate, terminator: ->(target, result_lambda) { result_lambda.call == false }
#
# In this example, if any before validate callbacks returns +false+,
# any successive before and around callback is not executed.
#
# The default terminator halts the chain when a callback throws +:abort+.
#
# * <tt>:skip_after_callbacks_if_terminated</tt> - Determines if after
# callbacks should be terminated by the <tt>:terminator</tt> option. By
# default after callbacks are executed no matter if callback chain was
# terminated or not. This option makes sense only when <tt>:terminator</tt>
# option is specified.
#
# * <tt>:scope</tt> - Indicates which methods should be executed when an
# object is used as a callback.
#
# class Audit
# def before(caller)
# puts 'Audit: before is called'
# end
#
# def before_save(caller)
# puts 'Audit: before_save is called'
# end
# end
#
# class Account
# include ActiveSupport::Callbacks
#
# define_callbacks :save
# set_callback :save, :before, Audit.new
#
# def save
# run_callbacks :save do
# puts 'save in main'
# end
# end
# end
#
# In the above case whenever you save an account the method
# <tt>Audit#before</tt> will be called. On the other hand
#
# define_callbacks :save, scope: [:kind, :name]
#
# would trigger <tt>Audit#before_save</tt> instead. That's constructed
# by calling <tt>#{kind}_#{name}</tt> on the given instance. In this
# case "kind" is "before" and "name" is "save". In this context +:kind+
# and +:name+ have special meanings: +:kind+ refers to the kind of
# callback (before/after/around) and +:name+ refers to the method on
# which callbacks are being defined.
#
# A declaration like
#
# define_callbacks :save, scope: [:name]
#
# would call <tt>Audit#save</tt>.
#
# ===== Notes
#
# +names+ passed to +define_callbacks+ must not end with
# <tt>!</tt>, <tt>?</tt> or <tt>=</tt>.
#
# Calling +define_callbacks+ multiple times with the same +names+ will
# overwrite previous callbacks registered with +set_callback+.
- 1
def define_callbacks(*names)
- 1
options = names.extract_options!
- 1
names.each do |name|
- 2
name = name.to_sym
- 2
set_callbacks name, CallbackChain.new(name, options)
- 2
module_eval <<-RUBY, __FILE__, __LINE__ + 1
- 1
def _run_#{name}_callbacks(&block)
run_callbacks #{name.inspect}, &block
end
- 1
def self._#{name}_callbacks
get_callbacks(#{name.inspect})
end
- 1
def self._#{name}_callbacks=(value)
set_callbacks(#{name.inspect}, value)
end
- 1
def _#{name}_callbacks
__callbacks[#{name.inspect}]
end
RUBY
end
end
- 1
protected
- 1
def get_callbacks(name) # :nodoc:
__callbacks[name.to_sym]
end
- 1
def set_callbacks(name, callbacks) # :nodoc:
- 2
self.__callbacks = __callbacks.merge(name.to_sym => callbacks)
end
- 1
def deprecated_false_terminator # :nodoc:
Proc.new do |target, result_lambda|
terminate = true
catch(:abort) do
result = result_lambda.call if result_lambda.is_a?(Proc)
if Callbacks.halt_and_display_warning_on_return_false && result == false
display_deprecation_warning_for_false_terminator
else
terminate = false
end
end
terminate
end
end
- 1
private
- 1
def display_deprecation_warning_for_false_terminator
ActiveSupport::Deprecation.warn(<<-MSG.squish)
Returning `false` in Active Record and Active Model callbacks will not implicitly halt a callback chain in Rails 5.1.
To explicitly halt the callback chain, please use `throw :abort` instead.
MSG
end
end
end
end
- 1
module ActiveSupport
# A typical module looks like this:
#
# module M
# def self.included(base)
# base.extend ClassMethods
# base.class_eval do
# scope :disabled, -> { where(disabled: true) }
# end
# end
#
# module ClassMethods
# ...
# end
# end
#
# By using <tt>ActiveSupport::Concern</tt> the above module could instead be
# written as:
#
# require 'active_support/concern'
#
# module M
# extend ActiveSupport::Concern
#
# included do
# scope :disabled, -> { where(disabled: true) }
# end
#
# class_methods do
# ...
# end
# end
#
# Moreover, it gracefully handles module dependencies. Given a +Foo+ module
# and a +Bar+ module which depends on the former, we would typically write the
# following:
#
# module Foo
# def self.included(base)
# base.class_eval do
# def self.method_injected_by_foo
# ...
# end
# end
# end
# end
#
# module Bar
# def self.included(base)
# base.method_injected_by_foo
# end
# end
#
# class Host
# include Foo # We need to include this dependency for Bar
# include Bar # Bar is the module that Host really needs
# end
#
# But why should +Host+ care about +Bar+'s dependencies, namely +Foo+? We
# could try to hide these from +Host+ directly including +Foo+ in +Bar+:
#
# module Bar
# include Foo
# def self.included(base)
# base.method_injected_by_foo
# end
# end
#
# class Host
# include Bar
# end
#
# Unfortunately this won't work, since when +Foo+ is included, its <tt>base</tt>
# is the +Bar+ module, not the +Host+ class. With <tt>ActiveSupport::Concern</tt>,
# module dependencies are properly resolved:
#
# require 'active_support/concern'
#
# module Foo
# extend ActiveSupport::Concern
# included do
# def self.method_injected_by_foo
# ...
# end
# end
# end
#
# module Bar
# extend ActiveSupport::Concern
# include Foo
#
# included do
# self.method_injected_by_foo
# end
# end
#
# class Host
# include Bar # It works, now Bar takes care of its dependencies
# end
- 1
module Concern
- 1
class MultipleIncludedBlocks < StandardError #:nodoc:
- 1
def initialize
super "Cannot define multiple 'included' blocks for a Concern"
end
end
- 1
def self.extended(base) #:nodoc:
- 6
base.instance_variable_set(:@_dependencies, [])
end
- 1
def append_features(base)
- 5
if base.instance_variable_defined?(:@_dependencies)
base.instance_variable_get(:@_dependencies) << self
return false
else
- 5
return false if base < self
- 5
@_dependencies.each { |dep| base.include(dep) }
- 5
super
- 5
base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
- 5
base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block)
end
end
- 1
def included(base = nil, &block)
- 9
if base.nil?
- 4
raise MultipleIncludedBlocks if instance_variable_defined?(:@_included_block)
- 4
@_included_block = block
else
- 5
super
end
end
- 1
def class_methods(&class_methods_module_definition)
mod = const_defined?(:ClassMethods, false) ?
const_get(:ClassMethods) :
const_set(:ClassMethods, Module.new)
mod.module_eval(&class_methods_module_definition)
end
end
end
- 1
class Hash
# By default, only instances of Hash itself are extractable.
# Subclasses of Hash may implement this method and return
# true to declare themselves as extractable. If a Hash
# is extractable, Array#extract_options! pops it from
# the Array when it is the last element of the Array.
- 1
def extractable_options?
- 6
instance_of?(Hash)
end
end
- 1
class Array
# Extracts options from a set of arguments. Removes and returns the last
# element in the array if it's a hash, otherwise returns a blank hash.
#
# def options(*args)
# args.extract_options!
# end
#
# options(1, 2) # => {}
# options(1, 2, a: :b) # => {:a=>:b}
- 1
def extract_options!
- 11
if last.is_a?(Hash) && last.extractable_options?
- 6
pop
else
- 5
{}
end
end
end
- 1
class Array
# The human way of thinking about adding stuff to the end of a list is with append.
- 1
alias_method :append, :<<
# The human way of thinking about adding stuff to the beginning of a list is with prepend.
- 1
alias_method :prepend, :unshift
end
- 1
require "active_support/core_ext/kernel/singleton_class"
- 1
require "active_support/core_ext/module/remove_method"
- 1
require "active_support/core_ext/array/extract_options"
- 1
class Class
# Declare a class-level attribute whose value is inheritable by subclasses.
# Subclasses can change their own value and it will not impact parent class.
#
# class Base
# class_attribute :setting
# end
#
# class Subclass < Base
# end
#
# Base.setting = true
# Subclass.setting # => true
# Subclass.setting = false
# Subclass.setting # => false
# Base.setting # => true
#
# In the above case as long as Subclass does not assign a value to setting
# by performing <tt>Subclass.setting = _something_</tt>, <tt>Subclass.setting</tt>
# would read value assigned to parent class. Once Subclass assigns a value then
# the value assigned by Subclass would be returned.
#
# This matches normal Ruby method inheritance: think of writing an attribute
# on a subclass as overriding the reader method. However, you need to be aware
# when using +class_attribute+ with mutable structures as +Array+ or +Hash+.
# In such cases, you don't want to do changes in place. Instead use setters:
#
# Base.setting = []
# Base.setting # => []
# Subclass.setting # => []
#
# # Appending in child changes both parent and child because it is the same object:
# Subclass.setting << :foo
# Base.setting # => [:foo]
# Subclass.setting # => [:foo]
#
# # Use setters to not propagate changes:
# Base.setting = []
# Subclass.setting += [:foo]
# Base.setting # => []
# Subclass.setting # => [:foo]
#
# For convenience, an instance predicate method is defined as well.
# To skip it, pass <tt>instance_predicate: false</tt>.
#
# Subclass.setting? # => false
#
# Instances may overwrite the class value in the same way:
#
# Base.setting = true
# object = Base.new
# object.setting # => true
# object.setting = false
# object.setting # => false
# Base.setting # => true
#
# To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
#
# object.setting # => NoMethodError
# object.setting? # => NoMethodError
#
# To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
#
# object.setting = false # => NoMethodError
#
# To opt out of both instance methods, pass <tt>instance_accessor: false</tt>.
- 1
def class_attribute(*attrs)
- 2
options = attrs.extract_options!
- 2
instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true)
- 2
instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true)
- 2
instance_predicate = options.fetch(:instance_predicate, true)
- 2
attrs.each do |name|
- 2
remove_possible_singleton_method(name)
- 3
define_singleton_method(name) { nil }
- 2
remove_possible_singleton_method("#{name}?")
- 2
define_singleton_method("#{name}?") { !!public_send(name) } if instance_predicate
- 2
ivar = "@#{name}"
- 2
remove_possible_singleton_method("#{name}=")
- 2
define_singleton_method("#{name}=") do |val|
- 3
singleton_class.class_eval do
- 3
remove_possible_method(name)
- 7
define_method(name) { val }
end
- 3
if singleton_class?
class_eval do
remove_possible_method(name)
define_method(name) do
if instance_variable_defined? ivar
instance_variable_get ivar
else
singleton_class.send name
end
end
end
end
- 3
val
end
- 2
if instance_reader
- 2
remove_possible_method name
- 2
define_method(name) do
- 2
if instance_variable_defined?(ivar)
instance_variable_get ivar
else
- 2
self.class.public_send name
end
end
- 2
remove_possible_method "#{name}?"
- 2
define_method("#{name}?") { !!public_send(name) } if instance_predicate
end
- 2
if instance_writer
remove_possible_method "#{name}="
attr_writer name
end
end
end
end
- 1
require "active_support/core_ext/module/attribute_accessors"
- 1
module DateAndTime
- 1
module Compatibility
# If true, +to_time+ preserves the timezone offset of receiver.
#
# NOTE: With Ruby 2.4+ the default for +to_time+ changed from
# converting to the local system time, to preserving the offset
# of the receiver. For backwards compatibility we're overriding
# this behavior, but new apps will have an initializer that sets
# this to true, because the new behavior is preferred.
- 2
mattr_accessor(:preserve_timezone, instance_writer: false) { false }
- 1
def to_time
if preserve_timezone
@_to_time_with_instance_offset ||= getlocal(utc_offset)
else
@_to_time_with_system_offset ||= getlocal
end
end
end
end
- 1
class Hash
# Returns a new hash with +self+ and +other_hash+ merged recursively.
#
# h1 = { a: true, b: { c: [1, 2, 3] } }
# h2 = { a: false, b: { x: [3, 4, 5] } }
#
# h1.deep_merge(h2) # => { a: false, b: { c: [1, 2, 3], x: [3, 4, 5] } }
#
# Like with Hash#merge in the standard library, a block can be provided
# to merge values:
#
# h1 = { a: 100, b: 200, c: { c1: 100 } }
# h2 = { b: 250, c: { c1: 200 } }
# h1.deep_merge(h2) { |key, this_val, other_val| this_val + other_val }
# # => { a: 100, b: 450, c: { c1: 300 } }
- 1
def deep_merge(other_hash, &block)
dup.deep_merge!(other_hash, &block)
end
# Same as +deep_merge+, but modifies +self+.
- 1
def deep_merge!(other_hash, &block)
other_hash.each_pair do |current_key, other_value|
this_value = self[current_key]
self[current_key] = if this_value.is_a?(Hash) && other_value.is_a?(Hash)
this_value.deep_merge(other_value, &block)
else
if block_given? && key?(current_key)
block.call(current_key, this_value, other_value)
else
other_value
end
end
end
self
end
end
- 1
class Hash
# Returns a hash that includes everything except given keys.
# hash = { a: true, b: false, c: nil }
# hash.except(:c) # => { a: true, b: false }
# hash.except(:a, :b) # => { c: nil }
# hash # => { a: true, b: false, c: nil }
#
# This is useful for limiting a set of parameters to everything but a few known toggles:
# @person.update(params[:person].except(:admin))
- 1
def except(*keys)
dup.except!(*keys)
end
# Removes the given keys from hash and returns it.
# hash = { a: true, b: false, c: nil }
# hash.except!(:c) # => { a: true, b: false }
# hash # => { a: true, b: false }
- 1
def except!(*keys)
keys.each { |key| delete(key) }
self
end
end
- 1
class Hash
# Slices a hash to include only the given keys. Returns a hash containing
# the given keys.
#
# { a: 1, b: 2, c: 3, d: 4 }.slice(:a, :b)
# # => {:a=>1, :b=>2}
#
# This is useful for limiting an options hash to valid keys before
# passing to a method:
#
# def search(criteria = {})
# criteria.assert_valid_keys(:mass, :velocity, :time)
# end
#
# search(options.slice(:mass, :velocity, :time))
#
# If you have an array of keys you want to limit to, you should splat them:
#
# valid_keys = [:mass, :velocity, :time]
# search(options.slice(*valid_keys))
- 1
def slice(*keys)
keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
keys.each_with_object(self.class.new) { |k, hash| hash[k] = self[k] if has_key?(k) }
end
# Replaces the hash with only the given keys.
# Returns a hash containing the removed key/value pairs.
#
# { a: 1, b: 2, c: 3, d: 4 }.slice!(:a, :b)
# # => {:c=>3, :d=>4}
- 1
def slice!(*keys)
keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
omit = slice(*self.keys - keys)
hash = slice(*keys)
hash.default = default
hash.default_proc = default_proc if default_proc
replace(hash)
omit
end
# Removes and returns the key/value pairs matching the given keys.
#
# { a: 1, b: 2, c: 3, d: 4 }.extract!(:a, :b) # => {:a=>1, :b=>2}
# { a: 1, b: 2 }.extract!(:a, :x) # => {:a=>1}
- 1
def extract!(*keys)
keys.each_with_object(self.class.new) { |key, result| result[key] = delete(key) if has_key?(key) }
end
end
- 1
module Kernel
- 1
module_function
# Sets $VERBOSE to +nil+ for the duration of the block and back to its original
# value afterwards.
#
# silence_warnings do
# value = noisy_call # no warning voiced
# end
#
# noisy_call # warning voiced
- 1
def silence_warnings
- 2
with_warnings(nil) { yield }
end
# Sets $VERBOSE to +true+ for the duration of the block and back to its
# original value afterwards.
- 1
def enable_warnings
with_warnings(true) { yield }
end
# Sets $VERBOSE for the duration of the block and back to its original
# value afterwards.
- 1
def with_warnings(flag)
- 1
old_verbose, $VERBOSE = $VERBOSE, flag
- 1
yield
ensure
- 1
$VERBOSE = old_verbose
end
# Blocks and ignores any exception passed as argument if raised within the block.
#
# suppress(ZeroDivisionError) do
# 1/0
# puts 'This code is NOT reached'
# end
#
# puts 'This code gets executed and nothing related to ZeroDivisionError was seen'
- 1
def suppress(*exception_classes)
yield
rescue *exception_classes
end
end
- 1
module Kernel
# class_eval on an object acts like singleton_class.class_eval.
- 1
def class_eval(*args, &block)
singleton_class.class_eval(*args, &block)
end
end
- 1
class Module
# Allows you to make aliases for attributes, which includes
# getter, setter, and a predicate.
#
# class Content < ActiveRecord::Base
# # has a title attribute
# end
#
# class Email < Content
# alias_attribute :subject, :title
# end
#
# e = Email.find(1)
# e.title # => "Superstars"
# e.subject # => "Superstars"
# e.subject? # => true
# e.subject = "Megastars"
# e.title # => "Megastars"
- 1
def alias_attribute(new_name, old_name)
# The following reader methods use an explicit `self` receiver in order to
# support aliases that start with an uppercase letter. Otherwise, they would
# be resolved as constants instead.
module_eval <<-STR, __FILE__, __LINE__ + 1
def #{new_name}; self.#{old_name}; end # def subject; self.title; end
def #{new_name}?; self.#{old_name}?; end # def subject?; self.title?; end
def #{new_name}=(v); self.#{old_name} = v; end # def subject=(v); self.title = v; end
STR
end
end
- 1
require "active_support/core_ext/array/extract_options"
- 1
require "active_support/core_ext/regexp"
# Extends the module object with class/module and instance accessors for
# class/module attributes, just like the native attr* accessors for instance
# attributes.
- 1
class Module
# Defines a class attribute and creates a class and instance reader methods.
# The underlying class variable is set to +nil+, if it is not previously
# defined. All class and instance methods created will be public, even if
# this method is called with a private or protected access modifier.
#
# module HairColors
# mattr_reader :hair_colors
# end
#
# HairColors.hair_colors # => nil
# HairColors.class_variable_set("@@hair_colors", [:brown, :black])
# HairColors.hair_colors # => [:brown, :black]
#
# The attribute name must be a valid method name in Ruby.
#
# module Foo
# mattr_reader :"1_Badname"
# end
# # => NameError: invalid attribute name: 1_Badname
#
# If you want to opt out the creation on the instance reader method, pass
# <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
#
# module HairColors
# mattr_reader :hair_colors, instance_reader: false
# end
#
# class Person
# include HairColors
# end
#
# Person.new.hair_colors # => NoMethodError
#
#
# Also, you can pass a block to set up the attribute with a default value.
#
# module HairColors
# mattr_reader :hair_colors do
# [:brown, :black, :blonde, :red]
# end
# end
#
# class Person
# include HairColors
# end
#
# Person.new.hair_colors # => [:brown, :black, :blonde, :red]
- 1
def mattr_reader(*syms)
- 4
options = syms.extract_options!
- 4
syms.each do |sym|
- 4
raise NameError.new("invalid attribute name: #{sym}") unless /\A[_A-Za-z]\w*\z/.match?(sym)
- 4
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
- 1
@@#{sym} = nil unless defined? @@#{sym}
- 1
def self.#{sym}
@@#{sym}
end
EOS
- 4
unless options[:instance_reader] == false || options[:instance_accessor] == false
- 4
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
- 1
def #{sym}
@@#{sym}
end
EOS
end
- 4
class_variable_set("@@#{sym}", yield) if block_given?
end
end
- 1
alias :cattr_reader :mattr_reader
# Defines a class attribute and creates a class and instance writer methods to
# allow assignment to the attribute. All class and instance methods created
# will be public, even if this method is called with a private or protected
# access modifier.
#
# module HairColors
# mattr_writer :hair_colors
# end
#
# class Person
# include HairColors
# end
#
# HairColors.hair_colors = [:brown, :black]
# Person.class_variable_get("@@hair_colors") # => [:brown, :black]
# Person.new.hair_colors = [:blonde, :red]
# HairColors.class_variable_get("@@hair_colors") # => [:blonde, :red]
#
# If you want to opt out the instance writer method, pass
# <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
#
# module HairColors
# mattr_writer :hair_colors, instance_writer: false
# end
#
# class Person
# include HairColors
# end
#
# Person.new.hair_colors = [:blonde, :red] # => NoMethodError
#
# Also, you can pass a block to set up the attribute with a default value.
#
# module HairColors
# mattr_writer :hair_colors do
# [:brown, :black, :blonde, :red]
# end
# end
#
# class Person
# include HairColors
# end
#
# Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
- 1
def mattr_writer(*syms)
- 4
options = syms.extract_options!
- 4
syms.each do |sym|
- 4
raise NameError.new("invalid attribute name: #{sym}") unless /\A[_A-Za-z]\w*\z/.match?(sym)
- 4
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
- 1
@@#{sym} = nil unless defined? @@#{sym}
- 1
def self.#{sym}=(obj)
@@#{sym} = obj
end
EOS
- 4
unless options[:instance_writer] == false || options[:instance_accessor] == false
- 2
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
- 1
def #{sym}=(obj)
@@#{sym} = obj
end
EOS
end
- 4
send("#{sym}=", yield) if block_given?
end
end
- 1
alias :cattr_writer :mattr_writer
# Defines both class and instance accessors for class attributes.
# All class and instance methods created will be public, even if
# this method is called with a private or protected access modifier.
#
# module HairColors
# mattr_accessor :hair_colors
# end
#
# class Person
# include HairColors
# end
#
# HairColors.hair_colors = [:brown, :black, :blonde, :red]
# HairColors.hair_colors # => [:brown, :black, :blonde, :red]
# Person.new.hair_colors # => [:brown, :black, :blonde, :red]
#
# If a subclass changes the value then that would also change the value for
# parent class. Similarly if parent class changes the value then that would
# change the value of subclasses too.
#
# class Male < Person
# end
#
# Male.new.hair_colors << :blue
# Person.new.hair_colors # => [:brown, :black, :blonde, :red, :blue]
#
# To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
# To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
#
# module HairColors
# mattr_accessor :hair_colors, instance_writer: false, instance_reader: false
# end
#
# class Person
# include HairColors
# end
#
# Person.new.hair_colors = [:brown] # => NoMethodError
# Person.new.hair_colors # => NoMethodError
#
# Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
#
# module HairColors
# mattr_accessor :hair_colors, instance_accessor: false
# end
#
# class Person
# include HairColors
# end
#
# Person.new.hair_colors = [:brown] # => NoMethodError
# Person.new.hair_colors # => NoMethodError
#
# Also you can pass a block to set up the attribute with a default value.
#
# module HairColors
# mattr_accessor :hair_colors do
# [:brown, :black, :blonde, :red]
# end
# end
#
# class Person
# include HairColors
# end
#
# Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
- 1
def mattr_accessor(*syms, &blk)
- 4
mattr_reader(*syms, &blk)
- 4
mattr_writer(*syms)
end
- 1
alias :cattr_accessor :mattr_accessor
end
- 1
require "set"
- 1
require "active_support/core_ext/regexp"
- 1
class Module
# Error generated by +delegate+ when a method is called on +nil+ and +allow_nil+
# option is not used.
- 1
class DelegationError < NoMethodError; end
- 1
RUBY_RESERVED_KEYWORDS = %w(alias and BEGIN begin break case class def defined? do
else elsif END end ensure false for if in module next nil not or redo rescue retry
return self super then true undef unless until when while yield)
- 1
DELEGATION_RESERVED_KEYWORDS = %w(_ arg args block)
- 1
DELEGATION_RESERVED_METHOD_NAMES = Set.new(
RUBY_RESERVED_KEYWORDS + DELEGATION_RESERVED_KEYWORDS
).freeze
# Provides a +delegate+ class method to easily expose contained objects'
# public methods as your own.
#
# ==== Options
# * <tt>:to</tt> - Specifies the target object
# * <tt>:prefix</tt> - Prefixes the new method with the target name or a custom prefix
# * <tt>:allow_nil</tt> - if set to true, prevents a +NoMethodError+ from being raised
#
# The macro receives one or more method names (specified as symbols or
# strings) and the name of the target object via the <tt>:to</tt> option
# (also a symbol or string).
#
# Delegation is particularly useful with Active Record associations:
#
# class Greeter < ActiveRecord::Base
# def hello
# 'hello'
# end
#
# def goodbye
# 'goodbye'
# end
# end
#
# class Foo < ActiveRecord::Base
# belongs_to :greeter
# delegate :hello, to: :greeter
# end
#
# Foo.new.hello # => "hello"
# Foo.new.goodbye # => NoMethodError: undefined method `goodbye' for #<Foo:0x1af30c>
#
# Multiple delegates to the same target are allowed:
#
# class Foo < ActiveRecord::Base
# belongs_to :greeter
# delegate :hello, :goodbye, to: :greeter
# end
#
# Foo.new.goodbye # => "goodbye"
#
# Methods can be delegated to instance variables, class variables, or constants
# by providing them as a symbols:
#
# class Foo
# CONSTANT_ARRAY = [0,1,2,3]
# @@class_array = [4,5,6,7]
#
# def initialize
# @instance_array = [8,9,10,11]
# end
# delegate :sum, to: :CONSTANT_ARRAY
# delegate :min, to: :@@class_array
# delegate :max, to: :@instance_array
# end
#
# Foo.new.sum # => 6
# Foo.new.min # => 4
# Foo.new.max # => 11
#
# It's also possible to delegate a method to the class by using +:class+:
#
# class Foo
# def self.hello
# "world"
# end
#
# delegate :hello, to: :class
# end
#
# Foo.new.hello # => "world"
#
# Delegates can optionally be prefixed using the <tt>:prefix</tt> option. If the value
# is <tt>true</tt>, the delegate methods are prefixed with the name of the object being
# delegated to.
#
# Person = Struct.new(:name, :address)
#
# class Invoice < Struct.new(:client)
# delegate :name, :address, to: :client, prefix: true
# end
#
# john_doe = Person.new('John Doe', 'Vimmersvej 13')
# invoice = Invoice.new(john_doe)
# invoice.client_name # => "John Doe"
# invoice.client_address # => "Vimmersvej 13"
#
# It is also possible to supply a custom prefix.
#
# class Invoice < Struct.new(:client)
# delegate :name, :address, to: :client, prefix: :customer
# end
#
# invoice = Invoice.new(john_doe)
# invoice.customer_name # => 'John Doe'
# invoice.customer_address # => 'Vimmersvej 13'
#
# If the target is +nil+ and does not respond to the delegated method a
# +NoMethodError+ is raised, as with any other value. Sometimes, however, it
# makes sense to be robust to that situation and that is the purpose of the
# <tt>:allow_nil</tt> option: If the target is not +nil+, or it is and
# responds to the method, everything works as usual. But if it is +nil+ and
# does not respond to the delegated method, +nil+ is returned.
#
# class User < ActiveRecord::Base
# has_one :profile
# delegate :age, to: :profile
# end
#
# User.new.age # raises NoMethodError: undefined method `age'
#
# But if not having a profile yet is fine and should not be an error
# condition:
#
# class User < ActiveRecord::Base
# has_one :profile
# delegate :age, to: :profile, allow_nil: true
# end
#
# User.new.age # nil
#
# Note that if the target is not +nil+ then the call is attempted regardless of the
# <tt>:allow_nil</tt> option, and thus an exception is still raised if said object
# does not respond to the method:
#
# class Foo
# def initialize(bar)
# @bar = bar
# end
#
# delegate :name, to: :@bar, allow_nil: true
# end
#
# Foo.new("Bar").name # raises NoMethodError: undefined method `name'
#
# The target method must be public, otherwise it will raise +NoMethodError+.
- 1
def delegate(*methods, to: nil, prefix: nil, allow_nil: nil)
- 15
unless to
raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, to: :greeter)."
end
- 15
if prefix == true && /^[^a-z_]/.match?(to)
raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method."
end
- 15
method_prefix = \
- 15
if prefix
"#{prefix == true ? to : prefix}_"
else
- 15
""
end
- 15
location = caller_locations(1, 1).first
- 15
file, line = location.path, location.lineno
- 15
to = to.to_s
- 15
to = "self.#{to}" if DELEGATION_RESERVED_METHOD_NAMES.include?(to)
- 15
methods.each do |method|
# Attribute writer methods only accept one argument. Makes sure []=
# methods still accept two arguments.
- 15
definition = /[^\]]=$/.match?(method) ? "arg" : "*args, &block"
# The following generated method calls the target exactly once, storing
# the returned value in a dummy variable.
#
# Reason is twofold: On one hand doing less calls is in general better.
# On the other hand it could be that the target has side-effects,
# whereas conceptually, from the user point of view, the delegator should
# be doing one call.
- 15
if allow_nil
method_def = [
"def #{method_prefix}#{method}(#{definition})",
"_ = #{to}",
"if !_.nil? || nil.respond_to?(:#{method})",
" _.#{method}(#{definition})",
"end",
"end"
].join ";"
else
- 15
exception = %(raise DelegationError, "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
- 15
method_def = [
"def #{method_prefix}#{method}(#{definition})",
" _ = #{to}",
" _.#{method}(#{definition})",
"rescue NoMethodError => e",
" if _.nil? && e.name == :#{method}",
" #{exception}",
" else",
" raise",
" end",
"end"
].join ";"
end
- 15
module_eval(method_def, file, line)
end
end
# When building decorators, a common pattern may emerge:
#
# class Partition
# def initialize(first_event)
# @events = [ first_event ]
# end
#
# def people
# if @events.first.detail.people.any?
# @events.collect { |e| Array(e.detail.people) }.flatten.uniq
# else
# @events.collect(&:creator).uniq
# end
# end
#
# private
# def respond_to_missing?(name, include_private = false)
# @events.respond_to?(name, include_private)
# end
#
# def method_missing(method, *args, &block)
# @events.send(method, *args, &block)
# end
# end
#
# With `Module#delegate_missing_to`, the above is condensed to:
#
# class Partition
# delegate_missing_to :@events
#
# def initialize(first_event)
# @events = [ first_event ]
# end
#
# def people
# if @events.first.detail.people.any?
# @events.collect { |e| Array(e.detail.people) }.flatten.uniq
# else
# @events.collect(&:creator).uniq
# end
# end
# end
#
# The target can be anything callable within the object. E.g. instance
# variables, methods, constants ant the likes.
- 1
def delegate_missing_to(target)
target = target.to_s
target = "self.#{target}" if DELEGATION_RESERVED_METHOD_NAMES.include?(target)
module_eval <<-RUBY, __FILE__, __LINE__ + 1
def respond_to_missing?(name, include_private = false)
#{target}.respond_to?(name, include_private)
end
def method_missing(method, *args, &block)
if #{target}.respond_to?(method)
#{target}.public_send(method, *args, &block)
else
super
end
end
RUBY
end
end
- 1
class Module
# deprecate :foo
# deprecate bar: 'message'
# deprecate :foo, :bar, baz: 'warning!', qux: 'gone!'
#
# You can also use custom deprecator instance:
#
# deprecate :foo, deprecator: MyLib::Deprecator.new
# deprecate :foo, bar: "warning!", deprecator: MyLib::Deprecator.new
#
# \Custom deprecators must respond to <tt>deprecation_warning(deprecated_method_name, message, caller_backtrace)</tt>
# method where you can implement your custom warning behavior.
#
# class MyLib::Deprecator
# def deprecation_warning(deprecated_method_name, message, caller_backtrace = nil)
# message = "#{deprecated_method_name} is deprecated and will be removed from MyLibrary | #{message}"
# Kernel.warn message
# end
# end
- 1
def deprecate(*method_names)
ActiveSupport::Deprecation.deprecate_methods(self, *method_names)
end
end
- 1
class Module
# Removes the named method, if it exists.
- 1
def remove_possible_method(method)
- 13
if method_defined?(method) || private_method_defined?(method)
- 3
undef_method(method)
end
end
# Removes the named singleton method, if it exists.
- 1
def remove_possible_singleton_method(method)
- 6
singleton_class.instance_eval do
- 6
remove_possible_method(method)
end
end
# Replaces the existing method definition, if there is one, with the passed
# block as its body.
- 1
def redefine_method(method, &block)
visibility = method_visibility(method)
remove_possible_method(method)
define_method(method, &block)
send(visibility, method)
end
- 1
def method_visibility(method) # :nodoc:
case
when private_method_defined?(method)
:private
when protected_method_defined?(method)
:protected
else
:public
end
end
end
- 1
class Regexp #:nodoc:
- 1
def multiline?
options & MULTILINE == MULTILINE
end
def match?(string, pos = 0)
!!match(string, pos)
- 1
end unless //.respond_to?(:match?)
end
- 1
class String
# Returns the string, first removing all whitespace on both ends of
# the string, and then changing remaining consecutive whitespace
# groups into one space each.
#
# Note that it handles both ASCII and Unicode whitespace.
#
# %{ Multi-line
# string }.squish # => "Multi-line string"
# " foo bar \n \t boo".squish # => "foo bar boo"
- 1
def squish
dup.squish!
end
# Performs a destructive squish. See String#squish.
# str = " foo bar \n \t boo"
# str.squish! # => "foo bar boo"
# str # => "foo bar boo"
- 1
def squish!
gsub!(/[[:space:]]+/, " ")
strip!
self
end
# Returns a new string with all occurrences of the patterns removed.
# str = "foo bar test"
# str.remove(" test") # => "foo bar"
# str.remove(" test", /bar/) # => "foo "
# str # => "foo bar test"
- 1
def remove(*patterns)
dup.remove!(*patterns)
end
# Alters the string by removing all occurrences of the patterns.
# str = "foo bar test"
# str.remove!(" test", /bar/) # => "foo "
# str # => "foo "
- 1
def remove!(*patterns)
patterns.each do |pattern|
gsub! pattern, ""
end
self
end
# Truncates a given +text+ after a given <tt>length</tt> if +text+ is longer than <tt>length</tt>:
#
# 'Once upon a time in a world far far away'.truncate(27)
# # => "Once upon a time in a wo..."
#
# Pass a string or regexp <tt>:separator</tt> to truncate +text+ at a natural break:
#
# 'Once upon a time in a world far far away'.truncate(27, separator: ' ')
# # => "Once upon a time in a..."
#
# 'Once upon a time in a world far far away'.truncate(27, separator: /\s/)
# # => "Once upon a time in a..."
#
# The last characters will be replaced with the <tt>:omission</tt> string (defaults to "...")
# for a total length not exceeding <tt>length</tt>:
#
# 'And they found that many people were sleeping better.'.truncate(25, omission: '... (continued)')
# # => "And they f... (continued)"
- 1
def truncate(truncate_at, options = {})
return dup unless length > truncate_at
omission = options[:omission] || "..."
length_with_room_for_omission = truncate_at - omission.length
stop = \
if options[:separator]
rindex(options[:separator], length_with_room_for_omission) || length_with_room_for_omission
else
length_with_room_for_omission
end
"#{self[0, stop]}#{omission}"
end
# Truncates a given +text+ after a given number of words (<tt>words_count</tt>):
#
# 'Once upon a time in a world far far away'.truncate_words(4)
# # => "Once upon a time..."
#
# Pass a string or regexp <tt>:separator</tt> to specify a different separator of words:
#
# 'Once<br>upon<br>a<br>time<br>in<br>a<br>world'.truncate_words(5, separator: '<br>')
# # => "Once<br>upon<br>a<br>time<br>in..."
#
# The last characters will be replaced with the <tt>:omission</tt> string (defaults to "..."):
#
# 'And they found that many people were sleeping better.'.truncate_words(5, omission: '... (continued)')
# # => "And they found that many... (continued)"
- 1
def truncate_words(words_count, options = {})
sep = options[:separator] || /\s+/
sep = Regexp.escape(sep.to_s) unless Regexp === sep
if self =~ /\A((?>.+?#{sep}){#{words_count - 1}}.+?)#{sep}.*/m
$1 + (options[:omission] || "...")
else
dup
end
end
end
- 1
require "active_support/inflector/methods"
- 1
require "active_support/inflector/transliterate"
# String inflections define new methods on the String class to transform names for different purposes.
# For instance, you can figure out the name of a table from the name of a class.
#
# 'ScaleScore'.tableize # => "scale_scores"
#
- 1
class String
# Returns the plural form of the word in the string.
#
# If the optional parameter +count+ is specified,
# the singular form will be returned if <tt>count == 1</tt>.
# For any other value of +count+ the plural will be returned.
#
# If the optional parameter +locale+ is specified,
# the word will be pluralized as a word of that language.
# By default, this parameter is set to <tt>:en</tt>.
# You must define your own inflection rules for languages other than English.
#
# 'post'.pluralize # => "posts"
# 'octopus'.pluralize # => "octopi"
# 'sheep'.pluralize # => "sheep"
# 'words'.pluralize # => "words"
# 'the blue mailman'.pluralize # => "the blue mailmen"
# 'CamelOctopus'.pluralize # => "CamelOctopi"
# 'apple'.pluralize(1) # => "apple"
# 'apple'.pluralize(2) # => "apples"
# 'ley'.pluralize(:es) # => "leyes"
# 'ley'.pluralize(1, :es) # => "ley"
- 1
def pluralize(count = nil, locale = :en)
locale = count if count.is_a?(Symbol)
if count == 1
dup
else
ActiveSupport::Inflector.pluralize(self, locale)
end
end
# The reverse of +pluralize+, returns the singular form of a word in a string.
#
# If the optional parameter +locale+ is specified,
# the word will be singularized as a word of that language.
# By default, this parameter is set to <tt>:en</tt>.
# You must define your own inflection rules for languages other than English.
#
# 'posts'.singularize # => "post"
# 'octopi'.singularize # => "octopus"
# 'sheep'.singularize # => "sheep"
# 'word'.singularize # => "word"
# 'the blue mailmen'.singularize # => "the blue mailman"
# 'CamelOctopi'.singularize # => "CamelOctopus"
# 'leyes'.singularize(:es) # => "ley"
- 1
def singularize(locale = :en)
ActiveSupport::Inflector.singularize(self, locale)
end
# +constantize+ tries to find a declared constant with the name specified
# in the string. It raises a NameError when the name is not in CamelCase
# or is not initialized. See ActiveSupport::Inflector.constantize
#
# 'Module'.constantize # => Module
# 'Class'.constantize # => Class
# 'blargle'.constantize # => NameError: wrong constant name blargle
- 1
def constantize
ActiveSupport::Inflector.constantize(self)
end
# +safe_constantize+ tries to find a declared constant with the name specified
# in the string. It returns +nil+ when the name is not in CamelCase
# or is not initialized. See ActiveSupport::Inflector.safe_constantize
#
# 'Module'.safe_constantize # => Module
# 'Class'.safe_constantize # => Class
# 'blargle'.safe_constantize # => nil
- 1
def safe_constantize
ActiveSupport::Inflector.safe_constantize(self)
end
# By default, +camelize+ converts strings to UpperCamelCase. If the argument to camelize
# is set to <tt>:lower</tt> then camelize produces lowerCamelCase.
#
# +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
#
# 'active_record'.camelize # => "ActiveRecord"
# 'active_record'.camelize(:lower) # => "activeRecord"
# 'active_record/errors'.camelize # => "ActiveRecord::Errors"
# 'active_record/errors'.camelize(:lower) # => "activeRecord::Errors"
- 1
def camelize(first_letter = :upper)
case first_letter
when :upper
ActiveSupport::Inflector.camelize(self, true)
when :lower
ActiveSupport::Inflector.camelize(self, false)
end
end
- 1
alias_method :camelcase, :camelize
# Capitalizes all the words and replaces some characters in the string to create
# a nicer looking title. +titleize+ is meant for creating pretty output. It is not
# used in the Rails internals.
#
# +titleize+ is also aliased as +titlecase+.
#
# 'man from the boondocks'.titleize # => "Man From The Boondocks"
# 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
- 1
def titleize
ActiveSupport::Inflector.titleize(self)
end
- 1
alias_method :titlecase, :titleize
# The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string.
#
# +underscore+ will also change '::' to '/' to convert namespaces to paths.
#
# 'ActiveModel'.underscore # => "active_model"
# 'ActiveModel::Errors'.underscore # => "active_model/errors"
- 1
def underscore
ActiveSupport::Inflector.underscore(self)
end
# Replaces underscores with dashes in the string.
#
# 'puni_puni'.dasherize # => "puni-puni"
- 1
def dasherize
ActiveSupport::Inflector.dasherize(self)
end
# Removes the module part from the constant expression in the string.
#
# 'ActiveSupport::Inflector::Inflections'.demodulize # => "Inflections"
# 'Inflections'.demodulize # => "Inflections"
# '::Inflections'.demodulize # => "Inflections"
# ''.demodulize # => ''
#
# See also +deconstantize+.
- 1
def demodulize
ActiveSupport::Inflector.demodulize(self)
end
# Removes the rightmost segment from the constant expression in the string.
#
# 'Net::HTTP'.deconstantize # => "Net"
# '::Net::HTTP'.deconstantize # => "::Net"
# 'String'.deconstantize # => ""
# '::String'.deconstantize # => ""
# ''.deconstantize # => ""
#
# See also +demodulize+.
- 1
def deconstantize
ActiveSupport::Inflector.deconstantize(self)
end
# Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
#
# class Person
# def to_param
# "#{id}-#{name.parameterize}"
# end
# end
#
# @person = Person.find(1)
# # => #<Person id: 1, name: "Donald E. Knuth">
#
# <%= link_to(@person.name, person_path) %>
# # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
#
# To preserve the case of the characters in a string, use the `preserve_case` argument.
#
# class Person
# def to_param
# "#{id}-#{name.parameterize(preserve_case: true)}"
# end
# end
#
# @person = Person.find(1)
# # => #<Person id: 1, name: "Donald E. Knuth">
#
# <%= link_to(@person.name, person_path) %>
# # => <a href="/person/1-Donald-E-Knuth">Donald E. Knuth</a>
- 1
def parameterize(separator: "-", preserve_case: false)
ActiveSupport::Inflector.parameterize(self, separator: separator, preserve_case: preserve_case)
end
# Creates the name of a table like Rails does for models to table names. This method
# uses the +pluralize+ method on the last word in the string.
#
# 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
# 'ham_and_egg'.tableize # => "ham_and_eggs"
# 'fancyCategory'.tableize # => "fancy_categories"
- 1
def tableize
ActiveSupport::Inflector.tableize(self)
end
# Creates a class name from a plural table name like Rails does for table names to models.
# Note that this returns a string and not a class. (To convert to an actual class
# follow +classify+ with +constantize+.)
#
# 'ham_and_eggs'.classify # => "HamAndEgg"
# 'posts'.classify # => "Post"
- 1
def classify
ActiveSupport::Inflector.classify(self)
end
# Capitalizes the first word, turns underscores into spaces, and strips a
# trailing '_id' if present.
# Like +titleize+, this is meant for creating pretty output.
#
# The capitalization of the first word can be turned off by setting the
# optional parameter +capitalize+ to false.
# By default, this parameter is true.
#
# 'employee_salary'.humanize # => "Employee salary"
# 'author_id'.humanize # => "Author"
# 'author_id'.humanize(capitalize: false) # => "author"
# '_id'.humanize # => "Id"
- 1
def humanize(options = {})
ActiveSupport::Inflector.humanize(self, options)
end
# Converts just the first character to uppercase.
#
# 'what a Lovely Day'.upcase_first # => "What a Lovely Day"
# 'w'.upcase_first # => "W"
# ''.upcase_first # => ""
- 1
def upcase_first
ActiveSupport::Inflector.upcase_first(self)
end
# Creates a foreign key name from a class name.
# +separate_class_name_and_id_with_underscore+ sets whether
# the method should put '_' between the name and 'id'.
#
# 'Message'.foreign_key # => "message_id"
# 'Message'.foreign_key(false) # => "messageid"
# 'Admin::Post'.foreign_key # => "post_id"
- 1
def foreign_key(separate_class_name_and_id_with_underscore = true)
ActiveSupport::Inflector.foreign_key(self, separate_class_name_and_id_with_underscore)
end
end
- 1
require "active_support/multibyte"
- 1
class String
# == Multibyte proxy
#
# +mb_chars+ is a multibyte safe proxy for string methods.
#
# It creates and returns an instance of the ActiveSupport::Multibyte::Chars class which
# encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy
# class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsulated string.
#
# >> "lj".upcase
# => "lj"
# >> "lj".mb_chars.upcase.to_s
# => "LJ"
#
# == Method chaining
#
# All the methods on the Chars proxy which normally return a string will return a Chars object. This allows
# method chaining on the result of any of these methods.
#
# name.mb_chars.reverse.length # => 12
#
# == Interoperability and configuration
#
# The Chars object tries to be as interchangeable with String objects as possible: sorting and comparing between
# String and Char work like expected. The bang! methods change the internal string representation in the Chars
# object. Interoperability problems can be resolved easily with a +to_s+ call.
#
# For more information about the methods defined on the Chars proxy see ActiveSupport::Multibyte::Chars. For
# information about how to change the default Multibyte behavior see ActiveSupport::Multibyte.
- 1
def mb_chars
ActiveSupport::Multibyte.proxy_class.new(self)
end
# Returns +true+ if string has utf_8 encoding.
#
# utf_8_str = "some string".encode "UTF-8"
# iso_str = "some string".encode "ISO-8859-1"
#
# utf_8_str.is_utf8? # => true
# iso_str.is_utf8? # => false
- 1
def is_utf8?
case encoding
when Encoding::UTF_8
valid_encoding?
when Encoding::ASCII_8BIT, Encoding::US_ASCII
dup.force_encoding(Encoding::UTF_8).valid_encoding?
else
false
end
end
end
- 1
class String
# Strips indentation in heredocs.
#
# For example in
#
# if options[:usage]
# puts <<-USAGE.strip_heredoc
# This command does such and such.
#
# Supported options are:
# -h This message
# ...
# USAGE
# end
#
# the user would see the usage message aligned against the left margin.
#
# Technically, it looks for the least indented non-empty line
# in the whole string, and removes that amount of leading whitespace.
- 1
def strip_heredoc
gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, "".freeze)
end
end
- 1
require "active_support/inflector/methods"
- 1
module ActiveSupport
# Autoload and eager load conveniences for your library.
#
# This module allows you to define autoloads based on
# Rails conventions (i.e. no need to define the path
# it is automatically guessed based on the filename)
# and also define a set of constants that needs to be
# eager loaded:
#
# module MyLib
# extend ActiveSupport::Autoload
#
# autoload :Model
#
# eager_autoload do
# autoload :Cache
# end
# end
#
# Then your library can be eager loaded by simply calling:
#
# MyLib.eager_load!
- 1
module Autoload
- 1
def self.extended(base) # :nodoc:
- 1
base.class_eval do
- 1
@_autoloads = {}
- 1
@_under_path = nil
- 1
@_at_path = nil
- 1
@_eager_autoload = false
end
end
- 1
def autoload(const_name, path = @_at_path)
- 35
unless path
- 34
full = [name, @_under_path, const_name.to_s].compact.join("::")
- 34
path = Inflector.underscore(full)
end
- 35
if @_eager_autoload
- 22
@_autoloads[const_name] = path
end
- 35
super const_name, path
end
- 1
def autoload_under(path)
@_under_path, old_path = path, @_under_path
yield
ensure
@_under_path = old_path
end
- 1
def autoload_at(path)
@_at_path, old_path = path, @_at_path
yield
ensure
@_at_path = old_path
end
- 1
def eager_autoload
- 1
old_eager, @_eager_autoload = @_eager_autoload, true
- 1
yield
ensure
- 1
@_eager_autoload = old_eager
end
- 1
def eager_load!
@_autoloads.each_value { |file| require file }
end
- 1
def autoloads
@_autoloads
end
end
end
- 1
require "singleton"
- 1
module ActiveSupport
# \Deprecation specifies the API used by Rails to deprecate methods, instance
# variables, objects and constants.
- 1
class Deprecation
# active_support.rb sets an autoload for ActiveSupport::Deprecation.
#
# If these requires were at the top of the file the constant would not be
# defined by the time their files were loaded. Since some of them reopen
# ActiveSupport::Deprecation its autoload would be triggered, resulting in
# a circular require warning for active_support/deprecation.rb.
#
# So, we define the constant first, and load dependencies later.
- 1
require "active_support/deprecation/instance_delegator"
- 1
require "active_support/deprecation/behaviors"
- 1
require "active_support/deprecation/reporting"
- 1
require "active_support/deprecation/method_wrappers"
- 1
require "active_support/deprecation/proxy_wrappers"
- 1
require "active_support/core_ext/module/deprecation"
- 1
include Singleton
- 1
include InstanceDelegator
- 1
include Behavior
- 1
include Reporting
- 1
include MethodWrapper
# The version number in which the deprecated behavior will be removed, by default.
- 1
attr_accessor :deprecation_horizon
# It accepts two parameters on initialization. The first is a version of library
# and the second is a library name
#
# ActiveSupport::Deprecation.new('2.0', 'MyLibrary')
- 1
def initialize(deprecation_horizon = "5.2", gem_name = "Rails")
- 1
self.gem_name = gem_name
- 1
self.deprecation_horizon = deprecation_horizon
# By default, warnings are not silenced and debugging is off.
- 1
self.silenced = false
- 1
self.debug = false
end
end
end
- 1
require "active_support/notifications"
- 1
module ActiveSupport
# Raised when <tt>ActiveSupport::Deprecation::Behavior#behavior</tt> is set with <tt>:raise</tt>.
# You would set <tt>:raise</tt>, as a behavior to raise errors and proactively report exceptions from deprecations.
- 1
class DeprecationException < StandardError
end
- 1
class Deprecation
# Default warning behaviors per Rails.env.
- 1
DEFAULT_BEHAVIORS = {
raise: ->(message, callstack) {
e = DeprecationException.new(message)
e.set_backtrace(callstack.map(&:to_s))
raise e
},
stderr: ->(message, callstack) {
$stderr.puts(message)
$stderr.puts callstack.join("\n ") if debug
},
log: ->(message, callstack) {
logger =
if defined?(Rails.logger) && Rails.logger
Rails.logger
else
require "active_support/logger"
ActiveSupport::Logger.new($stderr)
end
logger.warn message
logger.debug callstack.join("\n ") if debug
},
notify: ->(message, callstack) {
ActiveSupport::Notifications.instrument("deprecation.rails",
message: message, callstack: callstack)
},
silence: ->(message, callstack) {},
}
# Behavior module allows to determine how to display deprecation messages.
# You can create a custom behavior or set any from the +DEFAULT_BEHAVIORS+
# constant. Available behaviors are:
#
# [+raise+] Raise <tt>ActiveSupport::DeprecationException</tt>.
# [+stderr+] Log all deprecation warnings to +$stderr+.
# [+log+] Log all deprecation warnings to +Rails.logger+.
# [+notify+] Use +ActiveSupport::Notifications+ to notify +deprecation.rails+.
# [+silence+] Do nothing.
#
# Setting behaviors only affects deprecations that happen after boot time.
# For more information you can read the documentation of the +behavior=+ method.
- 1
module Behavior
# Whether to print a backtrace along with the warning.
- 1
attr_accessor :debug
# Returns the current behavior or if one isn't set, defaults to +:stderr+.
- 1
def behavior
@behavior ||= [DEFAULT_BEHAVIORS[:stderr]]
end
# Sets the behavior to the specified value. Can be a single value, array,
# or an object that responds to +call+.
#
# Available behaviors:
#
# [+raise+] Raise <tt>ActiveSupport::DeprecationException</tt>.
# [+stderr+] Log all deprecation warnings to +$stderr+.
# [+log+] Log all deprecation warnings to +Rails.logger+.
# [+notify+] Use +ActiveSupport::Notifications+ to notify +deprecation.rails+.
# [+silence+] Do nothing.
#
# Setting behaviors only affects deprecations that happen after boot time.
# Deprecation warnings raised by gems are not affected by this setting
# because they happen before Rails boots up.
#
# ActiveSupport::Deprecation.behavior = :stderr
# ActiveSupport::Deprecation.behavior = [:stderr, :log]
# ActiveSupport::Deprecation.behavior = MyCustomHandler
# ActiveSupport::Deprecation.behavior = ->(message, callstack) {
# # custom stuff
# }
- 1
def behavior=(behavior)
@behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || b }
end
end
end
end
- 1
require "active_support/core_ext/kernel/singleton_class"
- 1
require "active_support/core_ext/module/delegation"
- 1
module ActiveSupport
- 1
class Deprecation
- 1
module InstanceDelegator # :nodoc:
- 1
def self.included(base)
- 1
base.extend(ClassMethods)
- 1
base.singleton_class.prepend(OverrideDelegators)
- 1
base.public_class_method :new
end
- 1
module ClassMethods # :nodoc:
- 1
def include(included_module)
- 15
included_module.instance_methods.each { |m| method_added(m) }
- 3
super
end
- 1
def method_added(method_name)
- 1
singleton_class.delegate(method_name, to: :instance)
end
end
- 1
module OverrideDelegators # :nodoc:
- 1
def warn(message = nil, callstack = nil)
callstack ||= caller_locations(2)
super
end
- 1
def deprecation_warning(deprecated_method_name, message = nil, caller_backtrace = nil)
caller_backtrace ||= caller_locations(2)
super
end
end
end
end
end
- 1
require "active_support/core_ext/module/aliasing"
- 1
require "active_support/core_ext/array/extract_options"
- 1
module ActiveSupport
- 1
class Deprecation
- 1
module MethodWrapper
# Declare that a method has been deprecated.
#
# module Fred
# extend self
#
# def aaa; end
# def bbb; end
# def ccc; end
# def ddd; end
# def eee; end
# end
#
# Using the default deprecator:
# ActiveSupport::Deprecation.deprecate_methods(Fred, :aaa, bbb: :zzz, ccc: 'use Bar#ccc instead')
# # => [:aaa, :bbb, :ccc]
#
# Fred.aaa
# # DEPRECATION WARNING: aaa is deprecated and will be removed from Rails 5.1. (called from irb_binding at (irb):10)
# # => nil
#
# Fred.bbb
# # DEPRECATION WARNING: bbb is deprecated and will be removed from Rails 5.1 (use zzz instead). (called from irb_binding at (irb):11)
# # => nil
#
# Fred.ccc
# # DEPRECATION WARNING: ccc is deprecated and will be removed from Rails 5.1 (use Bar#ccc instead). (called from irb_binding at (irb):12)
# # => nil
#
# Passing in a custom deprecator:
# custom_deprecator = ActiveSupport::Deprecation.new('next-release', 'MyGem')
# ActiveSupport::Deprecation.deprecate_methods(Fred, ddd: :zzz, deprecator: custom_deprecator)
# # => [:ddd]
#
# Fred.ddd
# DEPRECATION WARNING: ddd is deprecated and will be removed from MyGem next-release (use zzz instead). (called from irb_binding at (irb):15)
# # => nil
#
# Using a custom deprecator directly:
# custom_deprecator = ActiveSupport::Deprecation.new('next-release', 'MyGem')
# custom_deprecator.deprecate_methods(Fred, eee: :zzz)
# # => [:eee]
#
# Fred.eee
# DEPRECATION WARNING: eee is deprecated and will be removed from MyGem next-release (use zzz instead). (called from irb_binding at (irb):18)
# # => nil
- 1
def deprecate_methods(target_module, *method_names)
options = method_names.extract_options!
deprecator = options.delete(:deprecator) || self
method_names += options.keys
mod = Module.new do
method_names.each do |method_name|
define_method(method_name) do |*args, &block|
deprecator.deprecation_warning(method_name, options[method_name])
super(*args, &block)
end
end
end
target_module.prepend(mod)
end
end
end
end
- 1
require "active_support/inflector/methods"
- 1
require "active_support/core_ext/regexp"
- 1
module ActiveSupport
- 1
class Deprecation
- 1
class DeprecationProxy #:nodoc:
- 1
def self.new(*args, &block)
object = args.first
return object unless object
super
end
- 61
instance_methods.each { |m| undef_method m unless /^__|^object_id$/.match?(m) }
# Don't give a deprecation warning on inspect since test/unit and error
# logs rely on it for diagnostics.
- 1
def inspect
target.inspect
end
- 1
private
- 1
def method_missing(called, *args, &block)
warn caller_locations, called, args
target.__send__(called, *args, &block)
end
end
# DeprecatedObjectProxy transforms an object into a deprecated one. It
# takes an object, a deprecation message and optionally a deprecator. The
# deprecator defaults to +ActiveSupport::Deprecator+ if none is specified.
#
# deprecated_object = ActiveSupport::Deprecation::DeprecatedObjectProxy.new(Object.new, "This object is now deprecated")
# # => #<Object:0x007fb9b34c34b0>
#
# deprecated_object.to_s
# DEPRECATION WARNING: This object is now deprecated.
# (Backtrace)
# # => "#<Object:0x007fb9b34c34b0>"
- 1
class DeprecatedObjectProxy < DeprecationProxy
- 1
def initialize(object, message, deprecator = ActiveSupport::Deprecation.instance)
@object = object
@message = message
@deprecator = deprecator
end
- 1
private
- 1
def target
@object
end
- 1
def warn(callstack, called, args)
@deprecator.warn(@message, callstack)
end
end
# DeprecatedInstanceVariableProxy transforms an instance variable into a
# deprecated one. It takes an instance of a class, a method on that class
# and an instance variable. It optionally takes a deprecator as the last
# argument. The deprecator defaults to +ActiveSupport::Deprecator+ if none
# is specified.
#
# class Example
# def initialize
# @request = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :request, :@request)
# @_request = :special_request
# end
#
# def request
# @_request
# end
#
# def old_request
# @request
# end
# end
#
# example = Example.new
# # => #<Example:0x007fb9b31090b8 @_request=:special_request, @request=:special_request>
#
# example.old_request.to_s
# # => DEPRECATION WARNING: @request is deprecated! Call request.to_s instead of
# @request.to_s
# (Backtrace information…)
# "special_request"
#
# example.request.to_s
# # => "special_request"
- 1
class DeprecatedInstanceVariableProxy < DeprecationProxy
- 1
def initialize(instance, method, var = "@#{method}", deprecator = ActiveSupport::Deprecation.instance)
@instance = instance
@method = method
@var = var
@deprecator = deprecator
end
- 1
private
- 1
def target
@instance.__send__(@method)
end
- 1
def warn(callstack, called, args)
@deprecator.warn("#{@var} is deprecated! Call #{@method}.#{called} instead of #{@var}.#{called}. Args: #{args.inspect}", callstack)
end
end
# DeprecatedConstantProxy transforms a constant into a deprecated one. It
# takes the names of an old (deprecated) constant and of a new constant
# (both in string form) and optionally a deprecator. The deprecator defaults
# to +ActiveSupport::Deprecator+ if none is specified. The deprecated constant
# now returns the value of the new one.
#
# PLANETS = %w(mercury venus earth mars jupiter saturn uranus neptune pluto)
#
# (In a later update, the original implementation of `PLANETS` has been removed.)
#
# PLANETS_POST_2006 = %w(mercury venus earth mars jupiter saturn uranus neptune)
# PLANETS = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('PLANETS', 'PLANETS_POST_2006')
#
# PLANETS.map { |planet| planet.capitalize }
# # => DEPRECATION WARNING: PLANETS is deprecated! Use PLANETS_POST_2006 instead.
# (Backtrace information…)
# ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]
- 1
class DeprecatedConstantProxy < DeprecationProxy
- 1
def initialize(old_const, new_const, deprecator = ActiveSupport::Deprecation.instance, message: "#{old_const} is deprecated! Use #{new_const} instead.")
@old_const = old_const
@new_const = new_const
@deprecator = deprecator
@message = message
end
# Returns the class of the new constant.
#
# PLANETS_POST_2006 = %w(mercury venus earth mars jupiter saturn uranus neptune)
# PLANETS = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('PLANETS', 'PLANETS_POST_2006')
# PLANETS.class # => Array
- 1
def class
target.class
end
- 1
private
- 1
def target
ActiveSupport::Inflector.constantize(@new_const.to_s)
end
- 1
def warn(callstack, called, args)
@deprecator.warn(@message, callstack)
end
end
end
end
- 1
require "rbconfig"
- 1
module ActiveSupport
- 1
class Deprecation
- 1
module Reporting
# Whether to print a message (silent mode)
- 1
attr_accessor :silenced
# Name of gem where method is deprecated
- 1
attr_accessor :gem_name
# Outputs a deprecation warning to the output configured by
# <tt>ActiveSupport::Deprecation.behavior</tt>.
#
# ActiveSupport::Deprecation.warn('something broke!')
# # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)"
- 1
def warn(message = nil, callstack = nil)
return if silenced
callstack ||= caller_locations(2)
deprecation_message(callstack, message).tap do |m|
behavior.each { |b| b.call(m, callstack) }
end
end
# Silence deprecation warnings within the block.
#
# ActiveSupport::Deprecation.warn('something broke!')
# # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)"
#
# ActiveSupport::Deprecation.silence do
# ActiveSupport::Deprecation.warn('something broke!')
# end
# # => nil
- 1
def silence
old_silenced, @silenced = @silenced, true
yield
ensure
@silenced = old_silenced
end
- 1
def deprecation_warning(deprecated_method_name, message = nil, caller_backtrace = nil)
caller_backtrace ||= caller_locations(2)
deprecated_method_warning(deprecated_method_name, message).tap do |msg|
warn(msg, caller_backtrace)
end
end
- 1
private
# Outputs a deprecation warning message
#
# ActiveSupport::Deprecation.deprecated_method_warning(:method_name)
# # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon}"
# ActiveSupport::Deprecation.deprecated_method_warning(:method_name, :another_method)
# # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon} (use another_method instead)"
# ActiveSupport::Deprecation.deprecated_method_warning(:method_name, "Optional message")
# # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon} (Optional message)"
- 1
def deprecated_method_warning(method_name, message = nil)
warning = "#{method_name} is deprecated and will be removed from #{gem_name} #{deprecation_horizon}"
case message
when Symbol then "#{warning} (use #{message} instead)"
when String then "#{warning} (#{message})"
else warning
end
end
- 1
def deprecation_message(callstack, message = nil)
message ||= "You are using deprecated behavior which will be removed from the next major or minor release."
"DEPRECATION WARNING: #{message} #{deprecation_caller_message(callstack)}"
end
- 1
def deprecation_caller_message(callstack)
file, line, method = extract_callstack(callstack)
if file
if line && method
"(called from #{method} at #{file}:#{line})"
else
"(called from #{file}:#{line})"
end
end
end
- 1
def extract_callstack(callstack)
return _extract_callstack(callstack) if callstack.first.is_a? String
offending_line = callstack.find { |frame|
frame.absolute_path && !ignored_callstack(frame.absolute_path)
} || callstack.first
[offending_line.path, offending_line.lineno, offending_line.label]
end
- 1
def _extract_callstack(callstack)
warn "Please pass `caller_locations` to the deprecation API" if $VERBOSE
offending_line = callstack.find { |line| !ignored_callstack(line) } || callstack.first
if offending_line
if md = offending_line.match(/^(.+?):(\d+)(?::in `(.*?)')?/)
md.captures
else
offending_line
end
end
end
- 1
RAILS_GEM_ROOT = File.expand_path("../../../../..", __FILE__) + "/"
- 1
def ignored_callstack(path)
path.start_with?(RAILS_GEM_ROOT) || path.start_with?(RbConfig::CONFIG["rubylibdir"])
end
end
end
end
- 1
module ActiveSupport
# This module provides an internal implementation to track descendants
# which is faster than iterating through ObjectSpace.
- 1
module DescendantsTracker
- 1
@@direct_descendants = {}
- 1
class << self
- 1
def direct_descendants(klass)
@@direct_descendants[klass] || []
end
- 1
def descendants(klass)
arr = []
accumulate_descendants(klass, arr)
arr
end
- 1
def clear
if defined? ActiveSupport::Dependencies
@@direct_descendants.each do |klass, descendants|
if ActiveSupport::Dependencies.autoloaded?(klass)
@@direct_descendants.delete(klass)
else
descendants.reject! { |v| ActiveSupport::Dependencies.autoloaded?(v) }
end
end
else
@@direct_descendants.clear
end
end
# This is the only method that is not thread safe, but is only ever called
# during the eager loading phase.
- 1
def store_inherited(klass, descendant)
- 1
(@@direct_descendants[klass] ||= []) << descendant
end
- 1
private
- 1
def accumulate_descendants(klass, acc)
if direct_descendants = @@direct_descendants[klass]
acc.concat(direct_descendants)
direct_descendants.each { |direct_descendant| accumulate_descendants(direct_descendant, acc) }
end
end
end
- 1
def inherited(base)
- 1
DescendantsTracker.store_inherited(self, base)
- 1
super
end
- 1
def direct_descendants
DescendantsTracker.direct_descendants(self)
end
- 1
def descendants
DescendantsTracker.descendants(self)
end
end
end
- 1
module ActiveSupport
# Returns the version of the currently loaded Active Support as a <tt>Gem::Version</tt>.
- 1
def self.gem_version
Gem::Version.new VERSION::STRING
end
- 1
module VERSION
- 1
MAJOR = 5
- 1
MINOR = 1
- 1
TINY = 0
- 1
PRE = "alpha"
- 1
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
end
- 1
require "active_support/core_ext/hash/deep_merge"
- 1
require "active_support/core_ext/hash/except"
- 1
require "active_support/core_ext/hash/slice"
- 1
begin
- 1
require "i18n"
rescue LoadError => e
$stderr.puts "The i18n gem is not available. Please add it to your Gemfile and run bundle install"
raise e
end
- 1
require "active_support/lazy_load_hooks"
- 1
ActiveSupport.run_load_hooks(:i18n)
- 1
I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml"
- 1
require "active_support/inflector/inflections"
#--
# Defines the standard inflection rules. These are the starting point for
# new projects and are not considered complete. The current set of inflection
# rules is frozen. This means, we do not change them to become more complete.
# This is a safety measure to keep existing applications from breaking.
#++
- 1
module ActiveSupport
- 1
Inflector.inflections(:en) do |inflect|
- 1
inflect.plural(/$/, "s")
- 1
inflect.plural(/s$/i, "s")
- 1
inflect.plural(/^(ax|test)is$/i, '\1es')
- 1
inflect.plural(/(octop|vir)us$/i, '\1i')
- 1
inflect.plural(/(octop|vir)i$/i, '\1i')
- 1
inflect.plural(/(alias|status)$/i, '\1es')
- 1
inflect.plural(/(bu)s$/i, '\1ses')
- 1
inflect.plural(/(buffal|tomat)o$/i, '\1oes')
- 1
inflect.plural(/([ti])um$/i, '\1a')
- 1
inflect.plural(/([ti])a$/i, '\1a')
- 1
inflect.plural(/sis$/i, "ses")
- 1
inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
- 1
inflect.plural(/(hive)$/i, '\1s')
- 1
inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
- 1
inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
- 1
inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
- 1
inflect.plural(/^(m|l)ouse$/i, '\1ice')
- 1
inflect.plural(/^(m|l)ice$/i, '\1ice')
- 1
inflect.plural(/^(ox)$/i, '\1en')
- 1
inflect.plural(/^(oxen)$/i, '\1')
- 1
inflect.plural(/(quiz)$/i, '\1zes')
- 1
inflect.singular(/s$/i, "")
- 1
inflect.singular(/(ss)$/i, '\1')
- 1
inflect.singular(/(n)ews$/i, '\1ews')
- 1
inflect.singular(/([ti])a$/i, '\1um')
- 1
inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '\1sis')
- 1
inflect.singular(/(^analy)(sis|ses)$/i, '\1sis')
- 1
inflect.singular(/([^f])ves$/i, '\1fe')
- 1
inflect.singular(/(hive)s$/i, '\1')
- 1
inflect.singular(/(tive)s$/i, '\1')
- 1
inflect.singular(/([lr])ves$/i, '\1f')
- 1
inflect.singular(/([^aeiouy]|qu)ies$/i, '\1y')
- 1
inflect.singular(/(s)eries$/i, '\1eries')
- 1
inflect.singular(/(m)ovies$/i, '\1ovie')
- 1
inflect.singular(/(x|ch|ss|sh)es$/i, '\1')
- 1
inflect.singular(/^(m|l)ice$/i, '\1ouse')
- 1
inflect.singular(/(bus)(es)?$/i, '\1')
- 1
inflect.singular(/(o)es$/i, '\1')
- 1
inflect.singular(/(shoe)s$/i, '\1')
- 1
inflect.singular(/(cris|test)(is|es)$/i, '\1is')
- 1
inflect.singular(/^(a)x[ie]s$/i, '\1xis')
- 1
inflect.singular(/(octop|vir)(us|i)$/i, '\1us')
- 1
inflect.singular(/(alias|status)(es)?$/i, '\1')
- 1
inflect.singular(/^(ox)en/i, '\1')
- 1
inflect.singular(/(vert|ind)ices$/i, '\1ex')
- 1
inflect.singular(/(matr)ices$/i, '\1ix')
- 1
inflect.singular(/(quiz)zes$/i, '\1')
- 1
inflect.singular(/(database)s$/i, '\1')
- 1
inflect.irregular("person", "people")
- 1
inflect.irregular("man", "men")
- 1
inflect.irregular("child", "children")
- 1
inflect.irregular("sex", "sexes")
- 1
inflect.irregular("move", "moves")
- 1
inflect.irregular("zombie", "zombies")
- 1
inflect.uncountable(%w(equipment information rice money species series fish sheep jeans police))
end
end
# in case active_support/inflector is required without the rest of active_support
- 1
require "active_support/inflector/inflections"
- 1
require "active_support/inflector/transliterate"
- 1
require "active_support/inflector/methods"
- 1
require "active_support/inflections"
- 1
require "active_support/core_ext/string/inflections"
- 1
require "concurrent/map"
- 1
require "active_support/core_ext/array/prepend_and_append"
- 1
require "active_support/core_ext/regexp"
- 1
require "active_support/i18n"
- 1
module ActiveSupport
- 1
module Inflector
- 1
extend self
# A singleton instance of this class is yielded by Inflector.inflections,
# which can then be used to specify additional inflection rules. If passed
# an optional locale, rules for other languages can be specified. The
# default locale is <tt>:en</tt>. Only rules for English are provided.
#
# ActiveSupport::Inflector.inflections(:en) do |inflect|
# inflect.plural /^(ox)$/i, '\1\2en'
# inflect.singular /^(ox)en/i, '\1'
#
# inflect.irregular 'octopus', 'octopi'
#
# inflect.uncountable 'equipment'
# end
#
# New rules are added at the top. So in the example above, the irregular
# rule for octopus will now be the first of the pluralization and
# singularization rules that is runs. This guarantees that your rules run
# before any of the rules that may already have been loaded.
- 1
class Inflections
- 1
@__instance__ = Concurrent::Map.new
- 1
class Uncountables < Array
- 1
def initialize
- 1
@regex_array = []
- 1
super
end
- 1
def delete(entry)
- 84
super entry
- 84
@regex_array.delete(to_regex(entry))
end
- 1
def <<(*word)
add(word)
end
- 1
def add(words)
- 1
words = words.flatten.map(&:downcase)
- 1
concat(words)
- 11
@regex_array += words.map { |word| to_regex(word) }
- 1
self
end
- 1
def uncountable?(str)
@regex_array.any? { |regex| regex.match? str }
end
- 1
private
- 1
def to_regex(string)
- 94
/\b#{::Regexp.escape(string)}\Z/i
end
end
- 1
def self.instance(locale = :en)
- 35
@__instance__[locale] ||= new
end
- 1
attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms, :acronym_regex
- 1
def initialize
- 1
@plurals, @singulars, @uncountables, @humans, @acronyms, @acronym_regex = [], [], Uncountables.new, [], {}, /(?=a)b/
end
# Private, for the test suite.
- 1
def initialize_dup(orig) # :nodoc:
%w(plurals singulars uncountables humans acronyms acronym_regex).each do |scope|
instance_variable_set("@#{scope}", orig.send(scope).dup)
end
end
# Specifies a new acronym. An acronym must be specified as it will appear
# in a camelized string. An underscore string that contains the acronym
# will retain the acronym when passed to +camelize+, +humanize+, or
# +titleize+. A camelized string that contains the acronym will maintain
# the acronym when titleized or humanized, and will convert the acronym
# into a non-delimited single lowercase word when passed to +underscore+.
#
# acronym 'HTML'
# titleize 'html' # => 'HTML'
# camelize 'html' # => 'HTML'
# underscore 'MyHTML' # => 'my_html'
#
# The acronym, however, must occur as a delimited unit and not be part of
# another word for conversions to recognize it:
#
# acronym 'HTTP'
# camelize 'my_http_delimited' # => 'MyHTTPDelimited'
# camelize 'https' # => 'Https', not 'HTTPs'
# underscore 'HTTPS' # => 'http_s', not 'https'
#
# acronym 'HTTPS'
# camelize 'https' # => 'HTTPS'
# underscore 'HTTPS' # => 'https'
#
# Note: Acronyms that are passed to +pluralize+ will no longer be
# recognized, since the acronym will not occur as a delimited unit in the
# pluralized result. To work around this, you must specify the pluralized
# form as an acronym as well:
#
# acronym 'API'
# camelize(pluralize('api')) # => 'Apis'
#
# acronym 'APIs'
# camelize(pluralize('api')) # => 'APIs'
#
# +acronym+ may be used to specify any word that contains an acronym or
# otherwise needs to maintain a non-standard capitalization. The only
# restriction is that the word must begin with a capital letter.
#
# acronym 'RESTful'
# underscore 'RESTful' # => 'restful'
# underscore 'RESTfulController' # => 'restful_controller'
# titleize 'RESTfulController' # => 'RESTful Controller'
# camelize 'restful' # => 'RESTful'
# camelize 'restful_controller' # => 'RESTfulController'
#
# acronym 'McDonald'
# underscore 'McDonald' # => 'mcdonald'
# camelize 'mcdonald' # => 'McDonald'
- 1
def acronym(word)
@acronyms[word.downcase] = word
@acronym_regex = /#{@acronyms.values.join("|")}/
end
# Specifies a new pluralization rule and its replacement. The rule can
# either be a string or a regular expression. The replacement should
# always be a string that may include references to the matched data from
# the rule.
- 1
def plural(rule, replacement)
- 33
@uncountables.delete(rule) if rule.is_a?(String)
- 33
@uncountables.delete(replacement)
- 33
@plurals.prepend([rule, replacement])
end
# Specifies a new singularization rule and its replacement. The rule can
# either be a string or a regular expression. The replacement should
# always be a string that may include references to the matched data from
# the rule.
- 1
def singular(rule, replacement)
- 39
@uncountables.delete(rule) if rule.is_a?(String)
- 39
@uncountables.delete(replacement)
- 39
@singulars.prepend([rule, replacement])
end
# Specifies a new irregular that applies to both pluralization and
# singularization at the same time. This can only be used for strings, not
# regular expressions. You simply pass the irregular in singular and
# plural form.
#
# irregular 'octopus', 'octopi'
# irregular 'person', 'people'
- 1
def irregular(singular, plural)
- 6
@uncountables.delete(singular)
- 6
@uncountables.delete(plural)
- 6
s0 = singular[0]
- 6
srest = singular[1..-1]
- 6
p0 = plural[0]
- 6
prest = plural[1..-1]
- 6
if s0.upcase == p0.upcase
- 6
plural(/(#{s0})#{srest}$/i, '\1' + prest)
- 6
plural(/(#{p0})#{prest}$/i, '\1' + prest)
- 6
singular(/(#{s0})#{srest}$/i, '\1' + srest)
- 6
singular(/(#{p0})#{prest}$/i, '\1' + srest)
else
plural(/#{s0.upcase}(?i)#{srest}$/, p0.upcase + prest)
plural(/#{s0.downcase}(?i)#{srest}$/, p0.downcase + prest)
plural(/#{p0.upcase}(?i)#{prest}$/, p0.upcase + prest)
plural(/#{p0.downcase}(?i)#{prest}$/, p0.downcase + prest)
singular(/#{s0.upcase}(?i)#{srest}$/, s0.upcase + srest)
singular(/#{s0.downcase}(?i)#{srest}$/, s0.downcase + srest)
singular(/#{p0.upcase}(?i)#{prest}$/, s0.upcase + srest)
singular(/#{p0.downcase}(?i)#{prest}$/, s0.downcase + srest)
end
end
# Specifies words that are uncountable and should not be inflected.
#
# uncountable 'money'
# uncountable 'money', 'information'
# uncountable %w( money information rice )
- 1
def uncountable(*words)
- 1
@uncountables.add(words)
end
# Specifies a humanized form of a string by a regular expression rule or
# by a string mapping. When using a regular expression based replacement,
# the normal humanize formatting is called after the replacement. When a
# string is used, the human form should be specified as desired (example:
# 'The name', not 'the_name').
#
# human /_cnt$/i, '\1_count'
# human 'legacy_col_person_name', 'Name'
- 1
def human(rule, replacement)
@humans.prepend([rule, replacement])
end
# Clears the loaded inflections within a given scope (default is
# <tt>:all</tt>). Give the scope as a symbol of the inflection type, the
# options are: <tt>:plurals</tt>, <tt>:singulars</tt>, <tt>:uncountables</tt>,
# <tt>:humans</tt>.
#
# clear :all
# clear :plurals
- 1
def clear(scope = :all)
case scope
when :all
@plurals, @singulars, @uncountables, @humans = [], [], Uncountables.new, []
else
instance_variable_set "@#{scope}", []
end
end
end
# Yields a singleton instance of Inflector::Inflections so you can specify
# additional inflector rules. If passed an optional locale, rules for other
# languages can be specified. If not specified, defaults to <tt>:en</tt>.
# Only rules for English are provided.
#
# ActiveSupport::Inflector.inflections(:en) do |inflect|
# inflect.uncountable 'rails'
# end
- 1
def inflections(locale = :en)
- 35
if block_given?
- 1
yield Inflections.instance(locale)
else
- 34
Inflections.instance(locale)
end
end
end
end
- 1
require "active_support/inflections"
- 1
require "active_support/core_ext/regexp"
- 1
module ActiveSupport
# The Inflector transforms words from singular to plural, class names to table
# names, modularized class names to ones without, and class names to foreign
# keys. The default inflections for pluralization, singularization, and
# uncountable words are kept in inflections.rb.
#
# The Rails core team has stated patches for the inflections library will not
# be accepted in order to avoid breaking legacy applications which may be
# relying on errant inflections. If you discover an incorrect inflection and
# require it for your application or wish to define rules for languages other
# than English, please correct or add them yourself (explained below).
- 1
module Inflector
- 1
extend self
# Returns the plural form of the word in the string.
#
# If passed an optional +locale+ parameter, the word will be
# pluralized using rules defined for that language. By default,
# this parameter is set to <tt>:en</tt>.
#
# pluralize('post') # => "posts"
# pluralize('octopus') # => "octopi"
# pluralize('sheep') # => "sheep"
# pluralize('words') # => "words"
# pluralize('CamelOctopus') # => "CamelOctopi"
# pluralize('ley', :es) # => "leyes"
- 1
def pluralize(word, locale = :en)
apply_inflections(word, inflections(locale).plurals)
end
# The reverse of #pluralize, returns the singular form of a word in a
# string.
#
# If passed an optional +locale+ parameter, the word will be
# singularized using rules defined for that language. By default,
# this parameter is set to <tt>:en</tt>.
#
# singularize('posts') # => "post"
# singularize('octopi') # => "octopus"
# singularize('sheep') # => "sheep"
# singularize('word') # => "word"
# singularize('CamelOctopi') # => "CamelOctopus"
# singularize('leyes', :es) # => "ley"
- 1
def singularize(word, locale = :en)
apply_inflections(word, inflections(locale).singulars)
end
# Converts strings to UpperCamelCase.
# If the +uppercase_first_letter+ parameter is set to false, then produces
# lowerCamelCase.
#
# Also converts '/' to '::' which is useful for converting
# paths to namespaces.
#
# camelize('active_model') # => "ActiveModel"
# camelize('active_model', false) # => "activeModel"
# camelize('active_model/errors') # => "ActiveModel::Errors"
# camelize('active_model/errors', false) # => "activeModel::Errors"
#
# As a rule of thumb you can think of +camelize+ as the inverse of
# #underscore, though there are cases where that does not hold:
#
# camelize(underscore('SSLError')) # => "SslError"
- 1
def camelize(term, uppercase_first_letter = true)
string = term.to_s
if uppercase_first_letter
string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize }
else
string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { |match| match.downcase }
end
string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
string.gsub!("/".freeze, "::".freeze)
string
end
# Makes an underscored, lowercase form from the expression in the string.
#
# Changes '::' to '/' to convert namespaces to paths.
#
# underscore('ActiveModel') # => "active_model"
# underscore('ActiveModel::Errors') # => "active_model/errors"
#
# As a rule of thumb you can think of +underscore+ as the inverse of
# #camelize, though there are cases where that does not hold:
#
# camelize(underscore('SSLError')) # => "SslError"
- 1
def underscore(camel_cased_word)
- 34
return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word)
- 34
word = camel_cased_word.to_s.gsub("::".freeze, "/".freeze)
- 34
word.gsub!(/(?:(?<=([A-Za-z\d]))|\b)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1 && '_'.freeze }#{$2.downcase}" }
- 34
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'.freeze)
- 34
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2'.freeze)
- 34
word.tr!("-".freeze, "_".freeze)
- 34
word.downcase!
- 34
word
end
# Tweaks an attribute name for display to end users.
#
# Specifically, performs these transformations:
#
# * Applies human inflection rules to the argument.
# * Deletes leading underscores, if any.
# * Removes a "_id" suffix if present.
# * Replaces underscores with spaces, if any.
# * Downcases all words except acronyms.
# * Capitalizes the first word.
#
# The capitalization of the first word can be turned off by setting the
# +:capitalize+ option to false (default is true).
#
# humanize('employee_salary') # => "Employee salary"
# humanize('author_id') # => "Author"
# humanize('author_id', capitalize: false) # => "author"
# humanize('_id') # => "Id"
#
# If "SSL" was defined to be an acronym:
#
# humanize('ssl_error') # => "SSL error"
#
- 1
def humanize(lower_case_and_underscored_word, options = {})
result = lower_case_and_underscored_word.to_s.dup
inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
result.sub!(/\A_+/, "".freeze)
result.sub!(/_id\z/, "".freeze)
result.tr!("_".freeze, " ".freeze)
result.gsub!(/([a-z\d]*)/i) do |match|
"#{inflections.acronyms[match] || match.downcase}"
end
if options.fetch(:capitalize, true)
result.sub!(/\A\w/) { |match| match.upcase }
end
result
end
# Converts just the first character to uppercase.
#
# upcase_first('what a Lovely Day') # => "What a Lovely Day"
# upcase_first('w') # => "W"
# upcase_first('') # => ""
- 1
def upcase_first(string)
string.length > 0 ? string[0].upcase.concat(string[1..-1]) : ""
end
# Capitalizes all the words and replaces some characters in the string to
# create a nicer looking title. +titleize+ is meant for creating pretty
# output. It is not used in the Rails internals.
#
# +titleize+ is also aliased as +titlecase+.
#
# titleize('man from the boondocks') # => "Man From The Boondocks"
# titleize('x-men: the last stand') # => "X Men: The Last Stand"
# titleize('TheManWithoutAPast') # => "The Man Without A Past"
# titleize('raiders_of_the_lost_ark') # => "Raiders Of The Lost Ark"
- 1
def titleize(word)
humanize(underscore(word)).gsub(/\b(?<!['’`])[a-z]/) { |match| match.capitalize }
end
# Creates the name of a table like Rails does for models to table names.
# This method uses the #pluralize method on the last word in the string.
#
# tableize('RawScaledScorer') # => "raw_scaled_scorers"
# tableize('ham_and_egg') # => "ham_and_eggs"
# tableize('fancyCategory') # => "fancy_categories"
- 1
def tableize(class_name)
pluralize(underscore(class_name))
end
# Creates a class name from a plural table name like Rails does for table
# names to models. Note that this returns a string and not a Class (To
# convert to an actual class follow +classify+ with #constantize).
#
# classify('ham_and_eggs') # => "HamAndEgg"
# classify('posts') # => "Post"
#
# Singular names are not handled correctly:
#
# classify('calculus') # => "Calculus"
- 1
def classify(table_name)
# strip out any leading schema name
camelize(singularize(table_name.to_s.sub(/.*\./, "".freeze)))
end
# Replaces underscores with dashes in the string.
#
# dasherize('puni_puni') # => "puni-puni"
- 1
def dasherize(underscored_word)
underscored_word.tr("_".freeze, "-".freeze)
end
# Removes the module part from the expression in the string.
#
# demodulize('ActiveSupport::Inflector::Inflections') # => "Inflections"
# demodulize('Inflections') # => "Inflections"
# demodulize('::Inflections') # => "Inflections"
# demodulize('') # => ""
#
# See also #deconstantize.
- 1
def demodulize(path)
path = path.to_s
if i = path.rindex("::")
path[(i + 2)..-1]
else
path
end
end
# Removes the rightmost segment from the constant expression in the string.
#
# deconstantize('Net::HTTP') # => "Net"
# deconstantize('::Net::HTTP') # => "::Net"
# deconstantize('String') # => ""
# deconstantize('::String') # => ""
# deconstantize('') # => ""
#
# See also #demodulize.
- 1
def deconstantize(path)
path.to_s[0, path.rindex("::") || 0] # implementation based on the one in facets' Module#spacename
end
# Creates a foreign key name from a class name.
# +separate_class_name_and_id_with_underscore+ sets whether
# the method should put '_' between the name and 'id'.
#
# foreign_key('Message') # => "message_id"
# foreign_key('Message', false) # => "messageid"
# foreign_key('Admin::Post') # => "post_id"
- 1
def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
end
# Tries to find a constant with the name specified in the argument string.
#
# constantize('Module') # => Module
# constantize('Foo::Bar') # => Foo::Bar
#
# The name is assumed to be the one of a top-level constant, no matter
# whether it starts with "::" or not. No lexical context is taken into
# account:
#
# C = 'outside'
# module M
# C = 'inside'
# C # => 'inside'
# constantize('C') # => 'outside', same as ::C
# end
#
# NameError is raised when the name is not in CamelCase or the constant is
# unknown.
- 1
def constantize(camel_cased_word)
names = camel_cased_word.split("::".freeze)
# Trigger a built-in NameError exception including the ill-formed constant in the message.
Object.const_get(camel_cased_word) if names.empty?
# Remove the first blank element in case of '::ClassName' notation.
names.shift if names.size > 1 && names.first.empty?
names.inject(Object) do |constant, name|
if constant == Object
constant.const_get(name)
else
candidate = constant.const_get(name)
next candidate if constant.const_defined?(name, false)
next candidate unless Object.const_defined?(name)
# Go down the ancestors to check if it is owned directly. The check
# stops when we reach Object or the end of ancestors tree.
constant = constant.ancestors.inject(constant) do |const, ancestor|
break const if ancestor == Object
break ancestor if ancestor.const_defined?(name, false)
const
end
# owner is in Object, so raise
constant.const_get(name, false)
end
end
end
# Tries to find a constant with the name specified in the argument string.
#
# safe_constantize('Module') # => Module
# safe_constantize('Foo::Bar') # => Foo::Bar
#
# The name is assumed to be the one of a top-level constant, no matter
# whether it starts with "::" or not. No lexical context is taken into
# account:
#
# C = 'outside'
# module M
# C = 'inside'
# C # => 'inside'
# safe_constantize('C') # => 'outside', same as ::C
# end
#
# +nil+ is returned when the name is not in CamelCase or the constant (or
# part of it) is unknown.
#
# safe_constantize('blargle') # => nil
# safe_constantize('UnknownModule') # => nil
# safe_constantize('UnknownModule::Foo::Bar') # => nil
- 1
def safe_constantize(camel_cased_word)
constantize(camel_cased_word)
rescue NameError => e
raise if e.name && !(camel_cased_word.to_s.split("::").include?(e.name.to_s) ||
e.name.to_s == camel_cased_word.to_s)
rescue ArgumentError => e
raise unless /not missing constant #{const_regexp(camel_cased_word)}!$/.match?(e.message)
end
# Returns the suffix that should be added to a number to denote the position
# in an ordered sequence such as 1st, 2nd, 3rd, 4th.
#
# ordinal(1) # => "st"
# ordinal(2) # => "nd"
# ordinal(1002) # => "nd"
# ordinal(1003) # => "rd"
# ordinal(-11) # => "th"
# ordinal(-1021) # => "st"
- 1
def ordinal(number)
abs_number = number.to_i.abs
if (11..13).include?(abs_number % 100)
"th"
else
case abs_number % 10
when 1; "st"
when 2; "nd"
when 3; "rd"
else "th"
end
end
end
# Turns a number into an ordinal string used to denote the position in an
# ordered sequence such as 1st, 2nd, 3rd, 4th.
#
# ordinalize(1) # => "1st"
# ordinalize(2) # => "2nd"
# ordinalize(1002) # => "1002nd"
# ordinalize(1003) # => "1003rd"
# ordinalize(-11) # => "-11th"
# ordinalize(-1021) # => "-1021st"
- 1
def ordinalize(number)
"#{number}#{ordinal(number)}"
end
- 1
private
# Mounts a regular expression, returned as a string to ease interpolation,
# that will match part by part the given constant.
#
# const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?"
# const_regexp("::") # => "::"
- 1
def const_regexp(camel_cased_word)
parts = camel_cased_word.split("::".freeze)
return Regexp.escape(camel_cased_word) if parts.blank?
last = parts.pop
parts.reverse.inject(last) do |acc, part|
part.empty? ? acc : "#{part}(::#{acc})?"
end
end
# Applies inflection rules for +singularize+ and +pluralize+.
#
# apply_inflections('post', inflections.plurals) # => "posts"
# apply_inflections('posts', inflections.singulars) # => "post"
- 1
def apply_inflections(word, rules)
result = word.to_s.dup
if word.empty? || inflections.uncountables.uncountable?(result)
result
else
rules.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
result
end
end
end
end
- 1
require "active_support/core_ext/string/multibyte"
- 1
require "active_support/i18n"
- 1
module ActiveSupport
- 1
module Inflector
# Replaces non-ASCII characters with an ASCII approximation, or if none
# exists, a replacement character which defaults to "?".
#
# transliterate('Ærøskøbing')
# # => "AEroskobing"
#
# Default approximations are provided for Western/Latin characters,
# e.g, "ø", "ñ", "é", "ß", etc.
#
# This method is I18n aware, so you can set up custom approximations for a
# locale. This can be useful, for example, to transliterate German's "ü"
# and "ö" to "ue" and "oe", or to add support for transliterating Russian
# to ASCII.
#
# In order to make your custom transliterations available, you must set
# them as the <tt>i18n.transliterate.rule</tt> i18n key:
#
# # Store the transliterations in locales/de.yml
# i18n:
# transliterate:
# rule:
# ü: "ue"
# ö: "oe"
#
# # Or set them using Ruby
# I18n.backend.store_translations(:de, i18n: {
# transliterate: {
# rule: {
# 'ü' => 'ue',
# 'ö' => 'oe'
# }
# }
# })
#
# The value for <tt>i18n.transliterate.rule</tt> can be a simple Hash that
# maps characters to ASCII approximations as shown above, or, for more
# complex requirements, a Proc:
#
# I18n.backend.store_translations(:de, i18n: {
# transliterate: {
# rule: ->(string) { MyTransliterator.transliterate(string) }
# }
# })
#
# Now you can have different transliterations for each locale:
#
# I18n.locale = :en
# transliterate('Jürgen')
# # => "Jurgen"
#
# I18n.locale = :de
# transliterate('Jürgen')
# # => "Juergen"
- 1
def transliterate(string, replacement = "?".freeze)
raise ArgumentError, "Can only transliterate strings. Received #{string.class.name}" unless string.is_a?(String)
I18n.transliterate(ActiveSupport::Multibyte::Unicode.normalize(
ActiveSupport::Multibyte::Unicode.tidy_bytes(string), :c),
replacement: replacement)
end
# Replaces special characters in a string so that it may be used as part of
# a 'pretty' URL.
#
# parameterize("Donald E. Knuth") # => "donald-e-knuth"
# parameterize("^trés|Jolie-- ") # => "tres-jolie"
#
# To use a custom separator, override the `separator` argument.
#
# parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth"
# parameterize("^trés|Jolie-- ", separator: '_') # => "tres_jolie"
#
# To preserve the case of the characters in a string, use the `preserve_case` argument.
#
# parameterize("Donald E. Knuth", preserve_case: true) # => "Donald-E-Knuth"
# parameterize("^trés|Jolie-- ", preserve_case: true) # => "tres-Jolie"
#
- 1
def parameterize(string, separator: "-", preserve_case: false)
# Replace accented chars with their ASCII equivalents.
parameterized_string = transliterate(string)
# Turn unwanted chars into the separator.
parameterized_string.gsub!(/[^a-z0-9\-_]+/i, separator)
unless separator.nil? || separator.empty?
if separator == "-".freeze
re_duplicate_separator = /-{2,}/
re_leading_trailing_separator = /^-|-$/i
else
re_sep = Regexp.escape(separator)
re_duplicate_separator = /#{re_sep}{2,}/
re_leading_trailing_separator = /^#{re_sep}|#{re_sep}$/i
end
# No more than one of the separator in a row.
parameterized_string.gsub!(re_duplicate_separator, separator)
# Remove leading/trailing separator.
parameterized_string.gsub!(re_leading_trailing_separator, "".freeze)
end
parameterized_string.downcase! unless preserve_case
parameterized_string
end
end
end
- 1
module ActiveSupport
# lazy_load_hooks allows Rails to lazily load a lot of components and thus
# making the app boot faster. Because of this feature now there is no need to
# require <tt>ActiveRecord::Base</tt> at boot time purely to apply
# configuration. Instead a hook is registered that applies configuration once
# <tt>ActiveRecord::Base</tt> is loaded. Here <tt>ActiveRecord::Base</tt> is
# used as example but this feature can be applied elsewhere too.
#
# Here is an example where +on_load+ method is called to register a hook.
#
# initializer 'active_record.initialize_timezone' do
# ActiveSupport.on_load(:active_record) do
# self.time_zone_aware_attributes = true
# self.default_timezone = :utc
# end
# end
#
# When the entirety of +ActiveRecord::Base+ has been
# evaluated then +run_load_hooks+ is invoked. The very last line of
# +ActiveRecord::Base+ is:
#
# ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
- 1
module LazyLoadHooks
- 1
def self.extended(base) # :nodoc:
- 1
base.class_eval do
- 3
@load_hooks = Hash.new { |h, k| h[k] = [] }
- 3
@loaded = Hash.new { |h, k| h[k] = [] }
end
end
# Declares a block that will be executed when a Rails component is fully
# loaded.
- 1
def on_load(name, options = {}, &block)
@loaded[name].each do |base|
execute_hook(base, options, block)
end
@load_hooks[name] << [block, options]
end
- 1
def execute_hook(base, options, block)
if options[:yield]
block.call(base)
else
base.instance_eval(&block)
end
end
- 1
def run_load_hooks(name, base = Object)
- 2
@loaded[name] << base
- 2
@load_hooks[name].each do |hook, options|
execute_hook(base, options, hook)
end
end
end
- 1
extend LazyLoadHooks
end
- 1
require "active_support/logger_silence"
- 1
require "active_support/logger_thread_safe_level"
- 1
require "logger"
- 1
module ActiveSupport
- 1
class Logger < ::Logger
- 1
include ActiveSupport::LoggerThreadSafeLevel
- 1
include LoggerSilence
# Returns true if the logger destination matches one of the sources
#
# logger = Logger.new(STDOUT)
# ActiveSupport::Logger.logger_outputs_to?(logger, STDOUT)
# # => true
- 1
def self.logger_outputs_to?(logger, *sources)
logdev = logger.instance_variable_get("@logdev")
logger_source = logdev.dev if logdev.respond_to?(:dev)
sources.any? { |source| source == logger_source }
end
# Broadcasts logs to multiple loggers.
- 1
def self.broadcast(logger) # :nodoc:
Module.new do
define_method(:add) do |*args, &block|
logger.add(*args, &block)
super(*args, &block)
end
define_method(:<<) do |x|
logger << x
super(x)
end
define_method(:close) do
logger.close
super()
end
define_method(:progname=) do |name|
logger.progname = name
super(name)
end
define_method(:formatter=) do |formatter|
logger.formatter = formatter
super(formatter)
end
define_method(:level=) do |level|
logger.level = level
super(level)
end
define_method(:local_level=) do |level|
logger.local_level = level if logger.respond_to?(:local_level=)
super(level) if respond_to?(:local_level=)
end
define_method(:silence) do |level = Logger::ERROR, &block|
if logger.respond_to?(:silence)
logger.silence(level) do
if defined?(super)
super(level, &block)
else
block.call(self)
end
end
else
if defined?(super)
super(level, &block)
else
block.call(self)
end
end
end
end
end
- 1
def initialize(*args)
super
@formatter = SimpleFormatter.new
after_initialize if respond_to? :after_initialize
end
- 1
def add(severity, message = nil, progname = nil, &block)
return true if @logdev.nil? || (severity || UNKNOWN) < level
super
end
- 1
Logger::Severity.constants.each do |severity|
- 6
class_eval(<<-EOT, __FILE__, __LINE__ + 1)
- 1
def #{severity.downcase}? # def debug?
Logger::#{severity} >= level # DEBUG >= level
end # end
EOT
end
# Simple formatter which only displays the message.
- 1
class SimpleFormatter < ::Logger::Formatter
# This method is invoked when a log event occurs
- 1
def call(severity, timestamp, progname, msg)
"#{String === msg ? msg : msg.inspect}\n"
end
end
end
end
- 1
require "active_support/concern"
- 1
require "active_support/core_ext/module/attribute_accessors"
- 1
require "concurrent"
- 1
module LoggerSilence
- 1
extend ActiveSupport::Concern
- 1
included do
- 1
cattr_accessor :silencer
- 1
self.silencer = true
end
# Silences the logger for the duration of the block.
- 1
def silence(temporary_level = Logger::ERROR)
if silencer
begin
old_local_level = local_level
self.local_level = temporary_level
yield self
ensure
self.local_level = old_local_level
end
else
yield self
end
end
end
- 1
require "active_support/concern"
- 1
module ActiveSupport
- 1
module LoggerThreadSafeLevel # :nodoc:
- 1
extend ActiveSupport::Concern
- 1
def after_initialize
@local_levels = Concurrent::Map.new(initial_capacity: 2)
end
- 1
def local_log_id
Thread.current.__id__
end
- 1
def local_level
@local_levels[local_log_id]
end
- 1
def local_level=(level)
if level
@local_levels[local_log_id] = level
else
@local_levels.delete(local_log_id)
end
end
- 1
def level
local_level || super
end
end
end
- 1
module ActiveSupport #:nodoc:
- 1
module Multibyte
- 1
autoload :Chars, "active_support/multibyte/chars"
- 1
autoload :Unicode, "active_support/multibyte/unicode"
# The proxy class returned when calling mb_chars. You can use this accessor
# to configure your own proxy class so you can support other encodings. See
# the ActiveSupport::Multibyte::Chars implementation for an example how to
# do this.
#
# ActiveSupport::Multibyte.proxy_class = CharsForUTF32
- 1
def self.proxy_class=(klass)
@proxy_class = klass
end
# Returns the current proxy class.
- 1
def self.proxy_class
@proxy_class ||= ActiveSupport::Multibyte::Chars
end
end
end
- 1
module ActiveSupport
- 1
module Multibyte
- 1
module Unicode
- 1
extend self
# A list of all available normalization forms.
# See http://www.unicode.org/reports/tr15/tr15-29.html for more
# information about normalization.
- 1
NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
# The Unicode version that is supported by the implementation
- 1
UNICODE_VERSION = "9.0.0"
# The default normalization used for operations that require
# normalization. It can be set to any of the normalizations
# in NORMALIZATION_FORMS.
#
# ActiveSupport::Multibyte::Unicode.default_normalization_form = :c
- 1
attr_accessor :default_normalization_form
- 1
@default_normalization_form = :kc
# Hangul character boundaries and properties
- 1
HANGUL_SBASE = 0xAC00
- 1
HANGUL_LBASE = 0x1100
- 1
HANGUL_VBASE = 0x1161
- 1
HANGUL_TBASE = 0x11A7
- 1
HANGUL_LCOUNT = 19
- 1
HANGUL_VCOUNT = 21
- 1
HANGUL_TCOUNT = 28
- 1
HANGUL_NCOUNT = HANGUL_VCOUNT * HANGUL_TCOUNT
- 1
HANGUL_SCOUNT = 11172
- 1
HANGUL_SLAST = HANGUL_SBASE + HANGUL_SCOUNT
# Detect whether the codepoint is in a certain character class. Returns
# +true+ when it's in the specified character class and +false+ otherwise.
# Valid character classes are: <tt>:cr</tt>, <tt>:lf</tt>, <tt>:l</tt>,
# <tt>:v</tt>, <tt>:lv</tt>, <tt>:lvt</tt> and <tt>:t</tt>.
#
# Primarily used by the grapheme cluster support.
- 1
def in_char_class?(codepoint, classes)
- 18487
classes.detect { |c| database.boundary[c] === codepoint } ? true : false
end
# Unpack the string at grapheme boundaries. Returns a list of character
# lists.
#
# Unicode.unpack_graphemes('क्षि') # => [[2325, 2381], [2359], [2367]]
# Unicode.unpack_graphemes('Café') # => [[67], [97], [102], [233]]
- 1
def unpack_graphemes(string)
- 744
codepoints = string.codepoints.to_a
- 744
unpacked = []
- 744
pos = 0
- 744
marker = 0
- 744
eoc = codepoints.length
- 3366
while (pos < eoc)
- 1878
pos += 1
- 1878
previous = codepoints[pos - 1]
- 1878
current = codepoints[pos]
# See http://unicode.org/reports/tr29/#Grapheme_Cluster_Boundary_Rules
- 1878
should_break =
- 1878
if pos == eoc
- 744
true
# GB3. CR X LF
- 1134
elsif previous == database.boundary[:cr] && current == database.boundary[:lf]
- 2
false
# GB4. (Control|CR|LF) ÷
- 1132
elsif previous && in_char_class?(previous, [:control, :cr, :lf])
- 115
true
# GB5. ÷ (Control|CR|LF)
- 1017
elsif in_char_class?(current, [:control, :cr, :lf])
- 106
true
# GB6. L X (L|V|LV|LVT)
- 911
elsif database.boundary[:l] === previous && in_char_class?(current, [:l, :v, :lv, :lvt])
- 5
false
# GB7. (LV|V) X (V|T)
- 906
elsif in_char_class?(previous, [:lv, :v]) && in_char_class?(current, [:v, :t])
- 5
false
# GB8. (LVT|T) X (T)
- 901
elsif in_char_class?(previous, [:lvt, :t]) && database.boundary[:t] === current
- 3
false
# GB9. X (Extend | ZWJ)
- 898
elsif in_char_class?(current, [:extend, :zwj])
- 381
false
# GB9a. X SpacingMark
- 517
elsif database.boundary[:spacingmark] === current
- 36
false
# GB9b. Prepend X
- 481
elsif database.boundary[:prepend] === previous
- 14
false
# GB10. (E_Base | EBG) Extend* X E_Modifier
- 1159
elsif (marker...pos).any? { |i| in_char_class?(codepoints[i], [:e_base, :e_base_gaz]) && codepoints[i + 1...pos].all? { |c| database.boundary[:extend] === c } } && database.boundary[:e_modifier] === current
- 7
false
# GB11. ZWJ X (Glue_After_Zwj | EBG)
- 460
elsif database.boundary[:zwj] === previous && in_char_class?(current, [:glue_after_zwj, :e_base_gaz])
- 5
false
# GB12. ^ (RI RI)* RI X RI
# GB13. [^RI] (RI RI)* RI X RI
- 981
elsif codepoints[marker..pos].all? { |c| database.boundary[:regional_indicator] === c } && codepoints[marker..pos].count { |c| database.boundary[:regional_indicator] === c }.even?
- 7
false
# GB999. Any ÷ Any
else
- 448
true
end
- 1878
if should_break
- 1413
unpacked << codepoints[marker..pos - 1]
- 1413
marker = pos
end
end
- 744
unpacked
end
# Reverse operation of unpack_graphemes.
#
# Unicode.pack_graphemes(Unicode.unpack_graphemes('क्षि')) # => 'क्षि'
- 1
def pack_graphemes(unpacked)
- 744
unpacked.flatten.pack("U*")
end
# Re-order codepoints so the string becomes canonical.
- 1
def reorder_characters(codepoints)
length = codepoints.length - 1
pos = 0
while pos < length do
cp1, cp2 = database.codepoints[codepoints[pos]], database.codepoints[codepoints[pos + 1]]
if (cp1.combining_class > cp2.combining_class) && (cp2.combining_class > 0)
codepoints[pos..pos + 1] = cp2.code, cp1.code
pos += (pos > 0 ? -1 : 1)
else
pos += 1
end
end
codepoints
end
# Decompose composed characters to the decomposed form.
- 1
def decompose(type, codepoints)
codepoints.inject([]) do |decomposed, cp|
# if it's a hangul syllable starter character
if HANGUL_SBASE <= cp && cp < HANGUL_SLAST
sindex = cp - HANGUL_SBASE
ncp = [] # new codepoints
ncp << HANGUL_LBASE + sindex / HANGUL_NCOUNT
ncp << HANGUL_VBASE + (sindex % HANGUL_NCOUNT) / HANGUL_TCOUNT
tindex = sindex % HANGUL_TCOUNT
ncp << (HANGUL_TBASE + tindex) unless tindex == 0
decomposed.concat ncp
# if the codepoint is decomposable in with the current decomposition type
elsif (ncp = database.codepoints[cp].decomp_mapping) && (!database.codepoints[cp].decomp_type || type == :compatibility)
decomposed.concat decompose(type, ncp.dup)
else
decomposed << cp
end
end
end
# Compose decomposed characters to the composed form.
- 1
def compose(codepoints)
pos = 0
eoa = codepoints.length - 1
starter_pos = 0
starter_char = codepoints[0]
previous_combining_class = -1
while pos < eoa
pos += 1
lindex = starter_char - HANGUL_LBASE
# -- Hangul
if 0 <= lindex && lindex < HANGUL_LCOUNT
vindex = codepoints[starter_pos + 1] - HANGUL_VBASE rescue vindex = -1
if 0 <= vindex && vindex < HANGUL_VCOUNT
tindex = codepoints[starter_pos + 2] - HANGUL_TBASE rescue tindex = -1
if 0 <= tindex && tindex < HANGUL_TCOUNT
j = starter_pos + 2
eoa -= 2
else
tindex = 0
j = starter_pos + 1
eoa -= 1
end
codepoints[starter_pos..j] = (lindex * HANGUL_VCOUNT + vindex) * HANGUL_TCOUNT + tindex + HANGUL_SBASE
end
starter_pos += 1
starter_char = codepoints[starter_pos]
# -- Other characters
else
current_char = codepoints[pos]
current = database.codepoints[current_char]
if current.combining_class > previous_combining_class
if ref = database.composition_map[starter_char]
composition = ref[current_char]
else
composition = nil
end
unless composition.nil?
codepoints[starter_pos] = composition
starter_char = composition
codepoints.delete_at pos
eoa -= 1
pos -= 1
previous_combining_class = -1
else
previous_combining_class = current.combining_class
end
else
previous_combining_class = current.combining_class
end
if current.combining_class == 0
starter_pos = pos
starter_char = codepoints[pos]
end
end
end
codepoints
end
# Rubinius' String#scrub, however, doesn't support ASCII-incompatible chars.
- 1
if !defined?(Rubinius)
# Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
# resulting in a valid UTF-8 string.
#
# Passing +true+ will forcibly tidy all bytes, assuming that the string's
# encoding is entirely CP1252 or ISO-8859-1.
- 1
def tidy_bytes(string, force = false)
return string if string.empty?
return recode_windows1252_chars(string) if force
string.scrub { |bad| recode_windows1252_chars(bad) }
end
else
def tidy_bytes(string, force = false)
return string if string.empty?
return recode_windows1252_chars(string) if force
# We can't transcode to the same format, so we choose a nearly-identical encoding.
# We're going to 'transcode' bytes from UTF-8 when possible, then fall back to
# CP1252 when we get errors. The final string will be 'converted' back to UTF-8
# before returning.
reader = Encoding::Converter.new(Encoding::UTF_8, Encoding::UTF_16LE)
source = string.dup
out = "".force_encoding(Encoding::UTF_16LE)
loop do
reader.primitive_convert(source, out)
_, _, _, error_bytes, _ = reader.primitive_errinfo
break if error_bytes.nil?
out << error_bytes.encode(Encoding::UTF_16LE, Encoding::Windows_1252, invalid: :replace, undef: :replace)
end
reader.finish
out.encode!(Encoding::UTF_8)
end
end
# Returns the KC normalization of the string by default. NFKC is
# considered the best normalization form for passing strings to databases
# and validations.
#
# * <tt>string</tt> - The string to perform normalization on.
# * <tt>form</tt> - The form you want to normalize in. Should be one of
# the following: <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>.
# Default is ActiveSupport::Multibyte::Unicode.default_normalization_form.
- 1
def normalize(string, form = nil)
form ||= @default_normalization_form
# See http://www.unicode.org/reports/tr15, Table 1
codepoints = string.codepoints.to_a
case form
when :d
reorder_characters(decompose(:canonical, codepoints))
when :c
compose(reorder_characters(decompose(:canonical, codepoints)))
when :kd
reorder_characters(decompose(:compatibility, codepoints))
when :kc
compose(reorder_characters(decompose(:compatibility, codepoints)))
else
raise ArgumentError, "#{form} is not a valid normalization variant", caller
end.pack("U*".freeze)
end
- 1
def downcase(string)
apply_mapping string, :lowercase_mapping
end
- 1
def upcase(string)
apply_mapping string, :uppercase_mapping
end
- 1
def swapcase(string)
apply_mapping string, :swapcase_mapping
end
# Holds data about a codepoint in the Unicode database.
- 1
class Codepoint
- 1
attr_accessor :code, :combining_class, :decomp_type, :decomp_mapping, :uppercase_mapping, :lowercase_mapping
# Initializing Codepoint object with default values
- 1
def initialize
- 1
@combining_class = 0
- 1
@uppercase_mapping = 0
- 1
@lowercase_mapping = 0
end
- 1
def swapcase_mapping
uppercase_mapping > 0 ? uppercase_mapping : lowercase_mapping
end
end
# Holds static data from the Unicode database.
- 1
class UnicodeDatabase
- 1
ATTRIBUTES = :codepoints, :composition_exclusion, :composition_map, :boundary, :cp1252
- 1
attr_writer(*ATTRIBUTES)
- 1
def initialize
- 1
@codepoints = Hash.new(Codepoint.new)
- 1
@composition_exclusion = []
- 1
@composition_map = {}
- 1
@boundary = {}
- 1
@cp1252 = {}
end
# Lazy load the Unicode database so it's only loaded when it's actually used
- 1
ATTRIBUTES.each do |attr_name|
- 5
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
- 1
def #{attr_name} # def codepoints
load # load
@#{attr_name} # @codepoints
end # end
EOS
end
# Loads the Unicode database and returns all the internal objects of
# UnicodeDatabase.
- 1
def load
- 1
begin
- 2
@codepoints, @composition_exclusion, @composition_map, @boundary, @cp1252 = File.open(self.class.filename, "rb") { |f| Marshal.load f.read }
rescue => e
raise IOError.new("Couldn't load the Unicode tables for UTF8Handler (#{e.message}), ActiveSupport::Multibyte is unusable")
end
# Redefine the === method so we can write shorter rules for grapheme cluster breaks
- 1
@boundary.each_key do |k|
@boundary[k].instance_eval do
- 15
def ===(other)
- 1054930
detect { |i| i === other } ? true : false
end
- 17
end if @boundary[k].kind_of?(Array)
end
# define attr_reader methods for the instance variables
- 1
class << self
- 1
attr_reader(*ATTRIBUTES)
end
end
# Returns the directory in which the data files are stored.
- 1
def self.dirname
- 1
File.dirname(__FILE__) + "/../values/"
end
# Returns the filename for the data file for this version.
- 1
def self.filename
- 1
File.expand_path File.join(dirname, "unicode_tables.dat")
end
end
- 1
private
- 1
def apply_mapping(string, mapping)
database.codepoints
string.each_codepoint.map do |codepoint|
cp = database.codepoints[codepoint]
if cp && (ncp = cp.send(mapping)) && ncp > 0
ncp
else
codepoint
end
end.pack("U*")
end
- 1
def recode_windows1252_chars(string)
string.encode(Encoding::UTF_8, Encoding::Windows_1252, invalid: :replace, undef: :replace)
end
- 1
def database
- 17065
@database ||= UnicodeDatabase.new
end
end
end
end
- 1
require "active_support/notifications/instrumenter"
- 1
require "active_support/notifications/fanout"
- 1
require "active_support/per_thread_registry"
- 1
module ActiveSupport
# = Notifications
#
# <tt>ActiveSupport::Notifications</tt> provides an instrumentation API for
# Ruby.
#
# == Instrumenters
#
# To instrument an event you just need to do:
#
# ActiveSupport::Notifications.instrument('render', extra: :information) do
# render plain: 'Foo'
# end
#
# That first executes the block and then notifies all subscribers once done.
#
# In the example above +render+ is the name of the event, and the rest is called
# the _payload_. The payload is a mechanism that allows instrumenters to pass
# extra information to subscribers. Payloads consist of a hash whose contents
# are arbitrary and generally depend on the event.
#
# == Subscribers
#
# You can consume those events and the information they provide by registering
# a subscriber.
#
# ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
# name # => String, name of the event (such as 'render' from above)
# start # => Time, when the instrumented block started execution
# finish # => Time, when the instrumented block ended execution
# id # => String, unique ID for this notification
# payload # => Hash, the payload
# end
#
# For instance, let's store all "render" events in an array:
#
# events = []
#
# ActiveSupport::Notifications.subscribe('render') do |*args|
# events << ActiveSupport::Notifications::Event.new(*args)
# end
#
# That code returns right away, you are just subscribing to "render" events.
# The block is saved and will be called whenever someone instruments "render":
#
# ActiveSupport::Notifications.instrument('render', extra: :information) do
# render plain: 'Foo'
# end
#
# event = events.first
# event.name # => "render"
# event.duration # => 10 (in milliseconds)
# event.payload # => { extra: :information }
#
# The block in the <tt>subscribe</tt> call gets the name of the event, start
# timestamp, end timestamp, a string with a unique identifier for that event
# (something like "535801666f04d0298cd6"), and a hash with the payload, in
# that order.
#
# If an exception happens during that particular instrumentation the payload will
# have a key <tt>:exception</tt> with an array of two elements as value: a string with
# the name of the exception class, and the exception message.
#
# As the previous example depicts, the class <tt>ActiveSupport::Notifications::Event</tt>
# is able to take the arguments as they come and provide an object-oriented
# interface to that data.
#
# It is also possible to pass an object which responds to <tt>call</tt> method
# as the second parameter to the <tt>subscribe</tt> method instead of a block:
#
# module ActionController
# class PageRequest
# def call(name, started, finished, unique_id, payload)
# Rails.logger.debug ['notification:', name, started, finished, unique_id, payload].join(' ')
# end
# end
# end
#
# ActiveSupport::Notifications.subscribe('process_action.action_controller', ActionController::PageRequest.new)
#
# resulting in the following output within the logs including a hash with the payload:
#
# notification: process_action.action_controller 2012-04-13 01:08:35 +0300 2012-04-13 01:08:35 +0300 af358ed7fab884532ec7 {
# controller: "Devise::SessionsController",
# action: "new",
# params: {"action"=>"new", "controller"=>"devise/sessions"},
# format: :html,
# method: "GET",
# path: "/login/sign_in",
# status: 200,
# view_runtime: 279.3080806732178,
# db_runtime: 40.053
# }
#
# You can also subscribe to all events whose name matches a certain regexp:
#
# ActiveSupport::Notifications.subscribe(/render/) do |*args|
# ...
# end
#
# and even pass no argument to <tt>subscribe</tt>, in which case you are subscribing
# to all events.
#
# == Temporary Subscriptions
#
# Sometimes you do not want to subscribe to an event for the entire life of
# the application. There are two ways to unsubscribe.
#
# WARNING: The instrumentation framework is designed for long-running subscribers,
# use this feature sparingly because it wipes some internal caches and that has
# a negative impact on performance.
#
# === Subscribe While a Block Runs
#
# You can subscribe to some event temporarily while some block runs. For
# example, in
#
# callback = lambda {|*args| ... }
# ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
# ...
# end
#
# the callback will be called for all "sql.active_record" events instrumented
# during the execution of the block. The callback is unsubscribed automatically
# after that.
#
# === Manual Unsubscription
#
# The +subscribe+ method returns a subscriber object:
#
# subscriber = ActiveSupport::Notifications.subscribe("render") do |*args|
# ...
# end
#
# To prevent that block from being called anymore, just unsubscribe passing
# that reference:
#
# ActiveSupport::Notifications.unsubscribe(subscriber)
#
# You can also unsubscribe by passing the name of the subscriber object. Note
# that this will unsubscribe all subscriptions with the given name:
#
# ActiveSupport::Notifications.unsubscribe("render")
#
# == Default Queue
#
# Notifications ships with a queue implementation that consumes and publishes events
# to all log subscribers. You can use any queue implementation you want.
#
- 1
module Notifications
- 1
class << self
- 1
attr_accessor :notifier
- 1
def publish(name, *args)
notifier.publish(name, *args)
end
- 1
def instrument(name, payload = {})
if notifier.listening?(name)
instrumenter.instrument(name, payload) { yield payload if block_given? }
else
yield payload if block_given?
end
end
- 1
def subscribe(*args, &block)
notifier.subscribe(*args, &block)
end
- 1
def subscribed(callback, *args, &block)
subscriber = subscribe(*args, &callback)
yield
ensure
unsubscribe(subscriber)
end
- 1
def unsubscribe(subscriber_or_name)
notifier.unsubscribe(subscriber_or_name)
end
- 1
def instrumenter
InstrumentationRegistry.instance.instrumenter_for(notifier)
end
end
# This class is a registry which holds all of the +Instrumenter+ objects
# in a particular thread local. To access the +Instrumenter+ object for a
# particular +notifier+, you can call the following method:
#
# InstrumentationRegistry.instrumenter_for(notifier)
#
# The instrumenters for multiple notifiers are held in a single instance of
# this class.
- 1
class InstrumentationRegistry # :nodoc:
- 1
extend ActiveSupport::PerThreadRegistry
- 1
def initialize
@registry = {}
end
- 1
def instrumenter_for(notifier)
@registry[notifier] ||= Instrumenter.new(notifier)
end
end
- 1
self.notifier = Fanout.new
end
end
- 1
require "mutex_m"
- 1
require "concurrent/map"
- 1
module ActiveSupport
- 1
module Notifications
# This is a default queue implementation that ships with Notifications.
# It just pushes events to all registered log subscribers.
#
# This class is thread safe. All methods are reentrant.
- 1
class Fanout
- 1
include Mutex_m
- 1
def initialize
- 1
@subscribers = []
- 1
@listeners_for = Concurrent::Map.new
- 1
super
end
- 1
def subscribe(pattern = nil, block = Proc.new)
subscriber = Subscribers.new pattern, block
synchronize do
@subscribers << subscriber
@listeners_for.clear
end
subscriber
end
- 1
def unsubscribe(subscriber_or_name)
synchronize do
case subscriber_or_name
when String
@subscribers.reject! { |s| s.matches?(subscriber_or_name) }
else
@subscribers.delete(subscriber_or_name)
end
@listeners_for.clear
end
end
- 1
def start(name, id, payload)
listeners_for(name).each { |s| s.start(name, id, payload) }
end
- 1
def finish(name, id, payload, listeners = listeners_for(name))
listeners.each { |s| s.finish(name, id, payload) }
end
- 1
def publish(name, *args)
listeners_for(name).each { |s| s.publish(name, *args) }
end
- 1
def listeners_for(name)
# this is correctly done double-checked locking (Concurrent::Map's lookups have volatile semantics)
@listeners_for[name] || synchronize do
# use synchronisation when accessing @subscribers
@listeners_for[name] ||= @subscribers.select { |s| s.subscribed_to?(name) }
end
end
- 1
def listening?(name)
listeners_for(name).any?
end
# This is a sync queue, so there is no waiting.
- 1
def wait
end
- 1
module Subscribers # :nodoc:
- 1
def self.new(pattern, listener)
if listener.respond_to?(:start) && listener.respond_to?(:finish)
subscriber = Evented.new pattern, listener
else
subscriber = Timed.new pattern, listener
end
unless pattern
AllMessages.new(subscriber)
else
subscriber
end
end
- 1
class Evented #:nodoc:
- 1
def initialize(pattern, delegate)
@pattern = pattern
@delegate = delegate
@can_publish = delegate.respond_to?(:publish)
end
- 1
def publish(name, *args)
if @can_publish
@delegate.publish name, *args
end
end
- 1
def start(name, id, payload)
@delegate.start name, id, payload
end
- 1
def finish(name, id, payload)
@delegate.finish name, id, payload
end
- 1
def subscribed_to?(name)
@pattern === name
end
- 1
def matches?(name)
@pattern && @pattern === name
end
end
- 1
class Timed < Evented # :nodoc:
- 1
def publish(name, *args)
@delegate.call name, *args
end
- 1
def start(name, id, payload)
timestack = Thread.current[:_timestack] ||= []
timestack.push Time.now
end
- 1
def finish(name, id, payload)
timestack = Thread.current[:_timestack]
started = timestack.pop
@delegate.call(name, started, Time.now, id, payload)
end
end
- 1
class AllMessages # :nodoc:
- 1
def initialize(delegate)
@delegate = delegate
end
- 1
def start(name, id, payload)
@delegate.start name, id, payload
end
- 1
def finish(name, id, payload)
@delegate.finish name, id, payload
end
- 1
def publish(name, *args)
@delegate.publish name, *args
end
- 1
def subscribed_to?(name)
true
end
- 1
alias :matches? :===
end
end
end
end
end
- 1
require "securerandom"
- 1
module ActiveSupport
- 1
module Notifications
# Instrumenters are stored in a thread local.
- 1
class Instrumenter
- 1
attr_reader :id
- 1
def initialize(notifier)
@id = unique_id
@notifier = notifier
end
# Instrument the given block by measuring the time taken to execute it
# and publish it. Notice that events get sent even if an error occurs
# in the passed-in block.
- 1
def instrument(name, payload = {})
# some of the listeners might have state
listeners_state = start name, payload
begin
yield payload
rescue Exception => e
payload[:exception] = [e.class.name, e.message]
payload[:exception_object] = e
raise e
ensure
finish_with_state listeners_state, name, payload
end
end
# Send a start notification with +name+ and +payload+.
- 1
def start(name, payload)
@notifier.start name, @id, payload
end
# Send a finish notification with +name+ and +payload+.
- 1
def finish(name, payload)
@notifier.finish name, @id, payload
end
- 1
def finish_with_state(listeners_state, name, payload)
@notifier.finish name, @id, payload, listeners_state
end
- 1
private
- 1
def unique_id
SecureRandom.hex(10)
end
end
- 1
class Event
- 1
attr_reader :name, :time, :transaction_id, :payload, :children
- 1
attr_accessor :end
- 1
def initialize(name, start, ending, transaction_id, payload)
@name = name
@payload = payload.dup
@time = start
@transaction_id = transaction_id
@end = ending
@children = []
@duration = nil
end
# Returns the difference in milliseconds between when the execution of the
# event started and when it ended.
#
# ActiveSupport::Notifications.subscribe('wait') do |*args|
# @event = ActiveSupport::Notifications::Event.new(*args)
# end
#
# ActiveSupport::Notifications.instrument('wait') do
# sleep 1
# end
#
# @event.duration # => 1000.138
- 1
def duration
@duration ||= 1000.0 * (self.end - time)
end
- 1
def <<(event)
@children << event
end
- 1
def parent_of?(event)
@children.include? event
end
end
end
end
- 1
require "active_support/core_ext/module/delegation"
- 1
module ActiveSupport
# NOTE: This approach has been deprecated for end-user code in favor of {thread_mattr_accessor}[rdoc-ref:Module#thread_mattr_accessor] and friends.
# Please use that approach instead.
#
# This module is used to encapsulate access to thread local variables.
#
# Instead of polluting the thread locals namespace:
#
# Thread.current[:connection_handler]
#
# you define a class that extends this module:
#
# module ActiveRecord
# class RuntimeRegistry
# extend ActiveSupport::PerThreadRegistry
#
# attr_accessor :connection_handler
# end
# end
#
# and invoke the declared instance accessors as class methods. So
#
# ActiveRecord::RuntimeRegistry.connection_handler = connection_handler
#
# sets a connection handler local to the current thread, and
#
# ActiveRecord::RuntimeRegistry.connection_handler
#
# returns a connection handler local to the current thread.
#
# This feature is accomplished by instantiating the class and storing the
# instance as a thread local keyed by the class name. In the example above
# a key "ActiveRecord::RuntimeRegistry" is stored in <tt>Thread.current</tt>.
# The class methods proxy to said thread local instance.
#
# If the class has an initializer, it must accept no arguments.
- 1
module PerThreadRegistry
- 1
def self.extended(object)
- 1
object.instance_variable_set "@per_thread_registry_key", object.name.freeze
end
- 1
def instance
Thread.current[@per_thread_registry_key] ||= new
end
- 1
private
- 1
def method_missing(name, *args, &block)
# Caches the method definition as a singleton method of the receiver.
#
# By letting #delegate handle it, we avoid an enclosure that'll capture args.
singleton_class.delegate name, to: :instance
send(name, *args, &block)
end
end
end
- 1
gem "minitest" # make sure we get the gem, not stdlib
- 1
require "minitest"
- 1
require "active_support/testing/tagged_logging"
- 1
require "active_support/testing/setup_and_teardown"
- 1
require "active_support/testing/assertions"
- 1
require "active_support/testing/deprecation"
- 1
require "active_support/testing/declarative"
- 1
require "active_support/testing/isolation"
- 1
require "active_support/testing/constant_lookup"
- 1
require "active_support/testing/time_helpers"
- 1
require "active_support/testing/file_fixtures"
- 1
require "active_support/core_ext/kernel/reporting"
- 1
module ActiveSupport
- 1
class TestCase < ::Minitest::Test
- 1
Assertion = Minitest::Assertion
- 1
class << self
# Sets the order in which test cases are run.
#
# ActiveSupport::TestCase.test_order = :random # => :random
#
# Valid values are:
# * +:random+ (to run tests in random order)
# * +:parallel+ (to run tests in parallel)
# * +:sorted+ (to run tests alphabetically by method name)
# * +:alpha+ (equivalent to +:sorted+)
- 1
def test_order=(new_order)
ActiveSupport.test_order = new_order
end
# Returns the order in which test cases are run.
#
# ActiveSupport::TestCase.test_order # => :random
#
# Possible values are +:random+, +:parallel+, +:alpha+, +:sorted+.
# Defaults to +:random+.
- 1
def test_order
- 4
ActiveSupport.test_order ||= :random
end
end
- 1
alias_method :method_name, :name
- 1
include ActiveSupport::Testing::TaggedLogging
- 1
include ActiveSupport::Testing::SetupAndTeardown
- 1
include ActiveSupport::Testing::Assertions
- 1
include ActiveSupport::Testing::Deprecation
- 1
include ActiveSupport::Testing::TimeHelpers
- 1
include ActiveSupport::Testing::FileFixtures
- 1
extend ActiveSupport::Testing::Declarative
# test/unit backwards compatibility methods
- 1
alias :assert_raise :assert_raises
- 1
alias :assert_not_empty :refute_empty
- 1
alias :assert_not_equal :refute_equal
- 1
alias :assert_not_in_delta :refute_in_delta
- 1
alias :assert_not_in_epsilon :refute_in_epsilon
- 1
alias :assert_not_includes :refute_includes
- 1
alias :assert_not_instance_of :refute_instance_of
- 1
alias :assert_not_kind_of :refute_kind_of
- 1
alias :assert_no_match :refute_match
- 1
alias :assert_not_nil :refute_nil
- 1
alias :assert_not_operator :refute_operator
- 1
alias :assert_not_predicate :refute_predicate
- 1
alias :assert_not_respond_to :refute_respond_to
- 1
alias :assert_not_same :refute_same
- 1
ActiveSupport.run_load_hooks(:active_support_test_case, self)
end
end
- 1
module ActiveSupport
- 1
module Testing
- 1
module Assertions
- 1
UNTRACKED = Object.new # :nodoc:
# Asserts that an expression is not truthy. Passes if <tt>object</tt> is
# +nil+ or +false+. "Truthy" means "considered true in a conditional"
# like <tt>if foo</tt>.
#
# assert_not nil # => true
# assert_not false # => true
# assert_not 'foo' # => Expected "foo" to be nil or false
#
# An error message can be specified.
#
# assert_not foo, 'foo should be false'
- 1
def assert_not(object, message = nil)
message ||= "Expected #{mu_pp(object)} to be nil or false"
assert !object, message
end
# Assertion that the block should not raise an exception.
#
# Passes if evaluated code in the yielded block raises no exception.
#
# assert_nothing_raised do
# perform_service(param: 'no_exception')
# end
- 1
def assert_nothing_raised
yield
end
# Test numeric difference between the return value of an expression as a
# result of what is evaluated in the yielded block.
#
# assert_difference 'Article.count' do
# post :create, params: { article: {...} }
# end
#
# An arbitrary expression is passed in and evaluated.
#
# assert_difference 'Article.last.comments(:reload).size' do
# post :create, params: { comment: {...} }
# end
#
# An arbitrary positive or negative difference can be specified.
# The default is <tt>1</tt>.
#
# assert_difference 'Article.count', -1 do
# post :delete, params: { id: ... }
# end
#
# An array of expressions can also be passed in and evaluated.
#
# assert_difference [ 'Article.count', 'Post.count' ], 2 do
# post :create, params: { article: {...} }
# end
#
# A lambda or a list of lambdas can be passed in and evaluated:
#
# assert_difference ->{ Article.count }, 2 do
# post :create, params: { article: {...} }
# end
#
# assert_difference [->{ Article.count }, ->{ Post.count }], 2 do
# post :create, params: { article: {...} }
# end
#
# An error message can be specified.
#
# assert_difference 'Article.count', -1, 'An Article should be destroyed' do
# post :delete, params: { id: ... }
# end
- 1
def assert_difference(expression, difference = 1, message = nil, &block)
expressions = Array(expression)
exps = expressions.map { |e|
e.respond_to?(:call) ? e : lambda { eval(e, block.binding) }
}
before = exps.map(&:call)
retval = yield
expressions.zip(exps).each_with_index do |(code, e), i|
error = "#{code.inspect} didn't change by #{difference}"
error = "#{message}.\n#{error}" if message
assert_equal(before[i] + difference, e.call, error)
end
retval
end
# Assertion that the numeric result of evaluating an expression is not
# changed before and after invoking the passed in block.
#
# assert_no_difference 'Article.count' do
# post :create, params: { article: invalid_attributes }
# end
#
# An error message can be specified.
#
# assert_no_difference 'Article.count', 'An Article should not be created' do
# post :create, params: { article: invalid_attributes }
# end
- 1
def assert_no_difference(expression, message = nil, &block)
assert_difference expression, 0, message, &block
end
# Assertion that the result of evaluating an expression is changed before
# and after invoking the passed in block.
#
# assert_changes 'Status.all_good?' do
# post :create, params: { status: { ok: false } }
# end
#
# You can pass the block as a string to be evaluated in the context of
# the block. A lambda can be passed for the block as well.
#
# assert_changes -> { Status.all_good? } do
# post :create, params: { status: { ok: false } }
# end
#
# The assertion is useful to test side effects. The passed block can be
# anything that can be converted to string with #to_s.
#
# assert_changes :@object do
# @object = 42
# end
#
# The keyword arguments :from and :to can be given to specify the
# expected initial value and the expected value after the block was
# executed.
#
# assert_changes :@object, from: nil, to: :foo do
# @object = :foo
# end
#
# An error message can be specified.
#
# assert_changes -> { Status.all_good? }, 'Expected the status to be bad' do
# post :create, params: { status: { incident: true } }
# end
- 1
def assert_changes(expression, message = nil, from: UNTRACKED, to: UNTRACKED, &block)
exp = expression.respond_to?(:call) ? expression : -> { eval(expression.to_s, block.binding) }
before = exp.call
retval = yield
unless from == UNTRACKED
error = "#{expression.inspect} isn't #{from.inspect}"
error = "#{message}.\n#{error}" if message
assert from === before, error
end
after = exp.call
if to == UNTRACKED
error = "#{expression.inspect} didn't changed"
error = "#{message}.\n#{error}" if message
assert_not_equal before, after, error
else
error = "#{expression.inspect} didn't change to #{to}"
error = "#{message}.\n#{error}" if message
assert to === after, error
end
retval
end
# Assertion that the result of evaluating an expression is changed before
# and after invoking the passed in block.
#
# assert_no_changes 'Status.all_good?' do
# post :create, params: { status: { ok: true } }
# end
#
# An error message can be specified.
#
# assert_no_changes -> { Status.all_good? }, 'Expected the status to be good' do
# post :create, params: { status: { ok: false } }
# end
- 1
def assert_no_changes(expression, message = nil, &block)
exp = expression.respond_to?(:call) ? expression : -> { eval(expression.to_s, block.binding) }
before = exp.call
retval = yield
after = exp.call
error = "#{expression.inspect} did change to #{after}"
error = "#{message}.\n#{error}" if message
assert_equal before, after, error
retval
end
end
end
end
- 1
gem "minitest"
- 1
require "minitest"
- 1
if Minitest.respond_to?(:run_via) && !Minitest.run_via[:rails]
Minitest.run_via[:ruby] = true
end
- 1
Minitest.autorun
- 1
require "active_support/concern"
- 1
require "active_support/inflector"
- 1
module ActiveSupport
- 1
module Testing
# Resolves a constant from a minitest spec name.
#
# Given the following spec-style test:
#
# describe WidgetsController, :index do
# describe "authenticated user" do
# describe "returns widgets" do
# it "has a controller that exists" do
# assert_kind_of WidgetsController, @controller
# end
# end
# end
# end
#
# The test will have the following name:
#
# "WidgetsController::index::authenticated user::returns widgets"
#
# The constant WidgetsController can be resolved from the name.
# The following code will resolve the constant:
#
# controller = determine_constant_from_test_name(name) do |constant|
# Class === constant && constant < ::ActionController::Metal
# end
- 1
module ConstantLookup
- 1
extend ::ActiveSupport::Concern
- 1
module ClassMethods # :nodoc:
- 1
def determine_constant_from_test_name(test_name)
names = test_name.split "::"
while names.size > 0 do
names.last.sub!(/Test$/, "")
begin
constant = names.join("::").safe_constantize
break(constant) if yield(constant)
ensure
names.pop
end
end
end
end
end
end
end
- 1
module ActiveSupport
- 1
module Testing
- 1
module Declarative
- 1
unless defined?(Spec)
# Helper to define a test method using a String. Under the hood, it replaces
# spaces with underscores and defines the test method.
#
# test "verify something" do
# ...
# end
- 1
def test(name, &block)
test_name = "test_#{name.gsub(/\s+/, '_')}".to_sym
defined = method_defined? test_name
raise "#{test_name} is already defined in #{self}" if defined
if block_given?
define_method(test_name, &block)
else
define_method(test_name) do
flunk "No implementation provided for #{name}"
end
end
end
end
end
end
end
- 1
require "active_support/deprecation"
- 1
require "active_support/core_ext/regexp"
- 1
module ActiveSupport
- 1
module Testing
- 1
module Deprecation #:nodoc:
- 1
def assert_deprecated(match = nil, deprecator = nil, &block)
result, warnings = collect_deprecations(deprecator, &block)
assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
if match
match = Regexp.new(Regexp.escape(match)) unless match.is_a?(Regexp)
assert warnings.any? { |w| match.match?(w) }, "No deprecation warning matched #{match}: #{warnings.join(', ')}"
end
result
end
- 1
def assert_not_deprecated(deprecator = nil, &block)
result, deprecations = collect_deprecations(deprecator, &block)
assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n #{deprecations * "\n "}"
result
end
- 1
def collect_deprecations(deprecator = nil)
deprecator ||= ActiveSupport::Deprecation
old_behavior = deprecator.behavior
deprecations = []
deprecator.behavior = Proc.new do |message, callstack|
deprecations << message
end
result = yield
[result, deprecations]
ensure
deprecator.behavior = old_behavior
end
end
end
end
- 1
module ActiveSupport
- 1
module Testing
# Adds simple access to sample files called file fixtures.
# File fixtures are normal files stored in
# <tt>ActiveSupport::TestCase.file_fixture_path</tt>.
#
# File fixtures are represented as +Pathname+ objects.
# This makes it easy to extract specific information:
#
# file_fixture("example.txt").read # get the file's content
# file_fixture("example.mp3").size # get the file size
- 1
module FileFixtures
- 1
extend ActiveSupport::Concern
- 1
included do
- 1
class_attribute :file_fixture_path, instance_writer: false
end
# Returns a +Pathname+ to the fixture file named +fixture_name+.
#
# Raises +ArgumentError+ if +fixture_name+ can't be found.
- 1
def file_fixture(fixture_name)
path = Pathname.new(File.join(file_fixture_path, fixture_name))
if path.exist?
path
else
msg = "the directory '%s' does not contain a file named '%s'"
raise ArgumentError, msg % [file_fixture_path, fixture_name]
end
end
end
end
end
- 1
module ActiveSupport
- 1
module Testing
- 1
module Isolation
- 1
require "thread"
- 1
def self.included(klass) #:nodoc:
klass.class_eval do
parallelize_me!
end
end
- 1
def self.forking_env?
- 1
!ENV["NO_FORK"] && Process.respond_to?(:fork)
end
- 1
def run
serialized = run_in_isolation do
super
end
Marshal.load(serialized)
end
- 1
module Forking
- 1
def run_in_isolation(&blk)
read, write = IO.pipe
read.binmode
write.binmode
pid = fork do
read.close
yield
begin
if error?
failures.map! { |e|
begin
Marshal.dump e
e
rescue TypeError
ex = Exception.new e.message
ex.set_backtrace e.backtrace
Minitest::UnexpectedError.new ex
end
}
end
result = Marshal.dump(dup)
end
write.puts [result].pack("m")
exit!
end
write.close
result = read.read
Process.wait2(pid)
return result.unpack("m")[0]
end
end
- 1
module Subprocess
- 1
ORIG_ARGV = ARGV.dup unless defined?(ORIG_ARGV)
# Crazy H4X to get this working in windows / jruby with
# no forking.
- 1
def run_in_isolation(&blk)
require "tempfile"
if ENV["ISOLATION_TEST"]
yield
File.open(ENV["ISOLATION_OUTPUT"], "w") do |file|
file.puts [Marshal.dump(dup)].pack("m")
end
exit!
else
Tempfile.open("isolation") do |tmpfile|
env = {
"ISOLATION_TEST" => self.class.name,
"ISOLATION_OUTPUT" => tmpfile.path
}
test_opts = "-n#{self.class.name}##{name}"
load_path_args = []
$-I.each do |p|
load_path_args << "-I"
load_path_args << File.expand_path(p)
end
child = IO.popen([env, Gem.ruby, *load_path_args, $0, *ORIG_ARGV, test_opts])
begin
Process.wait(child.pid)
rescue Errno::ECHILD # The child process may exit before we wait
nil
end
return tmpfile.read.unpack("m")[0]
end
end
end
end
- 1
include forking_env? ? Forking : Subprocess
end
end
end
- 1
require "minitest/mock"
- 1
module ActiveSupport
- 1
module Testing
- 1
module MethodCallAssertions # :nodoc:
- 1
private
- 1
def assert_called(object, method_name, message = nil, times: 1, returns: nil)
times_called = 0
object.stub(method_name, proc { times_called += 1; returns }) { yield }
error = "Expected #{method_name} to be called #{times} times, " \
"but was called #{times_called} times"
error = "#{message}.\n#{error}" if message
assert_equal times, times_called, error
end
- 1
def assert_called_with(object, method_name, args = [], returns: nil)
mock = Minitest::Mock.new
if args.all? { |arg| arg.is_a?(Array) }
args.each { |arg| mock.expect(:call, returns, arg) }
else
mock.expect(:call, returns, args)
end
object.stub(method_name, mock) { yield }
mock.verify
end
- 1
def assert_not_called(object, method_name, message = nil, &block)
assert_called(object, method_name, message, times: 0, &block)
end
- 1
def stub_any_instance(klass, instance: klass.new)
klass.stub(:new, instance) { yield instance }
end
end
end
end
- 1
require "active_support/concern"
- 1
require "active_support/callbacks"
- 1
module ActiveSupport
- 1
module Testing
# Adds support for +setup+ and +teardown+ callbacks.
# These callbacks serve as a replacement to overwriting the
# <tt>#setup</tt> and <tt>#teardown</tt> methods of your TestCase.
#
# class ExampleTest < ActiveSupport::TestCase
# setup do
# # ...
# end
#
# teardown do
# # ...
# end
# end
- 1
module SetupAndTeardown
- 1
extend ActiveSupport::Concern
- 1
included do
- 1
include ActiveSupport::Callbacks
- 1
define_callbacks :setup, :teardown
end
- 1
module ClassMethods
# Add a callback, which runs before <tt>TestCase#setup</tt>.
- 1
def setup(*args, &block)
set_callback(:setup, :before, *args, &block)
end
# Add a callback, which runs after <tt>TestCase#teardown</tt>.
- 1
def teardown(*args, &block)
set_callback(:teardown, :after, *args, &block)
end
end
- 1
def before_setup # :nodoc:
- 1
super
- 1
run_callbacks :setup
end
- 1
def after_teardown # :nodoc:
- 1
run_callbacks :teardown
- 1
super
end
end
end
end
- 1
module ActiveSupport
- 1
module Testing
# Logs a "PostsControllerTest: test name" heading before each test to
# make test.log easier to search and follow along with.
- 1
module TaggedLogging #:nodoc:
- 1
attr_writer :tagged_logger
- 1
def before_setup
- 1
if tagged_logger && tagged_logger.info?
heading = "#{self.class}: #{name}"
divider = "-" * heading.size
tagged_logger.info divider
tagged_logger.info heading
tagged_logger.info divider
end
- 1
super
end
- 1
private
- 1
def tagged_logger
- 1
@tagged_logger ||= (defined?(Rails.logger) && Rails.logger)
end
end
end
end
- 1
require "active_support/core_ext/string/strip" # for strip_heredoc
- 1
require "concurrent/map"
- 1
module ActiveSupport
- 1
module Testing
- 1
class SimpleStubs # :nodoc:
- 1
Stub = Struct.new(:object, :method_name, :original_method)
- 1
def initialize
@stubs = Concurrent::Map.new { |h, k| h[k] = {} }
end
- 1
def stub_object(object, method_name, &block)
if stub = stubbing(object, method_name)
unstub_object(stub)
end
new_name = "__simple_stub__#{method_name}"
@stubs[object.object_id][method_name] = Stub.new(object, method_name, new_name)
object.singleton_class.send :alias_method, new_name, method_name
object.define_singleton_method(method_name, &block)
end
- 1
def unstub_all!
@stubs.each_value do |object_stubs|
object_stubs.each_value do |stub|
unstub_object(stub)
end
end
@stubs.clear
end
- 1
def stubbing(object, method_name)
@stubs[object.object_id][method_name]
end
- 1
private
- 1
def unstub_object(stub)
singleton_class = stub.object.singleton_class
singleton_class.send :undef_method, stub.method_name
singleton_class.send :alias_method, stub.method_name, stub.original_method
singleton_class.send :undef_method, stub.original_method
end
end
# Contains helpers that help you test passage of time.
- 1
module TimeHelpers
# Changes current time to the time in the future or in the past by a given time difference by
# stubbing +Time.now+, +Date.today+, and +DateTime.now+.
#
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
# travel 1.day
# Time.current # => Sun, 10 Nov 2013 15:34:49 EST -05:00
# Date.current # => Sun, 10 Nov 2013
# DateTime.current # => Sun, 10 Nov 2013 15:34:49 -0500
#
# This method also accepts a block, which will return the current time back to its original
# state at the end of the block:
#
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
# travel 1.day do
# User.create.created_at # => Sun, 10 Nov 2013 15:34:49 EST -05:00
# end
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
- 1
def travel(duration, &block)
travel_to Time.now + duration, &block
end
# Changes current time to the given time by stubbing +Time.now+,
# +Date.today+, and +DateTime.now+ to return the time or date passed into this method.
#
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
# travel_to Time.zone.local(2004, 11, 24, 01, 04, 44)
# Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
# Date.current # => Wed, 24 Nov 2004
# DateTime.current # => Wed, 24 Nov 2004 01:04:44 -0500
#
# Dates are taken as their timestamp at the beginning of the day in the
# application time zone. <tt>Time.current</tt> returns said timestamp,
# and <tt>Time.now</tt> its equivalent in the system time zone. Similarly,
# <tt>Date.current</tt> returns a date equal to the argument, and
# <tt>Date.today</tt> the date according to <tt>Time.now</tt>, which may
# be different. (Note that you rarely want to deal with <tt>Time.now</tt>,
# or <tt>Date.today</tt>, in order to honor the application time zone
# please always use <tt>Time.current</tt> and <tt>Date.current</tt>.)
#
# Note that the usec for the time passed will be set to 0 to prevent rounding
# errors with external services, like MySQL (which will round instead of floor,
# leading to off-by-one-second errors).
#
# This method also accepts a block, which will return the current time back to its original
# state at the end of the block:
#
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
# travel_to Time.zone.local(2004, 11, 24, 01, 04, 44) do
# Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
# end
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
- 1
def travel_to(date_or_time)
if block_given? && simple_stubs.stubbing(Time, :now)
travel_to_nested_block_call = <<-MSG.strip_heredoc
Calling `travel_to` with a block, when we have previously already made a call to `travel_to`, can lead to confusing time stubbing.
Instead of:
travel_to 2.days.from_now do
# 2 days from today
travel_to 3.days.from_now do
# 5 days from today
end
end
preferred way to achieve above is:
travel 2.days do
# 2 days from today
end
travel 5.days do
# 5 days from today
end
MSG
raise travel_to_nested_block_call
end
if date_or_time.is_a?(Date) && !date_or_time.is_a?(DateTime)
now = date_or_time.midnight.to_time
else
now = date_or_time.to_time.change(usec: 0)
end
simple_stubs.stub_object(Time, :now) { at(now.to_i) }
simple_stubs.stub_object(Date, :today) { jd(now.to_date.jd) }
simple_stubs.stub_object(DateTime, :now) { jd(now.to_date.jd, now.hour, now.min, now.sec, Rational(now.utc_offset, 86400)) }
if block_given?
begin
yield
ensure
travel_back
end
end
end
# Returns the current time back to its original state, by removing the stubs added by
# `travel` and `travel_to`.
#
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
# travel_to Time.zone.local(2004, 11, 24, 01, 04, 44)
# Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
# travel_back
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
- 1
def travel_back
simple_stubs.unstub_all!
end
- 1
private
- 1
def simple_stubs
@simple_stubs ||= SimpleStubs.new
end
end
end
end
- 1
require_relative "gem_version"
- 1
module ActiveSupport
# Returns the version of the currently loaded ActiveSupport as a <tt>Gem::Version</tt>
- 1
def self.version
gem_version
end
end
- 1
ORIG_ARGV = ARGV.dup
- 1
require "active_support/core_ext/kernel/reporting"
- 1
silence_warnings do
- 1
Encoding.default_internal = Encoding::UTF_8
- 1
Encoding.default_external = Encoding::UTF_8
end
- 1
require "active_support/testing/autorun"
- 1
require "active_support/testing/method_call_assertions"
- 1
ENV["NO_RELOAD"] = "1"
- 1
require "active_support"
- 1
Thread.abort_on_exception = true
# Show backtraces for deprecated behavior for quicker cleanup.
- 1
ActiveSupport::Deprecation.debug = true
# Default to old to_time behavior but allow running tests with new behavior
- 1
ActiveSupport.to_time_preserves_timezone = ENV["PRESERVE_TIMEZONES"] == "1"
# Disable available locale checks to avoid warnings running the test suite.
- 1
I18n.enforce_available_locales = false
- 1
class ActiveSupport::TestCase
- 1
include ActiveSupport::Testing::MethodCallAssertions
# Skips the current run on Rubinius using Minitest::Assertions#skip
- 1
private def rubinius_skip(message = "")
skip message if RUBY_ENGINE == "rbx"
end
# Skips the current run on JRuby using Minitest::Assertions#skip
- 1
private def jruby_skip(message = "")
skip message if defined?(JRUBY_VERSION)
end
end
- 1
module MultibyteTestHelpers
- 1
class Downloader
- 1
def self.download(from, to)
- 1
unless File.exist?(to)
unless File.exist?(File.dirname(to))
system "mkdir -p #{File.dirname(to)}"
end
open(from) do |source|
File.open(to, "w") do |target|
source.each_line do |l|
target.write l
end
end
end
end
- 1
true
end
end
- 1
UNIDATA_URL = "http://www.unicode.org/Public/#{ActiveSupport::Multibyte::Unicode::UNICODE_VERSION}/ucd"
- 1
CACHE_DIR = "#{Dir.tmpdir}/cache/unicode_conformance/#{ActiveSupport::Multibyte::Unicode::UNICODE_VERSION}"
- 1
FileUtils.mkdir_p(CACHE_DIR)
- 1
UNICODE_STRING = "こにちわ".freeze
- 1
ASCII_STRING = "ohayo".freeze
- 1
BYTE_STRING = "\270\236\010\210\245".force_encoding("ASCII-8BIT").freeze
- 1
def chars(str)
ActiveSupport::Multibyte::Chars.new(str)
end
- 1
def inspect_codepoints(str)
str.to_s.unpack("U*").map { |cp| cp.to_s(16) }.join(" ")
end
- 1
def assert_equal_codepoints(expected, actual, message = nil)
assert_equal(inspect_codepoints(expected), inspect_codepoints(actual), message)
end
end