Remove unused parts of activesupport

This commit is contained in:
Douglas Eichelberger 2023-04-25 11:01:59 -07:00
parent 3b92bb9da5
commit 6935b84584
156 changed files with 0 additions and 13781 deletions

View File

@ -1,48 +0,0 @@
# frozen_string_literal: true
module ActiveSupport
# Actionable errors let's you define actions to resolve an error.
#
# To make an error actionable, include the <tt>ActiveSupport::ActionableError</tt>
# module and invoke the +action+ class macro to define the action. An action
# needs a name and a block to execute.
module ActionableError
extend Concern
class NonActionable < StandardError; end
included do
class_attribute :_actions, default: {}
end
def self.actions(error) # :nodoc:
case error
when ActionableError, -> it { Class === it && it < ActionableError }
error._actions
else
{}
end
end
def self.dispatch(error, name) # :nodoc:
actions(error).fetch(name).call
rescue KeyError
raise NonActionable, "Cannot find action \"#{name}\""
end
module ClassMethods
# Defines an action that can resolve the error.
#
# class PendingMigrationError < MigrationError
# include ActiveSupport::ActionableError
#
# action "Run pending migrations" do
# ActiveRecord::Tasks::DatabaseTasks.migrate
# end
# end
def action(name, &block)
_actions[name] = block
end
end
end
end

View File

@ -1,50 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/symbol/starts_ends_with"
module ActiveSupport
# Wrapping an array in an +ArrayInquirer+ gives a friendlier way to check
# its string-like contents:
#
# variants = ActiveSupport::ArrayInquirer.new([:phone, :tablet])
#
# variants.phone? # => true
# variants.tablet? # => true
# variants.desktop? # => false
class ArrayInquirer < Array
# Passes each element of +candidates+ collection to ArrayInquirer collection.
# The method returns true if any element from the ArrayInquirer collection
# is equal to the stringified or symbolized form of any element in the +candidates+ collection.
#
# If +candidates+ collection is not given, method returns true.
#
# variants = ActiveSupport::ArrayInquirer.new([:phone, :tablet])
#
# variants.any? # => true
# variants.any?(:phone, :tablet) # => true
# variants.any?('phone', 'desktop') # => true
# variants.any?(:desktop, :watch) # => false
def any?(*candidates)
if candidates.none?
super
else
candidates.any? do |candidate|
include?(candidate.to_sym) || include?(candidate.to_s)
end
end
end
private
def respond_to_missing?(name, include_private = false)
name.end_with?("?") || super
end
def method_missing(name, *args)
if name.end_with?("?")
any?(name[0..-2])
else
super
end
end
end
end

View File

@ -1,131 +0,0 @@
# frozen_string_literal: true
module ActiveSupport
# Backtraces often include many lines that are not relevant for the context
# under review. This makes it hard to find the signal amongst the backtrace
# noise, and adds debugging time. With a BacktraceCleaner, filters and
# silencers are used to remove the noisy lines, so that only the most relevant
# lines remain.
#
# Filters are used to modify lines of data, while silencers are used to remove
# lines entirely. The typical filter use case is to remove lengthy path
# information from the start of each line, and view file paths relevant to the
# app directory instead of the file system root. The typical silencer use case
# is to exclude the output of a noisy library from the backtrace, so that you
# can focus on the rest.
#
# bc = ActiveSupport::BacktraceCleaner.new
# bc.add_filter { |line| line.gsub(Rails.root.to_s, '') } # strip the Rails.root prefix
# bc.add_silencer { |line| /puma|rubygems/.match?(line) } # skip any lines from puma or rubygems
# bc.clean(exception.backtrace) # perform the cleanup
#
# To reconfigure an existing BacktraceCleaner (like the default one in Rails)
# and show as much data as possible, you can always call
# <tt>BacktraceCleaner#remove_silencers!</tt>, which will restore the
# backtrace to a pristine state. If you need to reconfigure an existing
# BacktraceCleaner so that it does not filter or modify the paths of any lines
# of the backtrace, you can call <tt>BacktraceCleaner#remove_filters!</tt>
# These two methods will give you a completely untouched backtrace.
#
# Inspired by the Quiet Backtrace gem by thoughtbot.
class BacktraceCleaner
def initialize
@filters, @silencers = [], []
add_gem_filter
add_gem_silencer
add_stdlib_silencer
end
# Returns the backtrace after all filters and silencers have been run
# against it. Filters run first, then silencers.
def clean(backtrace, kind = :silent)
filtered = filter_backtrace(backtrace)
case kind
when :silent
silence(filtered)
when :noise
noise(filtered)
else
filtered
end
end
alias :filter :clean
# Adds a filter from the block provided. Each line in the backtrace will be
# mapped against this filter.
#
# # Will turn "/my/rails/root/app/models/person.rb" into "/app/models/person.rb"
# backtrace_cleaner.add_filter { |line| line.gsub(Rails.root, '') }
def add_filter(&block)
@filters << block
end
# Adds a silencer from the block provided. If the silencer returns +true+
# for a given line, it will be excluded from the clean backtrace.
#
# # Will reject all lines that include the word "puma", like "/gems/puma/server.rb" or "/app/my_puma_server/rb"
# backtrace_cleaner.add_silencer { |line| /puma/.match?(line) }
def add_silencer(&block)
@silencers << block
end
# Removes all silencers, but leaves in the filters. Useful if your
# context of debugging suddenly expands as you suspect a bug in one of
# the libraries you use.
def remove_silencers!
@silencers = []
end
# Removes all filters, but leaves in the silencers. Useful if you suddenly
# need to see entire filepaths in the backtrace that you had already
# filtered out.
def remove_filters!
@filters = []
end
private
FORMATTED_GEMS_PATTERN = /\A[^\/]+ \([\w.]+\) /
def add_gem_filter
gems_paths = (Gem.path | [Gem.default_dir]).map { |p| Regexp.escape(p) }
return if gems_paths.empty?
gems_regexp = %r{\A(#{gems_paths.join('|')})/(bundler/)?gems/([^/]+)-([\w.]+)/(.*)}
gems_result = '\3 (\4) \5'
add_filter { |line| line.sub(gems_regexp, gems_result) }
end
def add_gem_silencer
add_silencer { |line| FORMATTED_GEMS_PATTERN.match?(line) }
end
def add_stdlib_silencer
add_silencer { |line| line.start_with?(RbConfig::CONFIG["rubylibdir"]) }
end
def filter_backtrace(backtrace)
@filters.each do |f|
backtrace = backtrace.map { |line| f.call(line) }
end
backtrace
end
def silence(backtrace)
@silencers.each do |s|
backtrace = backtrace.reject { |line| s.call(line) }
end
backtrace
end
def noise(backtrace)
backtrace.select do |line|
@silencers.any? do |s|
s.call(line)
end
end
end
end
end

View File

@ -1,51 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/benchmark"
require "active_support/core_ext/hash/keys"
module ActiveSupport
module Benchmarkable
# Allows you to measure the execution time of a block in a template and
# records the result to the log. Wrap this block around expensive operations
# or possible bottlenecks to get a time reading for the operation. For
# example, let's say you thought your file processing method was taking too
# long; you could wrap it in a benchmark block.
#
# <% benchmark 'Process data files' do %>
# <%= expensive_files_operation %>
# <% end %>
#
# That would add something like "Process data files (345.2ms)" to the log,
# which you can then use to compare timings when optimizing your code.
#
# You may give an optional logger level (<tt>:debug</tt>, <tt>:info</tt>,
# <tt>:warn</tt>, <tt>:error</tt>) as the <tt>:level</tt> option. The
# default logger level value is <tt>:info</tt>.
#
# <% benchmark 'Low-level files', level: :debug do %>
# <%= lowlevel_files_operation %>
# <% end %>
#
# Finally, you can pass true as the third argument to silence all log
# activity (other than the timing information) from inside the block. This
# is great for boiling down a noisy block to just a single statement that
# produces one log line:
#
# <% benchmark 'Process data files', level: :info, silence: true do %>
# <%= expensive_and_chatty_files_operation %>
# <% end %>
def benchmark(message = "Benchmarking", options = {})
if logger
options.assert_valid_keys(:level, :silence)
options[:level] ||= :info
result = nil
ms = Benchmark.ms { result = options[:silence] ? logger.silence { yield } : yield }
logger.public_send(options[:level], "%s (%.1fms)" % [ message, ms ])
result
else
yield
end
end
end
end

View File

@ -1,8 +0,0 @@
# frozen_string_literal: true
begin
require "builder"
rescue LoadError => e
$stderr.puts "You don't have builder installed in your application. Please add it to your Gemfile and run bundle install"
raise e
end

View File

@ -1,862 +0,0 @@
# frozen_string_literal: true
require "active_support/concern"
require "active_support/descendants_tracker"
require "active_support/core_ext/array/extract_options"
require "active_support/core_ext/class/attribute"
require "active_support/core_ext/string/filters"
require "thread"
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+).
#
# By default callbacks are halted by throwing +:abort+.
# See +ClassMethods.define_callbacks+ for details.
#
# 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
module Callbacks
extend Concern
included do
extend ActiveSupport::DescendantsTracker
class_attribute :__callbacks, instance_writer: false, default: {}
end
CALLBACK_FILTER_TYPES = [:before, :after, :around]
# 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.
def run_callbacks(kind)
callbacks = __callbacks[kind.to_sym]
if callbacks.empty?
yield if block_given?
else
env = Filters::Environment.new(self, false, nil)
next_sequence = callbacks.compile
# 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 = 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&.first
break env.value
end
end
invoke_sequence.call
end
end
end
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.
def halted_callback_hook(filter, name)
end
module Conditionals # :nodoc:
class Value
def initialize(&block)
@block = block
end
def call(target, value); @block.call(value); end
end
end
module Filters
Environment = Struct.new(:target, :halted, :value)
class Before
def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter, name)
halted_lambda = chain_config[:terminator]
if user_conditions.any?
halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter, name)
else
halting(callback_sequence, user_callback, halted_lambda, filter, name)
end
end
def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter, name)
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, name
end
end
env
end
end
private_class_method :halting_and_conditional
def self.halting(callback_sequence, user_callback, halted_lambda, filter, name)
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, name
end
end
env
end
end
private_class_method :halting
end
class After
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
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
private_class_method :halting_and_conditional
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
private_class_method :halting
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
private_class_method :conditional
def self.simple(callback_sequence, user_callback)
callback_sequence.after do |env|
user_callback.call env.target, env.value
env
end
end
private_class_method :simple
end
end
class Callback #:nodoc:#
def self.build(chain, filter, kind, options)
if filter.is_a?(String)
raise ArgumentError, <<-MSG.squish
Passing string to define a callback is not supported. See the `.set_callback`
documentation to see supported values.
MSG
end
new chain.name, filter, kind, options, chain.config
end
attr_accessor :kind, :name
attr_reader :chain_config
def initialize(name, filter, kind, options, chain_config)
@chain_config = chain_config
@name = name
@kind = kind
@filter = filter
@key = compute_identifier filter
@if = check_conditionals(options[:if])
@unless = check_conditionals(options[:unless])
end
def filter; @key; end
def raw_filter; @filter; end
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
def matches?(_kind, _filter)
@kind == _kind && filter == _filter
end
def duplicates?(other)
case @filter
when Symbol
matches?(other.kind, other.filter)
else
false
end
end
# Wraps code with filter
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, name)
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
def current_scopes
Array(chain_config[:scope]).map { |s| public_send(s) }
end
private
EMPTY_ARRAY = [].freeze
private_constant :EMPTY_ARRAY
def check_conditionals(conditionals)
return EMPTY_ARRAY if conditionals.blank?
conditionals = Array(conditionals)
if conditionals.any? { |c| c.is_a?(String) }
raise ArgumentError, <<-MSG.squish
Passing string to be evaluated in :if and :unless conditional
options is not supported. Pass a symbol for an instance method,
or a lambda, proc or block, instead.
MSG
end
conditionals.freeze
end
def compute_identifier(filter)
case filter
when ::Proc
filter.object_id
else
filter
end
end
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).
class CallTemplate # :nodoc:
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.
def expand(target, value, block)
expanded = [@override_target || target, @override_block || block, @method_name]
@arguments.each do |arg|
case arg
when :value then expanded << value
when :target then expanded << target
when :block then expanded << (block || raise(ArgumentError))
end
end
expanded
end
# Return a lambda that will make this call when given the input
# values.
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.
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.
# 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.
def self.build(filter, callback)
case filter
when Symbol
new(nil, filter, [], nil)
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
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
class CallbackSequence # :nodoc:
def initialize(nested = nil, call_template = nil, user_conditions = nil)
@nested = nested
@call_template = call_template
@user_conditions = user_conditions
@before = []
@after = []
end
def before(&before)
@before.unshift(before)
self
end
def after(&after)
@after.push(after)
self
end
def around(call_template, user_conditions)
CallbackSequence.new(self, call_template, user_conditions)
end
def skip?(arg)
arg.halted || !@user_conditions.all? { |c| c.call(arg.target, arg.value) }
end
attr_reader :nested
def final?
!@call_template
end
def expand_call_template(arg, block)
@call_template.expand(arg.target, arg.value, block)
end
def invoke_before(arg)
@before.each { |b| b.call(arg) }
end
def invoke_after(arg)
@after.each { |a| a.call(arg) }
end
end
class CallbackChain #:nodoc:#
include Enumerable
attr_reader :name, :config
def initialize(name, config)
@name = name
@config = {
scope: [:kind],
terminator: default_terminator
}.merge!(config)
@chain = []
@callbacks = nil
@mutex = Mutex.new
end
def each(&block); @chain.each(&block); end
def index(o); @chain.index(o); end
def empty?; @chain.empty?; end
def insert(index, o)
@callbacks = nil
@chain.insert(index, o)
end
def delete(o)
@callbacks = nil
@chain.delete(o)
end
def clear
@callbacks = nil
@chain.clear
self
end
def initialize_copy(other)
@callbacks = nil
@chain = other.chain.dup
@mutex = Mutex.new
end
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
def append(*callbacks)
callbacks.each { |c| append_one(c) }
end
def prepend(*callbacks)
callbacks.each { |c| prepend_one(c) }
end
protected
attr_reader :chain
private
def append_one(callback)
@callbacks = nil
remove_duplicates(callback)
@chain.push(callback)
end
def prepend_one(callback)
@callbacks = nil
remove_duplicates(callback)
@chain.unshift(callback)
end
def remove_duplicates(callback)
@callbacks = nil
@chain.delete_if { |c| callback.duplicates?(c) }
end
def default_terminator
Proc.new do |target, result_lambda|
terminate = true
catch(:abort) do
result_lambda.call
terminate = false
end
terminate
end
end
end
module ClassMethods
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.
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; 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 or an array of symbols, each naming an instance
# method or a proc; the callback will be called only when they all return
# a true value.
#
# If a proc is given, its body is evaluated in the context of the
# current object. It can also optionally accept the current object as
# an argument.
# * <tt>:unless</tt> - A symbol or an array of symbols, each naming an
# instance method or a proc; the callback will be called only when they
# all return a false value.
#
# If a proc is given, its body is evaluated in the context of the
# current object. It can also optionally accept the current object as
# an argument.
# * <tt>:prepend</tt> - If +true+, the callback will be prepended to the
# existing chain rather than appended.
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>).
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.
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 has no effect if <tt>:terminator</tt>
# option is set to +nil+.
#
# * <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+.
def define_callbacks(*names)
options = names.extract_options!
names.each do |name|
name = name.to_sym
([self] + ActiveSupport::DescendantsTracker.descendants(self)).each do |target|
target.set_callbacks name, CallbackChain.new(name, options)
end
module_eval <<-RUBY, __FILE__, __LINE__ + 1
def _run_#{name}_callbacks(&block)
run_callbacks #{name.inspect}, &block
end
def self._#{name}_callbacks
get_callbacks(#{name.inspect})
end
def self._#{name}_callbacks=(value)
set_callbacks(#{name.inspect}, value)
end
def _#{name}_callbacks
__callbacks[#{name.inspect}]
end
RUBY
end
end
protected
def get_callbacks(name) # :nodoc:
__callbacks[name.to_sym]
end
if Module.instance_method(:method_defined?).arity == 1 # Ruby 2.5 and older
def set_callbacks(name, callbacks) # :nodoc:
self.__callbacks = __callbacks.merge(name.to_sym => callbacks)
end
else # Ruby 2.6 and newer
def set_callbacks(name, callbacks) # :nodoc:
unless singleton_class.method_defined?(:__callbacks, false)
self.__callbacks = __callbacks.dup
end
self.__callbacks[name.to_sym] = callbacks
self.__callbacks
end
end
end
end
end

View File

@ -1,215 +0,0 @@
# frozen_string_literal: true
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
#
# === Prepending concerns
#
# Just like <tt>include</tt>, concerns also support <tt>prepend</tt> with a corresponding
# <tt>prepended do</tt> callback. <tt>module ClassMethods</tt> or <tt>class_methods do</tt> are
# prepended as well.
#
# <tt>prepend</tt> is also used for any dependencies.
module Concern
class MultipleIncludedBlocks < StandardError #:nodoc:
def initialize
super "Cannot define multiple 'included' blocks for a Concern"
end
end
class MultiplePrependBlocks < StandardError #:nodoc:
def initialize
super "Cannot define multiple 'prepended' blocks for a Concern"
end
end
def self.extended(base) #:nodoc:
base.instance_variable_set(:@_dependencies, [])
end
def append_features(base) #:nodoc:
if base.instance_variable_defined?(:@_dependencies)
base.instance_variable_get(:@_dependencies) << self
false
else
return false if base < self
@_dependencies.each { |dep| base.include(dep) }
super
base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block)
end
end
def prepend_features(base) #:nodoc:
if base.instance_variable_defined?(:@_dependencies)
base.instance_variable_get(:@_dependencies).unshift self
false
else
return false if base < self
@_dependencies.each { |dep| base.prepend(dep) }
super
base.singleton_class.prepend const_get(:ClassMethods) if const_defined?(:ClassMethods)
base.class_eval(&@_prepended_block) if instance_variable_defined?(:@_prepended_block)
end
end
# Evaluate given block in context of base class,
# so that you can write class macros here.
# When you define more than one +included+ block, it raises an exception.
def included(base = nil, &block)
if base.nil?
if instance_variable_defined?(:@_included_block)
if @_included_block.source_location != block.source_location
raise MultipleIncludedBlocks
end
else
@_included_block = block
end
else
super
end
end
# Evaluate given block in context of base class,
# so that you can write class macros here.
# When you define more than one +prepended+ block, it raises an exception.
def prepended(base = nil, &block)
if base.nil?
if instance_variable_defined?(:@_prepended_block)
if @_prepended_block.source_location != block.source_location
raise MultiplePrependBlocks
end
else
@_prepended_block = block
end
else
super
end
end
# Define class methods from given block.
# You can define private class methods as well.
#
# module Example
# extend ActiveSupport::Concern
#
# class_methods do
# def foo; puts 'foo'; end
#
# private
# def bar; puts 'bar'; end
# end
# end
#
# class Buzz
# include Example
# end
#
# Buzz.foo # => "foo"
# Buzz.bar # => private method 'bar' called for Buzz:Class(NoMethodError)
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

View File

@ -1,146 +0,0 @@
# frozen_string_literal: true
require "active_support/concern"
require "active_support/ordered_options"
module ActiveSupport
# Configurable provides a <tt>config</tt> method to store and retrieve
# configuration options as an <tt>OrderedOptions</tt>.
module Configurable
extend ActiveSupport::Concern
class Configuration < ActiveSupport::InheritableOptions
def compile_methods!
self.class.compile_methods!(keys)
end
# Compiles reader methods so we don't have to go through method_missing.
def self.compile_methods!(keys)
keys.reject { |m| method_defined?(m) }.each do |key|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{key}; _get(#{key.inspect}); end
RUBY
end
end
end
module ClassMethods
def config
@_config ||= if respond_to?(:superclass) && superclass.respond_to?(:config)
superclass.config.inheritable_copy
else
# create a new "anonymous" class that will host the compiled reader methods
Class.new(Configuration).new
end
end
def configure
yield config
end
# Allows you to add shortcut so that you don't have to refer to attribute
# through config. Also look at the example for config to contrast.
#
# Defines both class and instance config accessors.
#
# class User
# include ActiveSupport::Configurable
# config_accessor :allowed_access
# end
#
# User.allowed_access # => nil
# User.allowed_access = false
# User.allowed_access # => false
#
# user = User.new
# user.allowed_access # => false
# user.allowed_access = true
# user.allowed_access # => true
#
# User.allowed_access # => false
#
# The attribute name must be a valid method name in Ruby.
#
# class User
# include ActiveSupport::Configurable
# config_accessor :"1_Badname"
# end
# # => NameError: invalid config attribute name
#
# To omit the instance writer method, pass <tt>instance_writer: false</tt>.
# To omit the instance reader method, pass <tt>instance_reader: false</tt>.
#
# class User
# include ActiveSupport::Configurable
# config_accessor :allowed_access, instance_reader: false, instance_writer: false
# end
#
# User.allowed_access = false
# User.allowed_access # => false
#
# User.new.allowed_access = true # => NoMethodError
# User.new.allowed_access # => NoMethodError
#
# Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
#
# class User
# include ActiveSupport::Configurable
# config_accessor :allowed_access, instance_accessor: false
# end
#
# User.allowed_access = false
# User.allowed_access # => false
#
# User.new.allowed_access = true # => NoMethodError
# User.new.allowed_access # => NoMethodError
#
# Also you can pass a block to set up the attribute with a default value.
#
# class User
# include ActiveSupport::Configurable
# config_accessor :hair_colors do
# [:brown, :black, :blonde, :red]
# end
# end
#
# User.hair_colors # => [:brown, :black, :blonde, :red]
def config_accessor(*names, instance_reader: true, instance_writer: true, instance_accessor: true) # :doc:
names.each do |name|
raise NameError.new("invalid config attribute name") unless /\A[_A-Za-z]\w*\z/.match?(name)
reader, reader_line = "def #{name}; config.#{name}; end", __LINE__
writer, writer_line = "def #{name}=(value); config.#{name} = value; end", __LINE__
singleton_class.class_eval reader, __FILE__, reader_line
singleton_class.class_eval writer, __FILE__, writer_line
if instance_accessor
class_eval reader, __FILE__, reader_line if instance_reader
class_eval writer, __FILE__, writer_line if instance_writer
end
send("#{name}=", yield) if block_given?
end
end
private :config_accessor
end
# Reads and writes attributes from a configuration <tt>OrderedOptions</tt>.
#
# require "active_support/configurable"
#
# class User
# include ActiveSupport::Configurable
# end
#
# user = User.new
#
# user.config.allowed_access = true
# user.config.level = 1
#
# user.config.allowed_access # => true
# user.config.level # => 1
def config
@_config ||= self.class.config.inheritable_copy
end
end
end

View File

@ -1,51 +0,0 @@
# frozen_string_literal: true
module ActiveSupport
# Reads a YAML configuration file, evaluating any ERB, then
# parsing the resulting YAML.
#
# Warns in case of YAML confusing characters, like invisible
# non-breaking spaces.
class ConfigurationFile # :nodoc:
class FormatError < StandardError; end
def initialize(content_path)
@content_path = content_path.to_s
@content = read content_path
end
def self.parse(content_path, **options)
new(content_path).parse(**options)
end
def parse(context: nil, **options)
source = render(context)
if YAML.respond_to?(:unsafe_load)
YAML.unsafe_load(source, **options) || {}
else
YAML.load(source, **options) || {}
end
rescue Psych::SyntaxError => error
raise "YAML syntax error occurred while parsing #{@content_path}. " \
"Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " \
"Error: #{error.message}"
end
private
def read(content_path)
require "yaml"
require "erb"
File.read(content_path).tap do |content|
if content.include?("\u00A0")
warn "File contains invisible non-breaking spaces, you may want to remove those"
end
end
end
def render(context)
erb = ERB.new(@content).tap { |e| e.filename = @content_path }
context ? erb.result(context) : erb.result
end
end
end

View File

@ -1,5 +0,0 @@
# frozen_string_literal: true
Dir.glob(File.expand_path("core_ext/*.rb", __dir__)).sort.each do |path|
require path
end

View File

@ -1,9 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/array/wrap"
require "active_support/core_ext/array/access"
require "active_support/core_ext/array/conversions"
require "active_support/core_ext/array/extract"
require "active_support/core_ext/array/extract_options"
require "active_support/core_ext/array/grouping"
require "active_support/core_ext/array/inquiry"

View File

@ -1,213 +0,0 @@
# frozen_string_literal: true
require "active_support/xml_mini"
require "active_support/core_ext/hash/keys"
require "active_support/core_ext/string/inflections"
require "active_support/core_ext/object/to_param"
require "active_support/core_ext/object/to_query"
class Array
# Converts the array to a comma-separated sentence where the last element is
# joined by the connector word.
#
# You can pass the following options to change the default behavior. If you
# pass an option key that doesn't exist in the list below, it will raise an
# <tt>ArgumentError</tt>.
#
# ==== Options
#
# * <tt>:words_connector</tt> - The sign or word used to join the elements
# in arrays with two or more elements (default: ", ").
# * <tt>:two_words_connector</tt> - The sign or word used to join the elements
# in arrays with two elements (default: " and ").
# * <tt>:last_word_connector</tt> - The sign or word used to join the last element
# in arrays with three or more elements (default: ", and ").
# * <tt>:locale</tt> - If +i18n+ is available, you can set a locale and use
# the connector options defined on the 'support.array' namespace in the
# corresponding dictionary file.
#
# ==== Examples
#
# [].to_sentence # => ""
# ['one'].to_sentence # => "one"
# ['one', 'two'].to_sentence # => "one and two"
# ['one', 'two', 'three'].to_sentence # => "one, two, and three"
#
# ['one', 'two'].to_sentence(passing: 'invalid option')
# # => ArgumentError: Unknown key: :passing. Valid keys are: :words_connector, :two_words_connector, :last_word_connector, :locale
#
# ['one', 'two'].to_sentence(two_words_connector: '-')
# # => "one-two"
#
# ['one', 'two', 'three'].to_sentence(words_connector: ' or ', last_word_connector: ' or at least ')
# # => "one or two or at least three"
#
# Using <tt>:locale</tt> option:
#
# # Given this locale dictionary:
# #
# # es:
# # support:
# # array:
# # words_connector: " o "
# # two_words_connector: " y "
# # last_word_connector: " o al menos "
#
# ['uno', 'dos'].to_sentence(locale: :es)
# # => "uno y dos"
#
# ['uno', 'dos', 'tres'].to_sentence(locale: :es)
# # => "uno o dos o al menos tres"
def to_sentence(options = {})
options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
default_connectors = {
words_connector: ", ",
two_words_connector: " and ",
last_word_connector: ", and "
}
if defined?(I18n)
i18n_connectors = I18n.translate(:'support.array', locale: options[:locale], default: {})
default_connectors.merge!(i18n_connectors)
end
options = default_connectors.merge!(options)
case length
when 0
+""
when 1
+"#{self[0]}"
when 2
+"#{self[0]}#{options[:two_words_connector]}#{self[1]}"
else
+"#{self[0...-1].join(options[:words_connector])}#{options[:last_word_connector]}#{self[-1]}"
end
end
# Extends <tt>Array#to_s</tt> to convert a collection of elements into a
# comma separated id list if <tt>:db</tt> argument is given as the format.
#
# Blog.all.to_formatted_s(:db) # => "1,2,3"
# Blog.none.to_formatted_s(:db) # => "null"
# [1,2].to_formatted_s # => "[1, 2]"
def to_formatted_s(format = :default)
case format
when :db
if empty?
"null"
else
collect(&:id).join(",")
end
else
to_default_s
end
end
alias_method :to_default_s, :to_s
alias_method :to_s, :to_formatted_s
# Returns a string that represents the array in XML by invoking +to_xml+
# on each element. Active Record collections delegate their representation
# in XML to this method.
#
# All elements are expected to respond to +to_xml+, if any of them does
# not then an exception is raised.
#
# The root node reflects the class name of the first element in plural
# if all elements belong to the same type and that's not Hash:
#
# customer.projects.to_xml
#
# <?xml version="1.0" encoding="UTF-8"?>
# <projects type="array">
# <project>
# <amount type="decimal">20000.0</amount>
# <customer-id type="integer">1567</customer-id>
# <deal-date type="date">2008-04-09</deal-date>
# ...
# </project>
# <project>
# <amount type="decimal">57230.0</amount>
# <customer-id type="integer">1567</customer-id>
# <deal-date type="date">2008-04-15</deal-date>
# ...
# </project>
# </projects>
#
# Otherwise the root element is "objects":
#
# [{ foo: 1, bar: 2}, { baz: 3}].to_xml
#
# <?xml version="1.0" encoding="UTF-8"?>
# <objects type="array">
# <object>
# <bar type="integer">2</bar>
# <foo type="integer">1</foo>
# </object>
# <object>
# <baz type="integer">3</baz>
# </object>
# </objects>
#
# If the collection is empty the root element is "nil-classes" by default:
#
# [].to_xml
#
# <?xml version="1.0" encoding="UTF-8"?>
# <nil-classes type="array"/>
#
# To ensure a meaningful root element use the <tt>:root</tt> option:
#
# customer_with_no_projects.projects.to_xml(root: 'projects')
#
# <?xml version="1.0" encoding="UTF-8"?>
# <projects type="array"/>
#
# By default name of the node for the children of root is <tt>root.singularize</tt>.
# You can change it with the <tt>:children</tt> option.
#
# The +options+ hash is passed downwards:
#
# Message.all.to_xml(skip_types: true)
#
# <?xml version="1.0" encoding="UTF-8"?>
# <messages>
# <message>
# <created-at>2008-03-07T09:58:18+01:00</created-at>
# <id>1</id>
# <name>1</name>
# <updated-at>2008-03-07T09:58:18+01:00</updated-at>
# <user-id>1</user-id>
# </message>
# </messages>
#
def to_xml(options = {})
require "active_support/builder" unless defined?(Builder::XmlMarkup)
options = options.dup
options[:indent] ||= 2
options[:builder] ||= Builder::XmlMarkup.new(indent: options[:indent])
options[:root] ||= \
if first.class != Hash && all? { |e| e.is_a?(first.class) }
underscored = ActiveSupport::Inflector.underscore(first.class.name)
ActiveSupport::Inflector.pluralize(underscored).tr("/", "_")
else
"objects"
end
builder = options[:builder]
builder.instruct! unless options.delete(:skip_instruct)
root = ActiveSupport::XmlMini.rename_key(options[:root].to_s, options)
children = options.delete(:children) || root.singularize
attributes = options[:skip_types] ? {} : { type: "array" }
if empty?
builder.tag!(root, attributes)
else
builder.tag!(root, attributes) do
each { |value| ActiveSupport::XmlMini.to_tag(children, value, options) }
yield builder if block_given?
end
end
end
end

View File

@ -1,21 +0,0 @@
# frozen_string_literal: true
class Array
# Removes and returns the elements for which the block returns a true value.
# If no block is given, an Enumerator is returned instead.
#
# numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# odd_numbers = numbers.extract! { |number| number.odd? } # => [1, 3, 5, 7, 9]
# numbers # => [0, 2, 4, 6, 8]
def extract!
return to_enum(:extract!) { size } unless block_given?
extracted_elements = []
reject! do |element|
extracted_elements << element if yield(element)
end
extracted_elements
end
end

View File

@ -1,31 +0,0 @@
# frozen_string_literal: true
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.
def extractable_options?
instance_of?(Hash)
end
end
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}
def extract_options!
if last.is_a?(Hash) && last.extractable_options?
pop
else
{}
end
end
end

View File

@ -1,109 +0,0 @@
# frozen_string_literal: true
class Array
# Splits or iterates over the array in groups of size +number+,
# padding any remaining slots with +fill_with+ unless it is +false+.
#
# %w(1 2 3 4 5 6 7 8 9 10).in_groups_of(3) {|group| p group}
# ["1", "2", "3"]
# ["4", "5", "6"]
# ["7", "8", "9"]
# ["10", nil, nil]
#
# %w(1 2 3 4 5).in_groups_of(2, '&nbsp;') {|group| p group}
# ["1", "2"]
# ["3", "4"]
# ["5", "&nbsp;"]
#
# %w(1 2 3 4 5).in_groups_of(2, false) {|group| p group}
# ["1", "2"]
# ["3", "4"]
# ["5"]
def in_groups_of(number, fill_with = nil)
if number.to_i <= 0
raise ArgumentError,
"Group size must be a positive integer, was #{number.inspect}"
end
if fill_with == false
collection = self
else
# size % number gives how many extra we have;
# subtracting from number gives how many to add;
# modulo number ensures we don't add group of just fill.
padding = (number - size % number) % number
collection = dup.concat(Array.new(padding, fill_with))
end
if block_given?
collection.each_slice(number) { |slice| yield(slice) }
else
collection.each_slice(number).to_a
end
end
# Splits or iterates over the array in +number+ of groups, padding any
# remaining slots with +fill_with+ unless it is +false+.
#
# %w(1 2 3 4 5 6 7 8 9 10).in_groups(3) {|group| p group}
# ["1", "2", "3", "4"]
# ["5", "6", "7", nil]
# ["8", "9", "10", nil]
#
# %w(1 2 3 4 5 6 7 8 9 10).in_groups(3, '&nbsp;') {|group| p group}
# ["1", "2", "3", "4"]
# ["5", "6", "7", "&nbsp;"]
# ["8", "9", "10", "&nbsp;"]
#
# %w(1 2 3 4 5 6 7).in_groups(3, false) {|group| p group}
# ["1", "2", "3"]
# ["4", "5"]
# ["6", "7"]
def in_groups(number, fill_with = nil)
# size.div number gives minor group size;
# size % number gives how many objects need extra accommodation;
# each group hold either division or division + 1 items.
division = size.div number
modulo = size % number
# create a new array avoiding dup
groups = []
start = 0
number.times do |index|
length = division + (modulo > 0 && modulo > index ? 1 : 0)
groups << last_group = slice(start, length)
last_group << fill_with if fill_with != false &&
modulo > 0 && length == division
start += length
end
if block_given?
groups.each { |g| yield(g) }
else
groups
end
end
# Divides the array into one or more subarrays based on a delimiting +value+
# or the result of an optional block.
#
# [1, 2, 3, 4, 5].split(3) # => [[1, 2], [4, 5]]
# (1..10).to_a.split { |i| i % 3 == 0 } # => [[1, 2], [4, 5], [7, 8], [10]]
def split(value = nil)
arr = dup
result = []
if block_given?
while (idx = arr.index { |i| yield i })
result << arr.shift(idx)
arr.shift
end
else
while (idx = arr.index(value))
result << arr.shift(idx)
arr.shift
end
end
result << arr
end
end

View File

@ -1,19 +0,0 @@
# frozen_string_literal: true
require "active_support/array_inquirer"
class Array
# Wraps the array in an +ArrayInquirer+ object, which gives a friendlier way
# to check its string-like contents.
#
# pets = [:cat, :dog].inquiry
#
# pets.cat? # => true
# pets.ferret? # => false
#
# pets.any?(:cat, :ferret) # => true
# pets.any?(:ferret, :alligator) # => false
def inquiry
ActiveSupport::ArrayInquirer.new(self)
end
end

View File

@ -1,48 +0,0 @@
# frozen_string_literal: true
class Array
# Wraps its argument in an array unless it is already an array (or array-like).
#
# Specifically:
#
# * If the argument is +nil+ an empty array is returned.
# * Otherwise, if the argument responds to +to_ary+ it is invoked, and its result returned.
# * Otherwise, returns an array with the argument as its single element.
#
# Array.wrap(nil) # => []
# Array.wrap([1, 2, 3]) # => [1, 2, 3]
# Array.wrap(0) # => [0]
#
# This method is similar in purpose to <tt>Kernel#Array</tt>, but there are some differences:
#
# * If the argument responds to +to_ary+ the method is invoked. <tt>Kernel#Array</tt>
# moves on to try +to_a+ if the returned value is +nil+, but <tt>Array.wrap</tt> returns
# an array with the argument as its single element right away.
# * If the returned value from +to_ary+ is neither +nil+ nor an +Array+ object, <tt>Kernel#Array</tt>
# raises an exception, while <tt>Array.wrap</tt> does not, it just returns the value.
# * It does not call +to_a+ on the argument, if the argument does not respond to +to_ary+
# it returns an array with the argument as its single element.
#
# The last point is easily explained with some enumerables:
#
# Array(foo: :bar) # => [[:foo, :bar]]
# Array.wrap(foo: :bar) # => [{:foo=>:bar}]
#
# There's also a related idiom that uses the splat operator:
#
# [*object]
#
# which returns <tt>[]</tt> for +nil+, but calls to <tt>Array(object)</tt> otherwise.
#
# The differences with <tt>Kernel#Array</tt> explained above
# apply to the rest of <tt>object</tt>s.
def self.wrap(object)
if object.nil?
[]
elsif object.respond_to?(:to_ary)
object.to_ary || [object]
else
[object]
end
end
end

View File

@ -1,16 +0,0 @@
# frozen_string_literal: true
require "benchmark"
class << Benchmark
# Benchmark realtime in milliseconds.
#
# Benchmark.realtime { User.all }
# # => 8.0e-05
#
# Benchmark.ms { User.all }
# # => 0.074
def ms(&block)
1000 * realtime(&block)
end
end

View File

@ -1,3 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/big_decimal/conversions"

View File

@ -1,14 +0,0 @@
# frozen_string_literal: true
require "bigdecimal"
require "bigdecimal/util"
module ActiveSupport
module BigDecimalWithDefaultFormat #:nodoc:
def to_s(format = "F")
super(format)
end
end
end
BigDecimal.prepend(ActiveSupport::BigDecimalWithDefaultFormat)

View File

@ -1,4 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/class/attribute"
require "active_support/core_ext/class/subclasses"

View File

@ -1,131 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/module/redefine_method"
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.
#
# ==== Options
#
# * <tt>:instance_reader</tt> - Sets the instance reader method (defaults to true).
# * <tt>:instance_writer</tt> - Sets the instance writer method (defaults to true).
# * <tt>:instance_accessor</tt> - Sets both instance methods (defaults to true).
# * <tt>:instance_predicate</tt> - Sets a predicate method (defaults to true).
# * <tt>:default</tt> - Sets a default value for the attribute (defaults to nil).
#
# ==== Examples
#
# 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>.
#
# To set a default value for the attribute, pass <tt>default:</tt>, like so:
#
# class_attribute :settings, default: {}
def class_attribute(*attrs, instance_accessor: true,
instance_reader: instance_accessor, instance_writer: instance_accessor, instance_predicate: true, default: nil)
class_methods, methods = [], []
attrs.each do |name|
unless name.is_a?(Symbol) || name.is_a?(String)
raise TypeError, "#{name.inspect} is not a symbol nor a string"
end
class_methods << <<~RUBY # In case the method exists and is not public
silence_redefinition_of_method def #{name}
end
RUBY
methods << <<~RUBY if instance_reader
silence_redefinition_of_method def #{name}
defined?(@#{name}) ? @#{name} : self.class.#{name}
end
RUBY
class_methods << <<~RUBY
silence_redefinition_of_method def #{name}=(value)
redefine_method(:#{name}) { value } if singleton_class?
redefine_singleton_method(:#{name}) { value }
value
end
RUBY
methods << <<~RUBY if instance_writer
silence_redefinition_of_method(:#{name}=)
attr_writer :#{name}
RUBY
if instance_predicate
class_methods << "silence_redefinition_of_method def #{name}?; !!self.#{name}; end"
if instance_reader
methods << "silence_redefinition_of_method def #{name}?; !!self.#{name}; end"
end
end
end
location = caller_locations(1, 1).first
class_eval(["class << self", *class_methods, "end", *methods].join(";").tr("\n", ";"), location.path, location.lineno)
attrs.each { |name| public_send("#{name}=", default) }
end
end

View File

@ -1,6 +0,0 @@
# frozen_string_literal: true
# cattr_* became mattr_* aliases in 7dfbd91b0780fbd6a1dd9bfbc176e10894871d2d,
# but we keep this around for libraries that directly require it knowing they
# want cattr_*. No need to deprecate.
require "active_support/core_ext/module/attribute_accessors"

View File

@ -1,33 +0,0 @@
# frozen_string_literal: true
class Class
# Returns an array with all classes that are < than its receiver.
#
# class C; end
# C.descendants # => []
#
# class B < C; end
# C.descendants # => [B]
#
# class A < B; end
# C.descendants # => [B, A]
#
# class D < C; end
# C.descendants # => [B, A, D]
def descendants
ObjectSpace.each_object(singleton_class).reject do |k|
k.singleton_class? || k == self
end
end
# Returns an array with the direct children of +self+.
#
# class Foo; end
# class Bar < Foo; end
# class Baz < Bar; end
#
# Foo.subclasses # => [Bar]
def subclasses
descendants.select { |descendant| descendant.superclass == self }
end
end

View File

@ -1,7 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/date/acts_like"
require "active_support/core_ext/date/blank"
require "active_support/core_ext/date/calculations"
require "active_support/core_ext/date/conversions"
require "active_support/core_ext/date/zones"

View File

@ -1,10 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/object/acts_like"
class Date
# Duck-types as a Date-like class. See Object#acts_like?.
def acts_like_date?
true
end
end

View File

@ -1,14 +0,0 @@
# frozen_string_literal: true
require "date"
class Date #:nodoc:
# No Date is blank:
#
# Date.today.blank? # => false
#
# @return [false]
def blank?
false
end
end

View File

@ -1,146 +0,0 @@
# frozen_string_literal: true
require "date"
require "active_support/duration"
require "active_support/core_ext/object/acts_like"
require "active_support/core_ext/date/zones"
require "active_support/core_ext/time/zones"
require "active_support/core_ext/date_and_time/calculations"
class Date
include DateAndTime::Calculations
class << self
attr_accessor :beginning_of_week_default
# Returns the week start (e.g. :monday) for the current request, if this has been set (via Date.beginning_of_week=).
# If <tt>Date.beginning_of_week</tt> has not been set for the current request, returns the week start specified in <tt>config.beginning_of_week</tt>.
# If no config.beginning_of_week was specified, returns :monday.
def beginning_of_week
Thread.current[:beginning_of_week] || beginning_of_week_default || :monday
end
# Sets <tt>Date.beginning_of_week</tt> to a week start (e.g. :monday) for current request/thread.
#
# This method accepts any of the following day symbols:
# :monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday
def beginning_of_week=(week_start)
Thread.current[:beginning_of_week] = find_beginning_of_week!(week_start)
end
# Returns week start day symbol (e.g. :monday), or raises an +ArgumentError+ for invalid day symbol.
def find_beginning_of_week!(week_start)
raise ArgumentError, "Invalid beginning of week: #{week_start}" unless ::Date::DAYS_INTO_WEEK.key?(week_start)
week_start
end
# Returns a new Date representing the date 1 day ago (i.e. yesterday's date).
def yesterday
::Date.current.yesterday
end
# Returns a new Date representing the date 1 day after today (i.e. tomorrow's date).
def tomorrow
::Date.current.tomorrow
end
# Returns Time.zone.today when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise just returns Date.today.
def current
::Time.zone ? ::Time.zone.today : ::Date.today
end
end
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
# and then subtracts the specified number of seconds.
def ago(seconds)
in_time_zone.since(-seconds)
end
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
# and then adds the specified number of seconds
def since(seconds)
in_time_zone.since(seconds)
end
alias :in :since
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
def beginning_of_day
in_time_zone
end
alias :midnight :beginning_of_day
alias :at_midnight :beginning_of_day
alias :at_beginning_of_day :beginning_of_day
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the middle of the day (12:00)
def middle_of_day
in_time_zone.middle_of_day
end
alias :midday :middle_of_day
alias :noon :middle_of_day
alias :at_midday :middle_of_day
alias :at_noon :middle_of_day
alias :at_middle_of_day :middle_of_day
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the end of the day (23:59:59)
def end_of_day
in_time_zone.end_of_day
end
alias :at_end_of_day :end_of_day
def plus_with_duration(other) #:nodoc:
if ActiveSupport::Duration === other
other.since(self)
else
plus_without_duration(other)
end
end
alias_method :plus_without_duration, :+
alias_method :+, :plus_with_duration
def minus_with_duration(other) #:nodoc:
if ActiveSupport::Duration === other
plus_with_duration(-other)
else
minus_without_duration(other)
end
end
alias_method :minus_without_duration, :-
alias_method :-, :minus_with_duration
# Provides precise Date calculations for years, months, and days. The +options+ parameter takes a hash with
# any of these keys: <tt>:years</tt>, <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>.
def advance(options)
d = self
d = d >> options[:years] * 12 if options[:years]
d = d >> options[:months] if options[:months]
d = d + options[:weeks] * 7 if options[:weeks]
d = d + options[:days] if options[:days]
d
end
# Returns a new Date where one or more of the elements have been changed according to the +options+ parameter.
# The +options+ parameter is a hash with a combination of these keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>.
#
# Date.new(2007, 5, 12).change(day: 1) # => Date.new(2007, 5, 1)
# Date.new(2007, 5, 12).change(year: 2005, month: 1) # => Date.new(2005, 1, 12)
def change(options)
::Date.new(
options.fetch(:year, year),
options.fetch(:month, month),
options.fetch(:day, day)
)
end
# Allow Date to be compared with Time by converting to DateTime and relying on the <=> from there.
def compare_with_coercion(other)
if other.is_a?(Time)
to_datetime <=> other
else
compare_without_coercion(other)
end
end
alias_method :compare_without_coercion, :<=>
alias_method :<=>, :compare_with_coercion
end

View File

@ -1,97 +0,0 @@
# frozen_string_literal: true
require "date"
require "active_support/inflector/methods"
require "active_support/core_ext/date/zones"
require "active_support/core_ext/module/redefine_method"
class Date
DATE_FORMATS = {
short: "%d %b",
long: "%B %d, %Y",
db: "%Y-%m-%d",
inspect: "%Y-%m-%d",
number: "%Y%m%d",
long_ordinal: lambda { |date|
day_format = ActiveSupport::Inflector.ordinalize(date.day)
date.strftime("%B #{day_format}, %Y") # => "April 25th, 2007"
},
rfc822: "%d %b %Y",
iso8601: lambda { |date| date.iso8601 }
}
# Convert to a formatted string. See DATE_FORMATS for predefined formats.
#
# This method is aliased to <tt>to_s</tt>.
#
# date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
#
# date.to_formatted_s(:db) # => "2007-11-10"
# date.to_s(:db) # => "2007-11-10"
#
# date.to_formatted_s(:short) # => "10 Nov"
# date.to_formatted_s(:number) # => "20071110"
# date.to_formatted_s(:long) # => "November 10, 2007"
# date.to_formatted_s(:long_ordinal) # => "November 10th, 2007"
# date.to_formatted_s(:rfc822) # => "10 Nov 2007"
# date.to_formatted_s(:iso8601) # => "2007-11-10"
#
# == Adding your own date formats to to_formatted_s
# You can add your own formats to the Date::DATE_FORMATS hash.
# Use the format name as the hash key and either a strftime string
# or Proc instance that takes a date argument as the value.
#
# # config/initializers/date_formats.rb
# Date::DATE_FORMATS[:month_and_year] = '%B %Y'
# Date::DATE_FORMATS[:short_ordinal] = ->(date) { date.strftime("%B #{date.day.ordinalize}") }
def to_formatted_s(format = :default)
if formatter = DATE_FORMATS[format]
if formatter.respond_to?(:call)
formatter.call(self).to_s
else
strftime(formatter)
end
else
to_default_s
end
end
alias_method :to_default_s, :to_s
alias_method :to_s, :to_formatted_s
# Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005"
def readable_inspect
strftime("%a, %d %b %Y")
end
alias_method :default_inspect, :inspect
alias_method :inspect, :readable_inspect
silence_redefinition_of_method :to_time
# Converts a Date instance to a Time, where the time is set to the beginning of the day.
# The timezone can be either :local or :utc (default :local).
#
# date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
#
# date.to_time # => 2007-11-10 00:00:00 0800
# date.to_time(:local) # => 2007-11-10 00:00:00 0800
#
# date.to_time(:utc) # => 2007-11-10 00:00:00 UTC
#
# NOTE: The :local timezone is Ruby's *process* timezone, i.e. ENV['TZ'].
# If the *application's* timezone is needed, then use +in_time_zone+ instead.
def to_time(form = :local)
raise ArgumentError, "Expected :local or :utc, got #{form.inspect}." unless [:local, :utc].include?(form)
::Time.public_send(form, year, month, day)
end
silence_redefinition_of_method :xmlschema
# Returns a string which represents the time in used time zone as DateTime
# defined by XML Schema:
#
# date = Date.new(2015, 05, 23) # => Sat, 23 May 2015
# date.xmlschema # => "2015-05-23T00:00:00+04:00"
def xmlschema
in_time_zone.xmlschema
end
end

View File

@ -1,8 +0,0 @@
# frozen_string_literal: true
require "date"
require "active_support/core_ext/date_and_time/zones"
class Date
include DateAndTime::Zones
end

View File

@ -1,364 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/object/try"
require "active_support/core_ext/date_time/conversions"
module DateAndTime
module Calculations
DAYS_INTO_WEEK = {
sunday: 0,
monday: 1,
tuesday: 2,
wednesday: 3,
thursday: 4,
friday: 5,
saturday: 6
}
WEEKEND_DAYS = [ 6, 0 ]
# Returns a new date/time representing yesterday.
def yesterday
advance(days: -1)
end
# Returns a new date/time representing tomorrow.
def tomorrow
advance(days: 1)
end
# Returns true if the date/time is today.
def today?
to_date == ::Date.current
end
# Returns true if the date/time is tomorrow.
def tomorrow?
to_date == ::Date.current.tomorrow
end
alias :next_day? :tomorrow?
# Returns true if the date/time is yesterday.
def yesterday?
to_date == ::Date.current.yesterday
end
alias :prev_day? :yesterday?
# Returns true if the date/time is in the past.
def past?
self < self.class.current
end
# Returns true if the date/time is in the future.
def future?
self > self.class.current
end
# Returns true if the date/time falls on a Saturday or Sunday.
def on_weekend?
WEEKEND_DAYS.include?(wday)
end
# Returns true if the date/time does not fall on a Saturday or Sunday.
def on_weekday?
!WEEKEND_DAYS.include?(wday)
end
# Returns true if the date/time falls before <tt>date_or_time</tt>.
def before?(date_or_time)
self < date_or_time
end
# Returns true if the date/time falls after <tt>date_or_time</tt>.
def after?(date_or_time)
self > date_or_time
end
# Returns a new date/time the specified number of days ago.
def days_ago(days)
advance(days: -days)
end
# Returns a new date/time the specified number of days in the future.
def days_since(days)
advance(days: days)
end
# Returns a new date/time the specified number of weeks ago.
def weeks_ago(weeks)
advance(weeks: -weeks)
end
# Returns a new date/time the specified number of weeks in the future.
def weeks_since(weeks)
advance(weeks: weeks)
end
# Returns a new date/time the specified number of months ago.
def months_ago(months)
advance(months: -months)
end
# Returns a new date/time the specified number of months in the future.
def months_since(months)
advance(months: months)
end
# Returns a new date/time the specified number of years ago.
def years_ago(years)
advance(years: -years)
end
# Returns a new date/time the specified number of years in the future.
def years_since(years)
advance(years: years)
end
# Returns a new date/time at the start of the month.
#
# today = Date.today # => Thu, 18 Jun 2015
# today.beginning_of_month # => Mon, 01 Jun 2015
#
# +DateTime+ objects will have a time set to 0:00.
#
# now = DateTime.current # => Thu, 18 Jun 2015 15:23:13 +0000
# now.beginning_of_month # => Mon, 01 Jun 2015 00:00:00 +0000
def beginning_of_month
first_hour(change(day: 1))
end
alias :at_beginning_of_month :beginning_of_month
# Returns a new date/time at the start of the quarter.
#
# today = Date.today # => Fri, 10 Jul 2015
# today.beginning_of_quarter # => Wed, 01 Jul 2015
#
# +DateTime+ objects will have a time set to 0:00.
#
# now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000
# now.beginning_of_quarter # => Wed, 01 Jul 2015 00:00:00 +0000
def beginning_of_quarter
first_quarter_month = month - (2 + month) % 3
beginning_of_month.change(month: first_quarter_month)
end
alias :at_beginning_of_quarter :beginning_of_quarter
# Returns a new date/time at the end of the quarter.
#
# today = Date.today # => Fri, 10 Jul 2015
# today.end_of_quarter # => Wed, 30 Sep 2015
#
# +DateTime+ objects will have a time set to 23:59:59.
#
# now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000
# now.end_of_quarter # => Wed, 30 Sep 2015 23:59:59 +0000
def end_of_quarter
last_quarter_month = month + (12 - month) % 3
beginning_of_month.change(month: last_quarter_month).end_of_month
end
alias :at_end_of_quarter :end_of_quarter
# Returns a new date/time at the beginning of the year.
#
# today = Date.today # => Fri, 10 Jul 2015
# today.beginning_of_year # => Thu, 01 Jan 2015
#
# +DateTime+ objects will have a time set to 0:00.
#
# now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000
# now.beginning_of_year # => Thu, 01 Jan 2015 00:00:00 +0000
def beginning_of_year
change(month: 1).beginning_of_month
end
alias :at_beginning_of_year :beginning_of_year
# Returns a new date/time representing the given day in the next week.
#
# today = Date.today # => Thu, 07 May 2015
# today.next_week # => Mon, 11 May 2015
#
# The +given_day_in_next_week+ defaults to the beginning of the week
# which is determined by +Date.beginning_of_week+ or +config.beginning_of_week+
# when set.
#
# today = Date.today # => Thu, 07 May 2015
# today.next_week(:friday) # => Fri, 15 May 2015
#
# +DateTime+ objects have their time set to 0:00 unless +same_time+ is true.
#
# now = DateTime.current # => Thu, 07 May 2015 13:31:16 +0000
# now.next_week # => Mon, 11 May 2015 00:00:00 +0000
def next_week(given_day_in_next_week = Date.beginning_of_week, same_time: false)
result = first_hour(weeks_since(1).beginning_of_week.days_since(days_span(given_day_in_next_week)))
same_time ? copy_time_to(result) : result
end
# Returns a new date/time representing the next weekday.
def next_weekday
if next_day.on_weekend?
next_week(:monday, same_time: true)
else
next_day
end
end
# Short-hand for months_since(3)
def next_quarter
months_since(3)
end
# Returns a new date/time representing the given day in the previous week.
# Week is assumed to start on +start_day+, default is
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
# DateTime objects have their time set to 0:00 unless +same_time+ is true.
def prev_week(start_day = Date.beginning_of_week, same_time: false)
result = first_hour(weeks_ago(1).beginning_of_week.days_since(days_span(start_day)))
same_time ? copy_time_to(result) : result
end
alias_method :last_week, :prev_week
# Returns a new date/time representing the previous weekday.
def prev_weekday
if prev_day.on_weekend?
copy_time_to(beginning_of_week(:friday))
else
prev_day
end
end
alias_method :last_weekday, :prev_weekday
# Short-hand for months_ago(1).
def last_month
months_ago(1)
end
# Short-hand for months_ago(3).
def prev_quarter
months_ago(3)
end
alias_method :last_quarter, :prev_quarter
# Short-hand for years_ago(1).
def last_year
years_ago(1)
end
# Returns the number of days to the start of the week on the given day.
# Week is assumed to start on +start_day+, default is
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
def days_to_week_start(start_day = Date.beginning_of_week)
start_day_number = DAYS_INTO_WEEK.fetch(start_day)
(wday - start_day_number) % 7
end
# Returns a new date/time representing the start of this week on the given day.
# Week is assumed to start on +start_day+, default is
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
# +DateTime+ objects have their time set to 0:00.
def beginning_of_week(start_day = Date.beginning_of_week)
result = days_ago(days_to_week_start(start_day))
acts_like?(:time) ? result.midnight : result
end
alias :at_beginning_of_week :beginning_of_week
# Returns Monday of this week assuming that week starts on Monday.
# +DateTime+ objects have their time set to 0:00.
def monday
beginning_of_week(:monday)
end
# Returns a new date/time representing the end of this week on the given day.
# Week is assumed to start on +start_day+, default is
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
# DateTime objects have their time set to 23:59:59.
def end_of_week(start_day = Date.beginning_of_week)
last_hour(days_since(6 - days_to_week_start(start_day)))
end
alias :at_end_of_week :end_of_week
# Returns Sunday of this week assuming that week starts on Monday.
# +DateTime+ objects have their time set to 23:59:59.
def sunday
end_of_week(:monday)
end
# Returns a new date/time representing the end of the month.
# DateTime objects will have a time set to 23:59:59.
def end_of_month
last_day = ::Time.days_in_month(month, year)
last_hour(days_since(last_day - day))
end
alias :at_end_of_month :end_of_month
# Returns a new date/time representing the end of the year.
# DateTime objects will have a time set to 23:59:59.
def end_of_year
change(month: 12).end_of_month
end
alias :at_end_of_year :end_of_year
# Returns a Range representing the whole day of the current date/time.
def all_day
beginning_of_day..end_of_day
end
# Returns a Range representing the whole week of the current date/time.
# Week starts on start_day, default is <tt>Date.beginning_of_week</tt> or <tt>config.beginning_of_week</tt> when set.
def all_week(start_day = Date.beginning_of_week)
beginning_of_week(start_day)..end_of_week(start_day)
end
# Returns a Range representing the whole month of the current date/time.
def all_month
beginning_of_month..end_of_month
end
# Returns a Range representing the whole quarter of the current date/time.
def all_quarter
beginning_of_quarter..end_of_quarter
end
# Returns a Range representing the whole year of the current date/time.
def all_year
beginning_of_year..end_of_year
end
# Returns a new date/time representing the next occurrence of the specified day of week.
#
# today = Date.today # => Thu, 14 Dec 2017
# today.next_occurring(:monday) # => Mon, 18 Dec 2017
# today.next_occurring(:thursday) # => Thu, 21 Dec 2017
def next_occurring(day_of_week)
from_now = DAYS_INTO_WEEK.fetch(day_of_week) - wday
from_now += 7 unless from_now > 0
advance(days: from_now)
end
# Returns a new date/time representing the previous occurrence of the specified day of week.
#
# today = Date.today # => Thu, 14 Dec 2017
# today.prev_occurring(:monday) # => Mon, 11 Dec 2017
# today.prev_occurring(:thursday) # => Thu, 07 Dec 2017
def prev_occurring(day_of_week)
ago = wday - DAYS_INTO_WEEK.fetch(day_of_week)
ago += 7 unless ago > 0
advance(days: -ago)
end
private
def first_hour(date_or_time)
date_or_time.acts_like?(:time) ? date_or_time.beginning_of_day : date_or_time
end
def last_hour(date_or_time)
date_or_time.acts_like?(:time) ? date_or_time.end_of_day : date_or_time
end
def days_span(day)
(DAYS_INTO_WEEK.fetch(day) - DAYS_INTO_WEEK.fetch(Date.beginning_of_week)) % 7
end
def copy_time_to(other)
other.change(hour: hour, min: min, sec: sec, nsec: try(:nsec))
end
end
end

View File

@ -1,31 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/module/attribute_accessors"
module DateAndTime
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.
mattr_accessor :preserve_timezone, instance_writer: false, default: false
# Change the output of <tt>ActiveSupport::TimeZone.utc_to_local</tt>.
#
# When `true`, it returns local times with an UTC offset, with `false` local
# times are returned as UTC.
#
# # Given this zone:
# zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
#
# # With `utc_to_local_returns_utc_offset_times = false`, local time is converted to UTC:
# zone.utc_to_local(Time.utc(2000, 1)) # => 1999-12-31 19:00:00 UTC
#
# # With `utc_to_local_returns_utc_offset_times = true`, local time is returned with UTC offset:
# zone.utc_to_local(Time.utc(2000, 1)) # => 1999-12-31 19:00:00 -0500
mattr_accessor :utc_to_local_returns_utc_offset_times, instance_writer: false, default: false
end
end

View File

@ -1,40 +0,0 @@
# frozen_string_literal: true
module DateAndTime
module Zones
# Returns the simultaneous time in <tt>Time.zone</tt> if a zone is given or
# if Time.zone_default is set. Otherwise, it returns the current time.
#
# Time.zone = 'Hawaii' # => 'Hawaii'
# Time.utc(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00
# Date.new(2000).in_time_zone # => Sat, 01 Jan 2000 00:00:00 HST -10:00
#
# This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt> as the local zone
# instead of the operating system's time zone.
#
# You can also pass in a TimeZone instance or string that identifies a TimeZone as an argument,
# and the conversion will be based on that zone instead of <tt>Time.zone</tt>.
#
# Time.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
# Date.new(2000).in_time_zone('Alaska') # => Sat, 01 Jan 2000 00:00:00 AKST -09:00
def in_time_zone(zone = ::Time.zone)
time_zone = ::Time.find_zone! zone
time = acts_like?(:time) ? self : nil
if time_zone
time_with_zone(time, time_zone)
else
time || to_time
end
end
private
def time_with_zone(time, zone)
if time
ActiveSupport::TimeWithZone.new(time.utc? ? time : time.getutc, zone)
else
ActiveSupport::TimeWithZone.new(nil, zone, to_time(:utc))
end
end
end
end

View File

@ -1,7 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/date_time/acts_like"
require "active_support/core_ext/date_time/blank"
require "active_support/core_ext/date_time/calculations"
require "active_support/core_ext/date_time/compatibility"
require "active_support/core_ext/date_time/conversions"

View File

@ -1,16 +0,0 @@
# frozen_string_literal: true
require "date"
require "active_support/core_ext/object/acts_like"
class DateTime
# Duck-types as a Date-like class. See Object#acts_like?.
def acts_like_date?
true
end
# Duck-types as a Time-like class. See Object#acts_like?.
def acts_like_time?
true
end
end

View File

@ -1,14 +0,0 @@
# frozen_string_literal: true
require "date"
class DateTime #:nodoc:
# No DateTime is ever blank:
#
# DateTime.now.blank? # => false
#
# @return [false]
def blank?
false
end
end

View File

@ -1,211 +0,0 @@
# frozen_string_literal: true
require "date"
class DateTime
class << self
# Returns <tt>Time.zone.now.to_datetime</tt> when <tt>Time.zone</tt> or
# <tt>config.time_zone</tt> are set, otherwise returns
# <tt>Time.now.to_datetime</tt>.
def current
::Time.zone ? ::Time.zone.now.to_datetime : ::Time.now.to_datetime
end
end
# Returns the number of seconds since 00:00:00.
#
# DateTime.new(2012, 8, 29, 0, 0, 0).seconds_since_midnight # => 0
# DateTime.new(2012, 8, 29, 12, 34, 56).seconds_since_midnight # => 45296
# DateTime.new(2012, 8, 29, 23, 59, 59).seconds_since_midnight # => 86399
def seconds_since_midnight
sec + (min * 60) + (hour * 3600)
end
# Returns the number of seconds until 23:59:59.
#
# DateTime.new(2012, 8, 29, 0, 0, 0).seconds_until_end_of_day # => 86399
# DateTime.new(2012, 8, 29, 12, 34, 56).seconds_until_end_of_day # => 41103
# DateTime.new(2012, 8, 29, 23, 59, 59).seconds_until_end_of_day # => 0
def seconds_until_end_of_day
end_of_day.to_i - to_i
end
# Returns the fraction of a second as a +Rational+
#
# DateTime.new(2012, 8, 29, 0, 0, 0.5).subsec # => (1/2)
def subsec
sec_fraction
end
# Returns a new DateTime where one or more of the elements have been changed
# according to the +options+ parameter. The time options (<tt>:hour</tt>,
# <tt>:min</tt>, <tt>:sec</tt>) reset cascadingly, so if only the hour is
# passed, then minute and sec is set to 0. If the hour and minute is passed,
# then sec is set to 0. The +options+ parameter takes a hash with any of these
# keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>,
# <tt>:min</tt>, <tt>:sec</tt>, <tt>:offset</tt>, <tt>:start</tt>.
#
# DateTime.new(2012, 8, 29, 22, 35, 0).change(day: 1) # => DateTime.new(2012, 8, 1, 22, 35, 0)
# DateTime.new(2012, 8, 29, 22, 35, 0).change(year: 1981, day: 1) # => DateTime.new(1981, 8, 1, 22, 35, 0)
# DateTime.new(2012, 8, 29, 22, 35, 0).change(year: 1981, hour: 0) # => DateTime.new(1981, 8, 29, 0, 0, 0)
def change(options)
if new_nsec = options[:nsec]
raise ArgumentError, "Can't change both :nsec and :usec at the same time: #{options.inspect}" if options[:usec]
new_fraction = Rational(new_nsec, 1000000000)
else
new_usec = options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000))
new_fraction = Rational(new_usec, 1000000)
end
raise ArgumentError, "argument out of range" if new_fraction >= 1
::DateTime.civil(
options.fetch(:year, year),
options.fetch(:month, month),
options.fetch(:day, day),
options.fetch(:hour, hour),
options.fetch(:min, options[:hour] ? 0 : min),
options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec) + new_fraction,
options.fetch(:offset, offset),
options.fetch(:start, start)
)
end
# Uses Date to provide precise Time calculations for years, months, and days.
# The +options+ parameter takes a hash with any of these keys: <tt>:years</tt>,
# <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>, <tt>:hours</tt>,
# <tt>:minutes</tt>, <tt>:seconds</tt>.
def advance(options)
unless options[:weeks].nil?
options[:weeks], partial_weeks = options[:weeks].divmod(1)
options[:days] = options.fetch(:days, 0) + 7 * partial_weeks
end
unless options[:days].nil?
options[:days], partial_days = options[:days].divmod(1)
options[:hours] = options.fetch(:hours, 0) + 24 * partial_days
end
d = to_date.advance(options)
datetime_advanced_by_date = change(year: d.year, month: d.month, day: d.day)
seconds_to_advance = \
options.fetch(:seconds, 0) +
options.fetch(:minutes, 0) * 60 +
options.fetch(:hours, 0) * 3600
if seconds_to_advance.zero?
datetime_advanced_by_date
else
datetime_advanced_by_date.since(seconds_to_advance)
end
end
# Returns a new DateTime representing the time a number of seconds ago.
# Do not use this method in combination with x.months, use months_ago instead!
def ago(seconds)
since(-seconds)
end
# Returns a new DateTime representing the time a number of seconds since the
# instance time. Do not use this method in combination with x.months, use
# months_since instead!
def since(seconds)
self + Rational(seconds, 86400)
end
alias :in :since
# Returns a new DateTime representing the start of the day (0:00).
def beginning_of_day
change(hour: 0)
end
alias :midnight :beginning_of_day
alias :at_midnight :beginning_of_day
alias :at_beginning_of_day :beginning_of_day
# Returns a new DateTime representing the middle of the day (12:00)
def middle_of_day
change(hour: 12)
end
alias :midday :middle_of_day
alias :noon :middle_of_day
alias :at_midday :middle_of_day
alias :at_noon :middle_of_day
alias :at_middle_of_day :middle_of_day
# Returns a new DateTime representing the end of the day (23:59:59).
def end_of_day
change(hour: 23, min: 59, sec: 59, usec: Rational(999999999, 1000))
end
alias :at_end_of_day :end_of_day
# Returns a new DateTime representing the start of the hour (hh:00:00).
def beginning_of_hour
change(min: 0)
end
alias :at_beginning_of_hour :beginning_of_hour
# Returns a new DateTime representing the end of the hour (hh:59:59).
def end_of_hour
change(min: 59, sec: 59, usec: Rational(999999999, 1000))
end
alias :at_end_of_hour :end_of_hour
# Returns a new DateTime representing the start of the minute (hh:mm:00).
def beginning_of_minute
change(sec: 0)
end
alias :at_beginning_of_minute :beginning_of_minute
# Returns a new DateTime representing the end of the minute (hh:mm:59).
def end_of_minute
change(sec: 59, usec: Rational(999999999, 1000))
end
alias :at_end_of_minute :end_of_minute
# Returns a <tt>Time</tt> instance of the simultaneous time in the system timezone.
def localtime(utc_offset = nil)
utc = new_offset(0)
Time.utc(
utc.year, utc.month, utc.day,
utc.hour, utc.min, utc.sec + utc.sec_fraction
).getlocal(utc_offset)
end
alias_method :getlocal, :localtime
# Returns a <tt>Time</tt> instance of the simultaneous time in the UTC timezone.
#
# DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)) # => Mon, 21 Feb 2005 10:11:12 -0600
# DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)).utc # => Mon, 21 Feb 2005 16:11:12 UTC
def utc
utc = new_offset(0)
Time.utc(
utc.year, utc.month, utc.day,
utc.hour, utc.min, utc.sec + utc.sec_fraction
)
end
alias_method :getgm, :utc
alias_method :getutc, :utc
alias_method :gmtime, :utc
# Returns +true+ if <tt>offset == 0</tt>.
def utc?
offset == 0
end
# Returns the offset value in seconds.
def utc_offset
(offset * 86400).to_i
end
# Layers additional behavior on DateTime#<=> so that Time and
# ActiveSupport::TimeWithZone instances can be compared with a DateTime.
def <=>(other)
if other.respond_to? :to_datetime
super other.to_datetime rescue nil
else
super
end
end
end

View File

@ -1,18 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/date_and_time/compatibility"
require "active_support/core_ext/module/redefine_method"
class DateTime
include DateAndTime::Compatibility
silence_redefinition_of_method :to_time
# Either return an instance of +Time+ with the same UTC offset
# as +self+ or an instance of +Time+ representing the same time
# in the local system timezone depending on the setting of
# on the setting of +ActiveSupport.to_time_preserves_timezone+.
def to_time
preserve_timezone ? getlocal(utc_offset) : getlocal
end
end

View File

@ -1,106 +0,0 @@
# frozen_string_literal: true
require "date"
require "active_support/inflector/methods"
require "active_support/core_ext/time/conversions"
require "active_support/core_ext/date_time/calculations"
require "active_support/values/time_zone"
class DateTime
# Convert to a formatted string. See Time::DATE_FORMATS for predefined formats.
#
# This method is aliased to <tt>to_s</tt>.
#
# === Examples
# datetime = DateTime.civil(2007, 12, 4, 0, 0, 0, 0) # => Tue, 04 Dec 2007 00:00:00 +0000
#
# datetime.to_formatted_s(:db) # => "2007-12-04 00:00:00"
# datetime.to_s(:db) # => "2007-12-04 00:00:00"
# datetime.to_s(:number) # => "20071204000000"
# datetime.to_formatted_s(:short) # => "04 Dec 00:00"
# datetime.to_formatted_s(:long) # => "December 04, 2007 00:00"
# datetime.to_formatted_s(:long_ordinal) # => "December 4th, 2007 00:00"
# datetime.to_formatted_s(:rfc822) # => "Tue, 04 Dec 2007 00:00:00 +0000"
# datetime.to_formatted_s(:iso8601) # => "2007-12-04T00:00:00+00:00"
#
# == Adding your own datetime formats to to_formatted_s
# DateTime formats are shared with Time. You can add your own to the
# Time::DATE_FORMATS hash. Use the format name as the hash key and
# either a strftime string or Proc instance that takes a time or
# datetime argument as the value.
#
# # config/initializers/time_formats.rb
# Time::DATE_FORMATS[:month_and_year] = '%B %Y'
# Time::DATE_FORMATS[:short_ordinal] = lambda { |time| time.strftime("%B #{time.day.ordinalize}") }
def to_formatted_s(format = :default)
if formatter = ::Time::DATE_FORMATS[format]
formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
else
to_default_s
end
end
alias_method :to_default_s, :to_s if instance_methods(false).include?(:to_s)
alias_method :to_s, :to_formatted_s
# Returns a formatted string of the offset from UTC, or an alternative
# string if the time zone is already UTC.
#
# datetime = DateTime.civil(2000, 1, 1, 0, 0, 0, Rational(-6, 24))
# datetime.formatted_offset # => "-06:00"
# datetime.formatted_offset(false) # => "-0600"
def formatted_offset(colon = true, alternate_utc_string = nil)
utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon)
end
# Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005 14:30:00 +0000".
def readable_inspect
to_s(:rfc822)
end
alias_method :default_inspect, :inspect
alias_method :inspect, :readable_inspect
# Returns DateTime with local offset for given year if format is local else
# offset is zero.
#
# DateTime.civil_from_format :local, 2012
# # => Sun, 01 Jan 2012 00:00:00 +0300
# DateTime.civil_from_format :local, 2012, 12, 17
# # => Mon, 17 Dec 2012 00:00:00 +0000
def self.civil_from_format(utc_or_local, year, month = 1, day = 1, hour = 0, min = 0, sec = 0)
if utc_or_local.to_sym == :local
offset = ::Time.local(year, month, day).utc_offset.to_r / 86400
else
offset = 0
end
civil(year, month, day, hour, min, sec, offset)
end
# Converts +self+ to a floating-point number of seconds, including fractional microseconds, since the Unix epoch.
def to_f
seconds_since_unix_epoch.to_f + sec_fraction
end
# Converts +self+ to an integer number of seconds since the Unix epoch.
def to_i
seconds_since_unix_epoch.to_i
end
# Returns the fraction of a second as microseconds
def usec
(sec_fraction * 1_000_000).to_i
end
# Returns the fraction of a second as nanoseconds
def nsec
(sec_fraction * 1_000_000_000).to_i
end
private
def offset_in_seconds
(offset * 86400).to_i
end
def seconds_since_unix_epoch
(jd - 2440588) * 86400 - offset_in_seconds + seconds_since_midnight
end
end

View File

@ -1,3 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/digest/uuid"

View File

@ -1,54 +0,0 @@
# frozen_string_literal: true
require "securerandom"
require "digest"
module Digest
module UUID
DNS_NAMESPACE = "k\xA7\xB8\x10\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" #:nodoc:
URL_NAMESPACE = "k\xA7\xB8\x11\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" #:nodoc:
OID_NAMESPACE = "k\xA7\xB8\x12\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" #:nodoc:
X500_NAMESPACE = "k\xA7\xB8\x14\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" #:nodoc:
# Generates a v5 non-random UUID (Universally Unique IDentifier).
#
# Using Digest::MD5 generates version 3 UUIDs; Digest::SHA1 generates version 5 UUIDs.
# uuid_from_hash always generates the same UUID for a given name and namespace combination.
#
# See RFC 4122 for details of UUID at: https://www.ietf.org/rfc/rfc4122.txt
def self.uuid_from_hash(hash_class, uuid_namespace, name)
if hash_class == Digest::MD5
version = 3
elsif hash_class == Digest::SHA1
version = 5
else
raise ArgumentError, "Expected Digest::SHA1 or Digest::MD5, got #{hash_class.name}."
end
hash = hash_class.new
hash.update(uuid_namespace)
hash.update(name)
ary = hash.digest.unpack("NnnnnN")
ary[2] = (ary[2] & 0x0FFF) | (version << 12)
ary[3] = (ary[3] & 0x3FFF) | 0x8000
"%08x-%04x-%04x-%04x-%04x%08x" % ary
end
# Convenience method for uuid_from_hash using Digest::MD5.
def self.uuid_v3(uuid_namespace, name)
uuid_from_hash(Digest::MD5, uuid_namespace, name)
end
# Convenience method for uuid_from_hash using Digest::SHA1.
def self.uuid_v5(uuid_namespace, name)
uuid_from_hash(Digest::SHA1, uuid_namespace, name)
end
# Convenience method for SecureRandom.uuid.
def self.uuid_v4
SecureRandom.uuid
end
end
end

View File

@ -1,3 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/file/atomic"

View File

@ -1,10 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/hash/conversions"
require "active_support/core_ext/hash/deep_merge"
require "active_support/core_ext/hash/deep_transform_values"
require "active_support/core_ext/hash/except"
require "active_support/core_ext/hash/indifferent_access"
require "active_support/core_ext/hash/keys"
require "active_support/core_ext/hash/reverse_merge"
require "active_support/core_ext/hash/slice"

View File

@ -1,263 +0,0 @@
# frozen_string_literal: true
require "active_support/xml_mini"
require "active_support/core_ext/object/blank"
require "active_support/core_ext/object/to_param"
require "active_support/core_ext/object/to_query"
require "active_support/core_ext/object/try"
require "active_support/core_ext/array/wrap"
require "active_support/core_ext/hash/reverse_merge"
require "active_support/core_ext/string/inflections"
class Hash
# Returns a string containing an XML representation of its receiver:
#
# { foo: 1, bar: 2 }.to_xml
# # =>
# # <?xml version="1.0" encoding="UTF-8"?>
# # <hash>
# # <foo type="integer">1</foo>
# # <bar type="integer">2</bar>
# # </hash>
#
# To do so, the method loops over the pairs and builds nodes that depend on
# the _values_. Given a pair +key+, +value+:
#
# * If +value+ is a hash there's a recursive call with +key+ as <tt>:root</tt>.
#
# * If +value+ is an array there's a recursive call with +key+ as <tt>:root</tt>,
# and +key+ singularized as <tt>:children</tt>.
#
# * If +value+ is a callable object it must expect one or two arguments. Depending
# on the arity, the callable is invoked with the +options+ hash as first argument
# with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. The
# callable can add nodes by using <tt>options[:builder]</tt>.
#
# {foo: lambda { |options, key| options[:builder].b(key) }}.to_xml
# # => "<b>foo</b>"
#
# * If +value+ responds to +to_xml+ the method is invoked with +key+ as <tt>:root</tt>.
#
# class Foo
# def to_xml(options)
# options[:builder].bar 'fooing!'
# end
# end
#
# { foo: Foo.new }.to_xml(skip_instruct: true)
# # =>
# # <hash>
# # <bar>fooing!</bar>
# # </hash>
#
# * Otherwise, a node with +key+ as tag is created with a string representation of
# +value+ as text node. If +value+ is +nil+ an attribute "nil" set to "true" is added.
# Unless the option <tt>:skip_types</tt> exists and is true, an attribute "type" is
# added as well according to the following mapping:
#
# XML_TYPE_NAMES = {
# "Symbol" => "symbol",
# "Integer" => "integer",
# "BigDecimal" => "decimal",
# "Float" => "float",
# "TrueClass" => "boolean",
# "FalseClass" => "boolean",
# "Date" => "date",
# "DateTime" => "dateTime",
# "Time" => "dateTime"
# }
#
# By default the root node is "hash", but that's configurable via the <tt>:root</tt> option.
#
# The default XML builder is a fresh instance of <tt>Builder::XmlMarkup</tt>. You can
# configure your own builder with the <tt>:builder</tt> option. The method also accepts
# options like <tt>:dasherize</tt> and friends, they are forwarded to the builder.
def to_xml(options = {})
require "active_support/builder" unless defined?(Builder::XmlMarkup)
options = options.dup
options[:indent] ||= 2
options[:root] ||= "hash"
options[:builder] ||= Builder::XmlMarkup.new(indent: options[:indent])
builder = options[:builder]
builder.instruct! unless options.delete(:skip_instruct)
root = ActiveSupport::XmlMini.rename_key(options[:root].to_s, options)
builder.tag!(root) do
each { |key, value| ActiveSupport::XmlMini.to_tag(key, value, options) }
yield builder if block_given?
end
end
class << self
# Returns a Hash containing a collection of pairs when the key is the node name and the value is
# its content
#
# xml = <<-XML
# <?xml version="1.0" encoding="UTF-8"?>
# <hash>
# <foo type="integer">1</foo>
# <bar type="integer">2</bar>
# </hash>
# XML
#
# hash = Hash.from_xml(xml)
# # => {"hash"=>{"foo"=>1, "bar"=>2}}
#
# +DisallowedType+ is raised if the XML contains attributes with <tt>type="yaml"</tt> or
# <tt>type="symbol"</tt>. Use <tt>Hash.from_trusted_xml</tt> to
# parse this XML.
#
# Custom +disallowed_types+ can also be passed in the form of an
# array.
#
# xml = <<-XML
# <?xml version="1.0" encoding="UTF-8"?>
# <hash>
# <foo type="integer">1</foo>
# <bar type="string">"David"</bar>
# </hash>
# XML
#
# hash = Hash.from_xml(xml, ['integer'])
# # => ActiveSupport::XMLConverter::DisallowedType: Disallowed type attribute: "integer"
#
# Note that passing custom disallowed types will override the default types,
# which are Symbol and YAML.
def from_xml(xml, disallowed_types = nil)
ActiveSupport::XMLConverter.new(xml, disallowed_types).to_h
end
# Builds a Hash from XML just like <tt>Hash.from_xml</tt>, but also allows Symbol and YAML.
def from_trusted_xml(xml)
from_xml xml, []
end
end
end
module ActiveSupport
class XMLConverter # :nodoc:
# Raised if the XML contains attributes with type="yaml" or
# type="symbol". Read Hash#from_xml for more details.
class DisallowedType < StandardError
def initialize(type)
super "Disallowed type attribute: #{type.inspect}"
end
end
DISALLOWED_TYPES = %w(symbol yaml)
def initialize(xml, disallowed_types = nil)
@xml = normalize_keys(XmlMini.parse(xml))
@disallowed_types = disallowed_types || DISALLOWED_TYPES
end
def to_h
deep_to_h(@xml)
end
private
def normalize_keys(params)
case params
when Hash
Hash[params.map { |k, v| [k.to_s.tr("-", "_"), normalize_keys(v)] } ]
when Array
params.map { |v| normalize_keys(v) }
else
params
end
end
def deep_to_h(value)
case value
when Hash
process_hash(value)
when Array
process_array(value)
when String
value
else
raise "can't typecast #{value.class.name} - #{value.inspect}"
end
end
def process_hash(value)
if value.include?("type") && !value["type"].is_a?(Hash) && @disallowed_types.include?(value["type"])
raise DisallowedType, value["type"]
end
if become_array?(value)
_, entries = Array.wrap(value.detect { |k, v| not v.is_a?(String) })
if entries.nil? || value["__content__"].try(:empty?)
[]
else
case entries
when Array
entries.collect { |v| deep_to_h(v) }
when Hash
[deep_to_h(entries)]
else
raise "can't typecast #{entries.inspect}"
end
end
elsif become_content?(value)
process_content(value)
elsif become_empty_string?(value)
""
elsif become_hash?(value)
xml_value = value.transform_values { |v| deep_to_h(v) }
# Turn { files: { file: #<StringIO> } } into { files: #<StringIO> } so it is compatible with
# how multipart uploaded files from HTML appear
xml_value["file"].is_a?(StringIO) ? xml_value["file"] : xml_value
end
end
def become_content?(value)
value["type"] == "file" || (value["__content__"] && (value.keys.size == 1 || value["__content__"].present?))
end
def become_array?(value)
value["type"] == "array"
end
def become_empty_string?(value)
# { "string" => true }
# No tests fail when the second term is removed.
value["type"] == "string" && value["nil"] != "true"
end
def become_hash?(value)
!nothing?(value) && !garbage?(value)
end
def nothing?(value)
# blank or nil parsed values are represented by nil
value.blank? || value["nil"] == "true"
end
def garbage?(value)
# If the type is the only element which makes it then
# this still makes the value nil, except if type is
# an XML node(where type['value'] is a Hash)
value["type"] && !value["type"].is_a?(::Hash) && value.size == 1
end
def process_content(value)
content = value["__content__"]
if parser = ActiveSupport::XmlMini::PARSING[value["type"]]
parser.arity == 1 ? parser.call(content) : parser.call(content, value)
else
content
end
end
def process_array(value)
value.map! { |i| deep_to_h(i) }
value.length > 1 ? value : value.first
end
end
end

View File

@ -1,24 +0,0 @@
# frozen_string_literal: true
require "active_support/hash_with_indifferent_access"
class Hash
# Returns an <tt>ActiveSupport::HashWithIndifferentAccess</tt> out of its receiver:
#
# { a: 1 }.with_indifferent_access['a'] # => 1
def with_indifferent_access
ActiveSupport::HashWithIndifferentAccess.new(self)
end
# Called when object is nested under an object that receives
# #with_indifferent_access. This method will be called on the current object
# by the enclosing object and is aliased to #with_indifferent_access by
# default. Subclasses of Hash may overwrite this method to return +self+ if
# converting to an <tt>ActiveSupport::HashWithIndifferentAccess</tt> would not be
# desirable.
#
# b = { b: 1 }
# { a: b }.with_indifferent_access['a'] # calls b.nested_under_indifferent_access
# # => {"b"=>1}
alias nested_under_indifferent_access with_indifferent_access
end

View File

@ -1,25 +0,0 @@
# frozen_string_literal: true
class Hash
# Merges the caller into +other_hash+. For example,
#
# options = options.reverse_merge(size: 25, velocity: 10)
#
# is equivalent to
#
# options = { size: 25, velocity: 10 }.merge(options)
#
# This is particularly useful for initializing an options hash
# with default values.
def reverse_merge(other_hash)
other_hash.merge(self)
end
alias_method :with_defaults, :reverse_merge
# Destructive +reverse_merge+.
def reverse_merge!(other_hash)
replace(reverse_merge(other_hash))
end
alias_method :reverse_update, :reverse_merge!
alias_method :with_defaults!, :reverse_merge!
end

View File

@ -1,5 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/integer/multiple"
require "active_support/core_ext/integer/inflections"
require "active_support/core_ext/integer/time"

View File

@ -1,31 +0,0 @@
# frozen_string_literal: true
require "active_support/inflector"
class Integer
# Ordinalize turns a number into an ordinal string used to denote the
# position in an ordered sequence such as 1st, 2nd, 3rd, 4th.
#
# 1.ordinalize # => "1st"
# 2.ordinalize # => "2nd"
# 1002.ordinalize # => "1002nd"
# 1003.ordinalize # => "1003rd"
# -11.ordinalize # => "-11th"
# -1001.ordinalize # => "-1001st"
def ordinalize
ActiveSupport::Inflector.ordinalize(self)
end
# Ordinal returns the suffix used to denote the position
# in an ordered sequence such as 1st, 2nd, 3rd, 4th.
#
# 1.ordinal # => "st"
# 2.ordinal # => "nd"
# 1002.ordinal # => "nd"
# 1003.ordinal # => "rd"
# -11.ordinal # => "th"
# -1001.ordinal # => "st"
def ordinal
ActiveSupport::Inflector.ordinal(self)
end
end

View File

@ -1,12 +0,0 @@
# frozen_string_literal: true
class Integer
# Check whether the integer is evenly divisible by the argument.
#
# 0.multiple_of?(0) # => true
# 6.multiple_of?(5) # => false
# 10.multiple_of?(2) # => true
def multiple_of?(number)
number == 0 ? self == 0 : self % number == 0
end
end

View File

@ -1,22 +0,0 @@
# frozen_string_literal: true
require "active_support/duration"
require "active_support/core_ext/numeric/time"
class Integer
# Returns a Duration instance matching the number of months provided.
#
# 2.months # => 2 months
def months
ActiveSupport::Duration.months(self)
end
alias :month :months
# Returns a Duration instance matching the number of years provided.
#
# 2.years # => 2 years
def years
ActiveSupport::Duration.years(self)
end
alias :year :years
end

View File

@ -1,5 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/kernel/concern"
require "active_support/core_ext/kernel/reporting"
require "active_support/core_ext/kernel/singleton_class"

View File

@ -1,14 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/module/concerning"
module Kernel
module_function
# A shortcut to define a toplevel concern, not within a module.
#
# See Module::Concerning for more.
def concern(topic, &module_definition)
Object.concern topic, &module_definition
end
end

View File

@ -1,8 +0,0 @@
# frozen_string_literal: true
module Kernel
# class_eval on an object acts like singleton_class.class_eval.
def class_eval(*args, &block)
singleton_class.class_eval(*args, &block)
end
end

View File

@ -1,9 +0,0 @@
# frozen_string_literal: true
class LoadError
# Returns true if the given path name (except perhaps for the ".rb"
# extension) is the missing file which caused the exception to be raised.
def is_missing?(location)
location.delete_suffix(".rb") == path.to_s.delete_suffix(".rb")
end
end

View File

@ -1,26 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/string/inflections"
module ActiveSupport
module MarshalWithAutoloading # :nodoc:
def load(source, proc = nil)
super(source, proc)
rescue ArgumentError, NameError => exc
if exc.message.match(%r|undefined class/module (.+?)(?:::)?\z|)
# try loading the class/module
loaded = $1.constantize
raise unless $1 == loaded.name
# if it is an IO we need to go back to read the object
source.rewind if source.respond_to?(:rewind)
retry
else
raise exc
end
end
end
end
Marshal.singleton_class.prepend(ActiveSupport::MarshalWithAutoloading)

View File

@ -1,13 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/module/aliasing"
require "active_support/core_ext/module/introspection"
require "active_support/core_ext/module/anonymous"
require "active_support/core_ext/module/attribute_accessors"
require "active_support/core_ext/module/attribute_accessors_per_thread"
require "active_support/core_ext/module/attr_internal"
require "active_support/core_ext/module/concerning"
require "active_support/core_ext/module/delegation"
require "active_support/core_ext/module/deprecation"
require "active_support/core_ext/module/redefine_method"
require "active_support/core_ext/module/remove_method"

View File

@ -1,65 +0,0 @@
# frozen_string_literal: true
class NameError
# Extract the name of the missing constant from the exception message.
#
# begin
# HelloWorld
# rescue NameError => e
# e.missing_name
# end
# # => "HelloWorld"
def missing_name
# Since ruby v2.3.0 `did_you_mean` gem is loaded by default.
# It extends NameError#message with spell corrections which are SLOW.
# We should use original_message message instead.
message = respond_to?(:original_message) ? original_message : self.message
return unless message.start_with?("uninitialized constant ")
receiver = begin
self.receiver
rescue ArgumentError
nil
end
if receiver == Object
name.to_s
elsif receiver
"#{real_mod_name(receiver)}::#{self.name}"
else
if match = message.match(/((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/)
match[1]
end
end
end
# Was this exception raised because the given name was missing?
#
# begin
# HelloWorld
# rescue NameError => e
# e.missing_name?("HelloWorld")
# end
# # => true
def missing_name?(name)
if name.is_a? Symbol
self.name == name
else
missing_name == name.to_s
end
end
private
UNBOUND_METHOD_MODULE_NAME = Module.instance_method(:name)
private_constant :UNBOUND_METHOD_MODULE_NAME
if UnboundMethod.method_defined?(:bind_call)
def real_mod_name(mod)
UNBOUND_METHOD_MODULE_NAME.bind_call(mod)
end
else
def real_mod_name(mod)
UNBOUND_METHOD_MODULE_NAME.bind(mod).call
end
end
end

View File

@ -1,5 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/numeric/bytes"
require "active_support/core_ext/numeric/time"
require "active_support/core_ext/numeric/conversions"

View File

@ -1,16 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/object/acts_like"
require "active_support/core_ext/object/blank"
require "active_support/core_ext/object/duplicable"
require "active_support/core_ext/object/deep_dup"
require "active_support/core_ext/object/try"
require "active_support/core_ext/object/inclusion"
require "active_support/core_ext/object/conversions"
require "active_support/core_ext/object/instance_variables"
require "active_support/core_ext/object/json"
require "active_support/core_ext/object/to_param"
require "active_support/core_ext/object/to_query"
require "active_support/core_ext/object/with_options"

View File

@ -1,21 +0,0 @@
# frozen_string_literal: true
class Object
# A duck-type assistant method. For example, Active Support extends Date
# to define an <tt>acts_like_date?</tt> method, and extends Time to define
# <tt>acts_like_time?</tt>. As a result, we can do <tt>x.acts_like?(:time)</tt> and
# <tt>x.acts_like?(:date)</tt> to do duck-type-safe comparisons, since classes that
# we want to act like Time simply need to define an <tt>acts_like_time?</tt> method.
def acts_like?(duck)
case duck
when :time
respond_to? :acts_like_time?
when :date
respond_to? :acts_like_date?
when :string
respond_to? :acts_like_string?
else
respond_to? :"acts_like_#{duck}?"
end
end
end

View File

@ -1,6 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/object/to_param"
require "active_support/core_ext/object/to_query"
require "active_support/core_ext/array/conversions"
require "active_support/core_ext/hash/conversions"

View File

@ -1,29 +0,0 @@
# frozen_string_literal: true
class Object
# Returns true if this object is included in the argument. Argument must be
# any object which responds to +#include?+. Usage:
#
# characters = ["Konata", "Kagami", "Tsukasa"]
# "Konata".in?(characters) # => true
#
# This will throw an +ArgumentError+ if the argument doesn't respond
# to +#include?+.
def in?(another_object)
another_object.include?(self)
rescue NoMethodError
raise ArgumentError.new("The parameter passed to #in? must respond to #include?")
end
# Returns the receiver if it's included in the argument otherwise returns +nil+.
# Argument must be any object which responds to +#include?+. Usage:
#
# params[:bucket_type].presence_in %w( project calendar )
#
# This will throw an +ArgumentError+ if the argument doesn't respond to +#include?+.
#
# @return [Object]
def presence_in(another_object)
in?(another_object) ? self : nil
end
end

View File

@ -1,30 +0,0 @@
# frozen_string_literal: true
class Object
# Returns a hash with string keys that maps instance variable names without "@" to their
# corresponding values.
#
# class C
# def initialize(x, y)
# @x, @y = x, y
# end
# end
#
# C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}
def instance_values
Hash[instance_variables.map { |name| [name[1..-1], instance_variable_get(name)] }]
end
# Returns an array of instance variable names as strings including "@".
#
# class C
# def initialize(x, y)
# @x, @y = x, y
# end
# end
#
# C.new(0, 1).instance_variable_names # => ["@y", "@x"]
def instance_variable_names
instance_variables.map(&:to_s)
end
end

View File

@ -1,239 +0,0 @@
# frozen_string_literal: true
# Hack to load json gem first so we can overwrite its to_json.
require "json"
require "bigdecimal"
require "ipaddr"
require "uri/generic"
require "pathname"
require "active_support/core_ext/big_decimal/conversions" # for #to_s
require "active_support/core_ext/hash/except"
require "active_support/core_ext/hash/slice"
require "active_support/core_ext/object/instance_variables"
require "time"
require "active_support/core_ext/time/conversions"
require "active_support/core_ext/date_time/conversions"
require "active_support/core_ext/date/conversions"
#--
# The JSON gem adds a few modules to Ruby core classes containing :to_json definition, overwriting
# their default behavior. That said, we need to define the basic to_json method in all of them,
# otherwise they will always use to_json gem implementation, which is backwards incompatible in
# several cases (for instance, the JSON implementation for Hash does not work) with inheritance
# and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json.
#
# On the other hand, we should avoid conflict with ::JSON.{generate,dump}(obj). Unfortunately, the
# JSON gem's encoder relies on its own to_json implementation to encode objects. Since it always
# passes a ::JSON::State object as the only argument to to_json, we can detect that and forward the
# calls to the original to_json method.
#
# It should be noted that when using ::JSON.{generate,dump} directly, ActiveSupport's encoder is
# bypassed completely. This means that as_json won't be invoked and the JSON gem will simply
# ignore any options it does not natively understand. This also means that ::JSON.{generate,dump}
# should give exactly the same results with or without active support.
module ActiveSupport
module ToJsonWithActiveSupportEncoder # :nodoc:
def to_json(options = nil)
if options.is_a?(::JSON::State)
# Called from JSON.{generate,dump}, forward it to JSON gem's to_json
super(options)
else
# to_json is being invoked directly, use ActiveSupport's encoder
ActiveSupport::JSON.encode(self, options)
end
end
end
end
[Enumerable, Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].reverse_each do |klass|
klass.prepend(ActiveSupport::ToJsonWithActiveSupportEncoder)
end
class Object
def as_json(options = nil) #:nodoc:
if respond_to?(:to_hash)
to_hash.as_json(options)
else
instance_values.as_json(options)
end
end
end
class Struct #:nodoc:
def as_json(options = nil)
Hash[members.zip(values)].as_json(options)
end
end
class TrueClass
def as_json(options = nil) #:nodoc:
self
end
end
class FalseClass
def as_json(options = nil) #:nodoc:
self
end
end
class NilClass
def as_json(options = nil) #:nodoc:
self
end
end
class String
def as_json(options = nil) #:nodoc:
self
end
end
class Symbol
def as_json(options = nil) #:nodoc:
to_s
end
end
class Numeric
def as_json(options = nil) #:nodoc:
self
end
end
class Float
# Encoding Infinity or NaN to JSON should return "null". The default returns
# "Infinity" or "NaN" which are not valid JSON.
def as_json(options = nil) #:nodoc:
finite? ? self : nil
end
end
class BigDecimal
# A BigDecimal would be naturally represented as a JSON number. Most libraries,
# however, parse non-integer JSON numbers directly as floats. Clients using
# those libraries would get in general a wrong number and no way to recover
# other than manually inspecting the string with the JSON code itself.
#
# That's why a JSON string is returned. The JSON literal is not numeric, but
# if the other end knows by contract that the data is supposed to be a
# BigDecimal, it still has the chance to post-process the string and get the
# real value.
def as_json(options = nil) #:nodoc:
finite? ? to_s : nil
end
end
class Regexp
def as_json(options = nil) #:nodoc:
to_s
end
end
module Enumerable
def as_json(options = nil) #:nodoc:
to_a.as_json(options)
end
end
class IO
def as_json(options = nil) #:nodoc:
to_s
end
end
class Range
def as_json(options = nil) #:nodoc:
to_s
end
end
class Array
def as_json(options = nil) #:nodoc:
map { |v| options ? v.as_json(options.dup) : v.as_json }
end
end
class Hash
def as_json(options = nil) #:nodoc:
# create a subset of the hash by applying :only or :except
subset = if options
if attrs = options[:only]
slice(*Array(attrs))
elsif attrs = options[:except]
except(*Array(attrs))
else
self
end
else
self
end
result = {}
subset.each do |k, v|
result[k.to_s] = options ? v.as_json(options.dup) : v.as_json
end
result
end
end
class Time
def as_json(options = nil) #:nodoc:
if ActiveSupport::JSON::Encoding.use_standard_json_time_format
xmlschema(ActiveSupport::JSON::Encoding.time_precision)
else
%(#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
end
end
end
class Date
def as_json(options = nil) #:nodoc:
if ActiveSupport::JSON::Encoding.use_standard_json_time_format
strftime("%Y-%m-%d")
else
strftime("%Y/%m/%d")
end
end
end
class DateTime
def as_json(options = nil) #:nodoc:
if ActiveSupport::JSON::Encoding.use_standard_json_time_format
xmlschema(ActiveSupport::JSON::Encoding.time_precision)
else
strftime("%Y/%m/%d %H:%M:%S %z")
end
end
end
class URI::Generic #:nodoc:
def as_json(options = nil)
to_s
end
end
class Pathname #:nodoc:
def as_json(options = nil)
to_s
end
end
class IPAddr # :nodoc:
def as_json(options = nil)
to_s
end
end
class Process::Status #:nodoc:
def as_json(options = nil)
{ exitstatus: exitstatus, pid: pid }
end
end
class Exception
def as_json(options = nil)
to_s
end
end

View File

@ -1,3 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/object/to_query"

View File

@ -1,89 +0,0 @@
# frozen_string_literal: true
require "cgi"
class Object
# Alias of <tt>to_s</tt>.
def to_param
to_s
end
# Converts an object into a string suitable for use as a URL query string,
# using the given <tt>key</tt> as the param name.
def to_query(key)
"#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
end
end
class NilClass
# Returns +self+.
def to_param
self
end
end
class TrueClass
# Returns +self+.
def to_param
self
end
end
class FalseClass
# Returns +self+.
def to_param
self
end
end
class Array
# Calls <tt>to_param</tt> on all its elements and joins the result with
# slashes. This is used by <tt>url_for</tt> in Action Pack.
def to_param
collect(&:to_param).join "/"
end
# Converts an array into a string suitable for use as a URL query string,
# using the given +key+ as the param name.
#
# ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding"
def to_query(key)
prefix = "#{key}[]"
if empty?
nil.to_query(prefix)
else
collect { |value| value.to_query(prefix) }.join "&"
end
end
end
class Hash
# Returns a string representation of the receiver suitable for use as a URL
# query string:
#
# {name: 'David', nationality: 'Danish'}.to_query
# # => "name=David&nationality=Danish"
#
# An optional namespace can be passed to enclose key names:
#
# {name: 'David', nationality: 'Danish'}.to_query('user')
# # => "user%5Bname%5D=David&user%5Bnationality%5D=Danish"
#
# The string pairs "key=value" that conform the query string
# are sorted lexicographically in ascending order.
#
# This method is also aliased as +to_param+.
def to_query(namespace = nil)
query = collect do |key, value|
unless (value.is_a?(Hash) || value.is_a?(Array)) && value.empty?
value.to_query(namespace ? "#{namespace}[#{key}]" : key)
end
end.compact
query.sort! unless namespace.to_s.include?("[]")
query.join("&")
end
alias_method :to_param, :to_query
end

View File

@ -1,82 +0,0 @@
# frozen_string_literal: true
require "active_support/option_merger"
class Object
# An elegant way to factor duplication out of options passed to a series of
# method calls. Each method called in the block, with the block variable as
# the receiver, will have its options merged with the default +options+ hash
# provided. Each method called on the block variable must take an options
# hash as its final argument.
#
# Without <tt>with_options</tt>, this code contains duplication:
#
# class Account < ActiveRecord::Base
# has_many :customers, dependent: :destroy
# has_many :products, dependent: :destroy
# has_many :invoices, dependent: :destroy
# has_many :expenses, dependent: :destroy
# end
#
# Using <tt>with_options</tt>, we can remove the duplication:
#
# class Account < ActiveRecord::Base
# with_options dependent: :destroy do |assoc|
# assoc.has_many :customers
# assoc.has_many :products
# assoc.has_many :invoices
# assoc.has_many :expenses
# end
# end
#
# It can also be used with an explicit receiver:
#
# I18n.with_options locale: user.locale, scope: 'newsletter' do |i18n|
# subject i18n.t :subject
# body i18n.t :body, user_name: user.name
# end
#
# When you don't pass an explicit receiver, it executes the whole block
# in merging options context:
#
# class Account < ActiveRecord::Base
# with_options dependent: :destroy do
# has_many :customers
# has_many :products
# has_many :invoices
# has_many :expenses
# end
# end
#
# <tt>with_options</tt> can also be nested since the call is forwarded to its receiver.
#
# NOTE: Each nesting level will merge inherited defaults in addition to their own.
#
# class Post < ActiveRecord::Base
# with_options if: :persisted?, length: { minimum: 50 } do
# validates :content, if: -> { content.present? }
# end
# end
#
# The code is equivalent to:
#
# validates :content, length: { minimum: 50 }, if: -> { content.present? }
#
# Hence the inherited default for +if+ key is ignored.
#
# NOTE: You cannot call class methods implicitly inside of with_options.
# You can access these methods using the class name instead:
#
# class Phone < ActiveRecord::Base
# enum phone_number_type: { home: 0, office: 1, mobile: 2 }
#
# with_options presence: true do
# validates :phone_number_type, inclusion: { in: Phone.phone_number_types.keys }
# end
# end
#
def with_options(options, &block)
option_merger = ActiveSupport::OptionMerger.new(self, options)
block.arity.zero? ? option_merger.instance_eval(&block) : block.call(option_merger)
end
end

View File

@ -1,7 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/range/conversions"
require "active_support/core_ext/range/compare_range"
require "active_support/core_ext/range/include_time_with_zone"
require "active_support/core_ext/range/overlaps"
require "active_support/core_ext/range/each"

View File

@ -1,82 +0,0 @@
# frozen_string_literal: true
module ActiveSupport
module CompareWithRange
# Extends the default Range#=== to support range comparisons.
# (1..5) === (1..5) # => true
# (1..5) === (2..3) # => true
# (1..5) === (1...6) # => true
# (1..5) === (2..6) # => false
#
# The native Range#=== behavior is untouched.
# ('a'..'f') === ('c') # => true
# (5..9) === (11) # => false
#
# The given range must be fully bounded, with both start and end.
def ===(value)
if value.is_a?(::Range)
is_backwards_op = value.exclude_end? ? :>= : :>
return false if value.begin && value.end && value.begin.public_send(is_backwards_op, value.end)
# 1...10 includes 1..9 but it does not include 1..10.
# 1..10 includes 1...11 but it does not include 1...12.
operator = exclude_end? && !value.exclude_end? ? :< : :<=
value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
super(value.first) && (self.end.nil? || value_max.public_send(operator, last))
else
super
end
end
# Extends the default Range#include? to support range comparisons.
# (1..5).include?(1..5) # => true
# (1..5).include?(2..3) # => true
# (1..5).include?(1...6) # => true
# (1..5).include?(2..6) # => false
#
# The native Range#include? behavior is untouched.
# ('a'..'f').include?('c') # => true
# (5..9).include?(11) # => false
#
# The given range must be fully bounded, with both start and end.
def include?(value)
if value.is_a?(::Range)
is_backwards_op = value.exclude_end? ? :>= : :>
return false if value.begin && value.end && value.begin.public_send(is_backwards_op, value.end)
# 1...10 includes 1..9 but it does not include 1..10.
# 1..10 includes 1...11 but it does not include 1...12.
operator = exclude_end? && !value.exclude_end? ? :< : :<=
value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
super(value.first) && (self.end.nil? || value_max.public_send(operator, last))
else
super
end
end
# Extends the default Range#cover? to support range comparisons.
# (1..5).cover?(1..5) # => true
# (1..5).cover?(2..3) # => true
# (1..5).cover?(1...6) # => true
# (1..5).cover?(2..6) # => false
#
# The native Range#cover? behavior is untouched.
# ('a'..'f').cover?('c') # => true
# (5..9).cover?(11) # => false
#
# The given range must be fully bounded, with both start and end.
def cover?(value)
if value.is_a?(::Range)
is_backwards_op = value.exclude_end? ? :>= : :>
return false if value.begin && value.end && value.begin.public_send(is_backwards_op, value.end)
# 1...10 covers 1..9 but it does not cover 1..10.
# 1..10 covers 1...11 but it does not cover 1...12.
operator = exclude_end? && !value.exclude_end? ? :< : :<=
value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
super(value.first) && (self.end.nil? || value_max.public_send(operator, last))
else
super
end
end
end
end
Range.prepend(ActiveSupport::CompareWithRange)

View File

@ -1,41 +0,0 @@
# frozen_string_literal: true
module ActiveSupport
module RangeWithFormat
RANGE_FORMATS = {
db: -> (start, stop) do
case start
when String then "BETWEEN '#{start}' AND '#{stop}'"
else
"BETWEEN '#{start.to_s(:db)}' AND '#{stop.to_s(:db)}'"
end
end
}
# Convert range to a formatted string. See RANGE_FORMATS for predefined formats.
#
# range = (1..100) # => 1..100
#
# range.to_s # => "1..100"
# range.to_s(:db) # => "BETWEEN '1' AND '100'"
#
# == Adding your own range formats to to_s
# You can add your own formats to the Range::RANGE_FORMATS hash.
# Use the format name as the hash key and a Proc instance.
#
# # config/initializers/range_formats.rb
# Range::RANGE_FORMATS[:short] = ->(start, stop) { "Between #{start.to_s(:db)} and #{stop.to_s(:db)}" }
def to_s(format = :default)
if formatter = RANGE_FORMATS[format]
formatter.call(first, last)
else
super()
end
end
alias_method :to_default_s, :to_s
alias_method :to_formatted_s, :to_s
end
end
Range.prepend(ActiveSupport::RangeWithFormat)

View File

@ -1,24 +0,0 @@
# frozen_string_literal: true
require "active_support/time_with_zone"
module ActiveSupport
module EachTimeWithZone #:nodoc:
def each(&block)
ensure_iteration_allowed
super
end
def step(n = 1, &block)
ensure_iteration_allowed
super
end
private
def ensure_iteration_allowed
raise TypeError, "can't iterate from #{first.class}" if first.is_a?(TimeWithZone)
end
end
end
Range.prepend(ActiveSupport::EachTimeWithZone)

View File

@ -1,28 +0,0 @@
# frozen_string_literal: true
require "active_support/time_with_zone"
require "active_support/deprecation"
module ActiveSupport
module IncludeTimeWithZone #:nodoc:
# Extends the default Range#include? to support ActiveSupport::TimeWithZone.
#
# (1.hour.ago..1.hour.from_now).include?(Time.current) # => true
#
def include?(value)
if self.begin.is_a?(TimeWithZone) || self.end.is_a?(TimeWithZone)
ActiveSupport::Deprecation.warn(<<-MSG.squish)
Using `Range#include?` to check the inclusion of a value in
a date time range is deprecated.
It is recommended to use `Range#cover?` instead of `Range#include?` to
check the inclusion of a value in a date time range.
MSG
cover?(value)
else
super
end
end
end
end
Range.prepend(ActiveSupport::IncludeTimeWithZone)

View File

@ -1,10 +0,0 @@
# frozen_string_literal: true
class Range
# Compare two ranges and see if they overlap each other
# (1..5).overlaps?(4..6) # => true
# (1..5).overlaps?(7..9) # => false
def overlaps?(other)
cover?(other.first) || other.cover?(first)
end
end

View File

@ -1,14 +0,0 @@
# frozen_string_literal: true
class Regexp
# Returns +true+ if the regexp has the multiline flag set.
#
# (/./).multiline? # => false
# (/./m).multiline? # => true
#
# Regexp.new(".").multiline? # => false
# Regexp.new(".", Regexp::MULTILINE).multiline? # => true
def multiline?
options & MULTILINE == MULTILINE
end
end

View File

@ -1,45 +0,0 @@
# frozen_string_literal: true
require "securerandom"
module SecureRandom
BASE58_ALPHABET = ("0".."9").to_a + ("A".."Z").to_a + ("a".."z").to_a - ["0", "O", "I", "l"]
BASE36_ALPHABET = ("0".."9").to_a + ("a".."z").to_a
# SecureRandom.base58 generates a random base58 string.
#
# The argument _n_ specifies the length of the random string to be generated.
#
# If _n_ is not specified or is +nil+, 16 is assumed. It may be larger in the future.
#
# The result may contain alphanumeric characters except 0, O, I and l.
#
# p SecureRandom.base58 # => "4kUgL2pdQMSCQtjE"
# p SecureRandom.base58(24) # => "77TMHrHJFvFDwodq8w7Ev2m7"
def self.base58(n = 16)
SecureRandom.random_bytes(n).unpack("C*").map do |byte|
idx = byte % 64
idx = SecureRandom.random_number(58) if idx >= 58
BASE58_ALPHABET[idx]
end.join
end
# SecureRandom.base36 generates a random base36 string in lowercase.
#
# The argument _n_ specifies the length of the random string to be generated.
#
# If _n_ is not specified or is +nil+, 16 is assumed. It may be larger in the future.
# This method can be used over +base58+ if a deterministic case key is necessary.
#
# The result will contain alphanumeric characters in lowercase.
#
# p SecureRandom.base36 # => "4kugl2pdqmscqtje"
# p SecureRandom.base36(24) # => "77tmhrhjfvfdwodq8w7ev2m7"
def self.base36(n = 16)
SecureRandom.random_bytes(n).unpack("C*").map do |byte|
idx = byte % 64
idx = SecureRandom.random_number(36) if idx >= 36
BASE36_ALPHABET[idx]
end.join
end
end

View File

@ -1,15 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/string/conversions"
require "active_support/core_ext/string/filters"
require "active_support/core_ext/string/multibyte"
require "active_support/core_ext/string/starts_ends_with"
require "active_support/core_ext/string/inflections"
require "active_support/core_ext/string/access"
require "active_support/core_ext/string/behavior"
require "active_support/core_ext/string/output_safety"
require "active_support/core_ext/string/exclude"
require "active_support/core_ext/string/strip"
require "active_support/core_ext/string/inquiry"
require "active_support/core_ext/string/indent"
require "active_support/core_ext/string/zones"

View File

@ -1,95 +0,0 @@
# frozen_string_literal: true
class String
# If you pass a single integer, returns a substring of one character at that
# position. The first character of the string is at position 0, the next at
# position 1, and so on. If a range is supplied, a substring containing
# characters at offsets given by the range is returned. In both cases, if an
# offset is negative, it is counted from the end of the string. Returns +nil+
# if the initial offset falls outside the string. Returns an empty string if
# the beginning of the range is greater than the end of the string.
#
# str = "hello"
# str.at(0) # => "h"
# str.at(1..3) # => "ell"
# str.at(-2) # => "l"
# str.at(-2..-1) # => "lo"
# str.at(5) # => nil
# str.at(5..-1) # => ""
#
# If a Regexp is given, the matching portion of the string is returned.
# If a String is given, that given string is returned if it occurs in
# the string. In both cases, +nil+ is returned if there is no match.
#
# str = "hello"
# str.at(/lo/) # => "lo"
# str.at(/ol/) # => nil
# str.at("lo") # => "lo"
# str.at("ol") # => nil
def at(position)
self[position]
end
# Returns a substring from the given position to the end of the string.
# If the position is negative, it is counted from the end of the string.
#
# str = "hello"
# str.from(0) # => "hello"
# str.from(3) # => "lo"
# str.from(-2) # => "lo"
#
# You can mix it with +to+ method and do fun things like:
#
# str = "hello"
# str.from(0).to(-1) # => "hello"
# str.from(1).to(-2) # => "ell"
def from(position)
self[position, length]
end
# Returns a substring from the beginning of the string to the given position.
# If the position is negative, it is counted from the end of the string.
#
# str = "hello"
# str.to(0) # => "h"
# str.to(3) # => "hell"
# str.to(-2) # => "hell"
#
# You can mix it with +from+ method and do fun things like:
#
# str = "hello"
# str.from(0).to(-1) # => "hello"
# str.from(1).to(-2) # => "ell"
def to(position)
position += size if position < 0
self[0, position + 1] || +""
end
# Returns the first character. If a limit is supplied, returns a substring
# from the beginning of the string until it reaches the limit value. If the
# given limit is greater than or equal to the string length, returns a copy of self.
#
# str = "hello"
# str.first # => "h"
# str.first(1) # => "h"
# str.first(2) # => "he"
# str.first(0) # => ""
# str.first(6) # => "hello"
def first(limit = 1)
self[0, limit] || raise(ArgumentError, "negative limit")
end
# Returns the last character of the string. If a limit is supplied, returns a substring
# from the end of the string until it reaches the limit value (counting backwards). If
# the given limit is greater than or equal to the string length, returns a copy of self.
#
# str = "hello"
# str.last # => "o"
# str.last(1) # => "o"
# str.last(2) # => "lo"
# str.last(0) # => ""
# str.last(6) # => "hello"
def last(limit = 1)
self[[length - limit, 0].max, limit] || raise(ArgumentError, "negative limit")
end
end

View File

@ -1,8 +0,0 @@
# frozen_string_literal: true
class String
# Enables more predictable duck-typing on String-like classes. See <tt>Object#acts_like?</tt>.
def acts_like_string?
true
end
end

View File

@ -1,60 +0,0 @@
# frozen_string_literal: true
require "date"
require "active_support/core_ext/time/calculations"
class String
# Converts a string to a Time value.
# The +form+ can be either :utc or :local (default :local).
#
# The time is parsed using Time.parse method.
# If +form+ is :local, then the time is in the system timezone.
# If the date part is missing then the current date is used and if
# the time part is missing then it is assumed to be 00:00:00.
#
# "13-12-2012".to_time # => 2012-12-13 00:00:00 +0100
# "06:12".to_time # => 2012-12-13 06:12:00 +0100
# "2012-12-13 06:12".to_time # => 2012-12-13 06:12:00 +0100
# "2012-12-13T06:12".to_time # => 2012-12-13 06:12:00 +0100
# "2012-12-13T06:12".to_time(:utc) # => 2012-12-13 06:12:00 UTC
# "12/13/2012".to_time # => ArgumentError: argument out of range
# "1604326192".to_time # => ArgumentError: argument out of range
def to_time(form = :local)
parts = Date._parse(self, false)
used_keys = %i(year mon mday hour min sec sec_fraction offset)
return if (parts.keys & used_keys).empty?
now = Time.now
time = Time.new(
parts.fetch(:year, now.year),
parts.fetch(:mon, now.month),
parts.fetch(:mday, now.day),
parts.fetch(:hour, 0),
parts.fetch(:min, 0),
parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
parts.fetch(:offset, form == :utc ? 0 : nil)
)
form == :utc ? time.utc : time.to_time
end
# Converts a string to a Date value.
#
# "1-1-2012".to_date # => Sun, 01 Jan 2012
# "01/01/2012".to_date # => Sun, 01 Jan 2012
# "2012-12-13".to_date # => Thu, 13 Dec 2012
# "12/13/2012".to_date # => ArgumentError: invalid date
def to_date
::Date.parse(self, false) unless blank?
end
# Converts a string to a DateTime value.
#
# "1-1-2012".to_datetime # => Sun, 01 Jan 2012 00:00:00 +0000
# "01/01/2012 23:59:59".to_datetime # => Sun, 01 Jan 2012 23:59:59 +0000
# "2012-12-13 12:50".to_datetime # => Thu, 13 Dec 2012 12:50:00 +0000
# "12/13/2012".to_datetime # => ArgumentError: invalid date
def to_datetime
::DateTime.parse(self, false) unless blank?
end
end

View File

@ -1,16 +0,0 @@
# frozen_string_literal: true
require "active_support/string_inquirer"
require "active_support/environment_inquirer"
class String
# Wraps the current string in the <tt>ActiveSupport::StringInquirer</tt> class,
# which gives you a prettier way to test for equality.
#
# env = 'production'.inquiry
# env.production? # => true
# env.development? # => false
def inquiry
ActiveSupport::StringInquirer.new(self)
end
end

View File

@ -1,347 +0,0 @@
# frozen_string_literal: true
require "erb"
require "active_support/core_ext/module/redefine_method"
require "active_support/multibyte/unicode"
class ERB
module Util
HTML_ESCAPE = { "&" => "&amp;", ">" => "&gt;", "<" => "&lt;", '"' => "&quot;", "'" => "&#39;" }
JSON_ESCAPE = { "&" => '\u0026', ">" => '\u003e', "<" => '\u003c', "\u2028" => '\u2028', "\u2029" => '\u2029' }
HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+)|(#[xX][\dA-Fa-f]+));)/
JSON_ESCAPE_REGEXP = /[\u2028\u2029&><]/u
# Following XML requirements: https://www.w3.org/TR/REC-xml/#NT-Name
TAG_NAME_START_REGEXP_SET = "@:A-Z_a-z\u{C0}-\u{D6}\u{D8}-\u{F6}\u{F8}-\u{2FF}\u{370}-\u{37D}\u{37F}-\u{1FFF}" \
"\u{200C}-\u{200D}\u{2070}-\u{218F}\u{2C00}-\u{2FEF}\u{3001}-\u{D7FF}\u{F900}-\u{FDCF}" \
"\u{FDF0}-\u{FFFD}\u{10000}-\u{EFFFF}"
TAG_NAME_START_REGEXP = /[^#{TAG_NAME_START_REGEXP_SET}]/
TAG_NAME_FOLLOWING_REGEXP = /[^#{TAG_NAME_START_REGEXP_SET}\-.0-9\u{B7}\u{0300}-\u{036F}\u{203F}-\u{2040}]/
TAG_NAME_REPLACEMENT_CHAR = "_"
# A utility method for escaping HTML tag characters.
# This method is also aliased as <tt>h</tt>.
#
# puts html_escape('is a > 0 & a < 10?')
# # => is a &gt; 0 &amp; a &lt; 10?
def html_escape(s)
unwrapped_html_escape(s).html_safe
end
silence_redefinition_of_method :h
alias h html_escape
module_function :h
singleton_class.silence_redefinition_of_method :html_escape
module_function :html_escape
# HTML escapes strings but doesn't wrap them with an ActiveSupport::SafeBuffer.
# This method is not for public consumption! Seriously!
def unwrapped_html_escape(s) # :nodoc:
s = s.to_s
if s.html_safe?
s
else
CGI.escapeHTML(ActiveSupport::Multibyte::Unicode.tidy_bytes(s))
end
end
module_function :unwrapped_html_escape
# A utility method for escaping HTML without affecting existing escaped entities.
#
# html_escape_once('1 < 2 &amp; 3')
# # => "1 &lt; 2 &amp; 3"
#
# html_escape_once('&lt;&lt; Accept & Checkout')
# # => "&lt;&lt; Accept &amp; Checkout"
def html_escape_once(s)
result = ActiveSupport::Multibyte::Unicode.tidy_bytes(s.to_s).gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE)
s.html_safe? ? result.html_safe : result
end
module_function :html_escape_once
# A utility method for escaping HTML entities in JSON strings. Specifically, the
# &, > and < characters are replaced with their equivalent unicode escaped form -
# \u0026, \u003e, and \u003c. The Unicode sequences \u2028 and \u2029 are also
# escaped as they are treated as newline characters in some JavaScript engines.
# These sequences have identical meaning as the original characters inside the
# context of a JSON string, so assuming the input is a valid and well-formed
# JSON value, the output will have equivalent meaning when parsed:
#
# json = JSON.generate({ name: "</script><script>alert('PWNED!!!')</script>"})
# # => "{\"name\":\"</script><script>alert('PWNED!!!')</script>\"}"
#
# json_escape(json)
# # => "{\"name\":\"\\u003C/script\\u003E\\u003Cscript\\u003Ealert('PWNED!!!')\\u003C/script\\u003E\"}"
#
# JSON.parse(json) == JSON.parse(json_escape(json))
# # => true
#
# The intended use case for this method is to escape JSON strings before including
# them inside a script tag to avoid XSS vulnerability:
#
# <script>
# var currentUser = <%= raw json_escape(current_user.to_json) %>;
# </script>
#
# It is necessary to +raw+ the result of +json_escape+, so that quotation marks
# don't get converted to <tt>&quot;</tt> entities. +json_escape+ doesn't
# automatically flag the result as HTML safe, since the raw value is unsafe to
# use inside HTML attributes.
#
# If your JSON is being used downstream for insertion into the DOM, be aware of
# whether or not it is being inserted via <tt>html()</tt>. Most jQuery plugins do this.
# If that is the case, be sure to +html_escape+ or +sanitize+ any user-generated
# content returned by your JSON.
#
# If you need to output JSON elsewhere in your HTML, you can just do something
# like this, as any unsafe characters (including quotation marks) will be
# automatically escaped for you:
#
# <div data-user-info="<%= current_user.to_json %>">...</div>
#
# WARNING: this helper only works with valid JSON. Using this on non-JSON values
# will open up serious XSS vulnerabilities. For example, if you replace the
# +current_user.to_json+ in the example above with user input instead, the browser
# will happily eval() that string as JavaScript.
#
# The escaping performed in this method is identical to those performed in the
# Active Support JSON encoder when +ActiveSupport.escape_html_entities_in_json+ is
# set to true. Because this transformation is idempotent, this helper can be
# applied even if +ActiveSupport.escape_html_entities_in_json+ is already true.
#
# Therefore, when you are unsure if +ActiveSupport.escape_html_entities_in_json+
# is enabled, or if you are unsure where your JSON string originated from, it
# is recommended that you always apply this helper (other libraries, such as the
# JSON gem, do not provide this kind of protection by default; also some gems
# might override +to_json+ to bypass Active Support's encoder).
def json_escape(s)
result = s.to_s.gsub(JSON_ESCAPE_REGEXP, JSON_ESCAPE)
s.html_safe? ? result.html_safe : result
end
module_function :json_escape
# A utility method for escaping XML names of tags and names of attributes.
#
# xml_name_escape('1 < 2 & 3')
# # => "1___2___3"
#
# It follows the requirements of the specification: https://www.w3.org/TR/REC-xml/#NT-Name
def xml_name_escape(name)
name = name.to_s
return "" if name.blank?
starting_char = name[0].gsub(TAG_NAME_START_REGEXP, TAG_NAME_REPLACEMENT_CHAR)
return starting_char if name.size == 1
following_chars = name[1..-1].gsub(TAG_NAME_FOLLOWING_REGEXP, TAG_NAME_REPLACEMENT_CHAR)
starting_char + following_chars
end
module_function :xml_name_escape
end
end
class Object
def html_safe?
false
end
end
class Numeric
def html_safe?
true
end
end
module ActiveSupport #:nodoc:
class SafeBuffer < String
UNSAFE_STRING_METHODS = %w(
capitalize chomp chop delete delete_prefix delete_suffix
downcase lstrip next reverse rstrip scrub slice squeeze strip
succ swapcase tr tr_s unicode_normalize upcase
)
UNSAFE_STRING_METHODS_WITH_BACKREF = %w(gsub sub)
alias_method :original_concat, :concat
private :original_concat
# Raised when <tt>ActiveSupport::SafeBuffer#safe_concat</tt> is called on unsafe buffers.
class SafeConcatError < StandardError
def initialize
super "Could not concatenate to the buffer because it is not html safe."
end
end
def [](*args)
if html_safe?
new_string = super
return unless new_string
new_safe_buffer = new_string.is_a?(SafeBuffer) ? new_string : SafeBuffer.new(new_string)
new_safe_buffer.instance_variable_set :@html_safe, true
new_safe_buffer
else
to_str[*args]
end
end
def safe_concat(value)
raise SafeConcatError unless html_safe?
original_concat(value)
end
def initialize(str = "")
@html_safe = true
super
end
def initialize_copy(other)
super
@html_safe = other.html_safe?
end
def clone_empty
self[0, 0]
end
def concat(value)
super(html_escape_interpolated_argument(value))
end
alias << concat
def bytesplice(*args, value)
super(*args, implicit_html_escape_interpolated_argument(value))
end
def insert(index, value)
super(index, html_escape_interpolated_argument(value))
end
def prepend(value)
super(html_escape_interpolated_argument(value))
end
def replace(value)
super(html_escape_interpolated_argument(value))
end
def []=(*args)
if args.length == 3
super(args[0], args[1], html_escape_interpolated_argument(args[2]))
else
super(args[0], html_escape_interpolated_argument(args[1]))
end
end
def +(other)
dup.concat(other)
end
def *(*)
new_string = super
new_safe_buffer = new_string.is_a?(SafeBuffer) ? new_string : SafeBuffer.new(new_string)
new_safe_buffer.instance_variable_set(:@html_safe, @html_safe)
new_safe_buffer
end
def %(args)
case args
when Hash
escaped_args = args.transform_values { |arg| html_escape_interpolated_argument(arg) }
else
escaped_args = Array(args).map { |arg| html_escape_interpolated_argument(arg) }
end
self.class.new(super(escaped_args))
end
def html_safe?
defined?(@html_safe) && @html_safe
end
def to_s
self
end
def to_param
to_str
end
def encode_with(coder)
coder.represent_object nil, to_str
end
UNSAFE_STRING_METHODS.each do |unsafe_method|
if unsafe_method.respond_to?(unsafe_method)
class_eval <<-EOT, __FILE__, __LINE__ + 1
def #{unsafe_method}(*args, &block) # def capitalize(*args, &block)
to_str.#{unsafe_method}(*args, &block) # to_str.capitalize(*args, &block)
end # end
def #{unsafe_method}!(*args) # def capitalize!(*args)
@html_safe = false # @html_safe = false
super # super
end # end
EOT
end
end
UNSAFE_STRING_METHODS_WITH_BACKREF.each do |unsafe_method|
if unsafe_method.respond_to?(unsafe_method)
class_eval <<-EOT, __FILE__, __LINE__ + 1
def #{unsafe_method}(*args, &block) # def gsub(*args, &block)
if block # if block
to_str.#{unsafe_method}(*args) { |*params| # to_str.gsub(*args) { |*params|
set_block_back_references(block, $~) # set_block_back_references(block, $~)
block.call(*params) # block.call(*params)
} # }
else # else
to_str.#{unsafe_method}(*args) # to_str.gsub(*args)
end # end
end # end
def #{unsafe_method}!(*args, &block) # def gsub!(*args, &block)
@html_safe = false # @html_safe = false
if block # if block
super(*args) { |*params| # super(*args) { |*params|
set_block_back_references(block, $~) # set_block_back_references(block, $~)
block.call(*params) # block.call(*params)
} # }
else # else
super # super
end # end
end # end
EOT
end
end
private
def html_escape_interpolated_argument(arg)
(!html_safe? || arg.html_safe?) ? arg : CGI.escapeHTML(arg.to_s)
end
def set_block_back_references(block, match_data)
block.binding.eval("proc { |m| $~ = m }").call(match_data)
rescue ArgumentError
# Can't create binding from C level Proc
end
end
end
class String
# Marks a string as trusted safe. It will be inserted into HTML with no
# additional escaping performed. It is your responsibility to ensure that the
# string contains no malicious content. This method is equivalent to the
# +raw+ helper in views. It is recommended that you use +sanitize+ instead of
# this method. It should never be called on user input.
def html_safe
ActiveSupport::SafeBuffer.new(self)
end
end

View File

@ -1,6 +0,0 @@
# frozen_string_literal: true
class String
alias :starts_with? :start_with?
alias :ends_with? :end_with?
end

View File

@ -1,27 +0,0 @@
# frozen_string_literal: true
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.
def strip_heredoc
gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, "").tap do |stripped|
stripped.freeze if frozen?
end
end
end

View File

@ -1,16 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/string/conversions"
require "active_support/core_ext/time/zones"
class String
# Converts String to a TimeWithZone in the current zone if Time.zone or Time.zone_default
# is set, otherwise converts String to a Time via String#to_time
def in_time_zone(zone = ::Time.zone)
if zone
::Time.find_zone!(zone).parse(self)
else
to_time
end
end
end

View File

@ -1,3 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/symbol/starts_ends_with"

View File

@ -1,14 +0,0 @@
# frozen_string_literal: true
class Symbol
def start_with?(*prefixes)
to_s.start_with?(*prefixes)
end unless method_defined?(:start_with?)
def end_with?(*suffixes)
to_s.end_with?(*suffixes)
end unless method_defined?(:end_with?)
alias :starts_with? :start_with?
alias :ends_with? :end_with?
end

View File

@ -1,7 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/time/acts_like"
require "active_support/core_ext/time/calculations"
require "active_support/core_ext/time/compatibility"
require "active_support/core_ext/time/conversions"
require "active_support/core_ext/time/zones"

View File

@ -1,10 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/object/acts_like"
class Time
# Duck-types as a Time-like class. See Object#acts_like?.
def acts_like_time?
true
end
end

View File

@ -1,365 +0,0 @@
# frozen_string_literal: true
require "active_support/duration"
require "active_support/core_ext/time/conversions"
require "active_support/time_with_zone"
require "active_support/core_ext/time/zones"
require "active_support/core_ext/date_and_time/calculations"
require "active_support/core_ext/date/calculations"
require "active_support/core_ext/module/remove_method"
class Time
include DateAndTime::Calculations
COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
class << self
# Overriding case equality method so that it returns true for ActiveSupport::TimeWithZone instances
def ===(other)
super || (self == Time && other.is_a?(ActiveSupport::TimeWithZone))
end
# Returns the number of days in the given month.
# If no year is specified, it will use the current year.
def days_in_month(month, year = current.year)
if month == 2 && ::Date.gregorian_leap?(year)
29
else
COMMON_YEAR_DAYS_IN_MONTH[month]
end
end
# Returns the number of days in the given year.
# If no year is specified, it will use the current year.
def days_in_year(year = current.year)
days_in_month(2, year) + 337
end
# Returns <tt>Time.zone.now</tt> when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise just returns <tt>Time.now</tt>.
def current
::Time.zone ? ::Time.zone.now : ::Time.now
end
# Layers additional behavior on Time.at so that ActiveSupport::TimeWithZone and DateTime
# instances can be used when called with a single argument
def at_with_coercion(*args)
return at_without_coercion(*args) if args.size != 1
# Time.at can be called with a time or numerical value
time_or_number = args.first
if time_or_number.is_a?(ActiveSupport::TimeWithZone)
at_without_coercion(time_or_number.to_r).getlocal
elsif time_or_number.is_a?(DateTime)
at_without_coercion(time_or_number.to_f).getlocal
else
at_without_coercion(time_or_number)
end
end
ruby2_keywords(:at_with_coercion) if respond_to?(:ruby2_keywords, true)
alias_method :at_without_coercion, :at
alias_method :at, :at_with_coercion
# Creates a +Time+ instance from an RFC 3339 string.
#
# Time.rfc3339('1999-12-31T14:00:00-10:00') # => 2000-01-01 00:00:00 -1000
#
# If the time or offset components are missing then an +ArgumentError+ will be raised.
#
# Time.rfc3339('1999-12-31') # => ArgumentError: invalid date
def rfc3339(str)
parts = Date._rfc3339(str)
raise ArgumentError, "invalid date" if parts.empty?
Time.new(
parts.fetch(:year),
parts.fetch(:mon),
parts.fetch(:mday),
parts.fetch(:hour),
parts.fetch(:min),
parts.fetch(:sec) + parts.fetch(:sec_fraction, 0),
parts.fetch(:offset)
)
end
end
# Returns the number of seconds since 00:00:00.
#
# Time.new(2012, 8, 29, 0, 0, 0).seconds_since_midnight # => 0.0
# Time.new(2012, 8, 29, 12, 34, 56).seconds_since_midnight # => 45296.0
# Time.new(2012, 8, 29, 23, 59, 59).seconds_since_midnight # => 86399.0
def seconds_since_midnight
to_i - change(hour: 0).to_i + (usec / 1.0e+6)
end
# Returns the number of seconds until 23:59:59.
#
# Time.new(2012, 8, 29, 0, 0, 0).seconds_until_end_of_day # => 86399
# Time.new(2012, 8, 29, 12, 34, 56).seconds_until_end_of_day # => 41103
# Time.new(2012, 8, 29, 23, 59, 59).seconds_until_end_of_day # => 0
def seconds_until_end_of_day
end_of_day.to_i - to_i
end
# Returns the fraction of a second as a +Rational+
#
# Time.new(2012, 8, 29, 0, 0, 0.5).sec_fraction # => (1/2)
def sec_fraction
subsec
end
unless Time.method_defined?(:floor)
def floor(precision = 0)
change(nsec: 0) + subsec.floor(precision)
end
end
# Restricted Ruby version due to a bug in `Time#ceil`
# See https://bugs.ruby-lang.org/issues/17025 for more details
if RUBY_VERSION <= "2.8"
remove_possible_method :ceil
def ceil(precision = 0)
change(nsec: 0) + subsec.ceil(precision)
end
end
# Returns a new Time where one or more of the elements have been changed according
# to the +options+ parameter. The time options (<tt>:hour</tt>, <tt>:min</tt>,
# <tt>:sec</tt>, <tt>:usec</tt>, <tt>:nsec</tt>) reset cascadingly, so if only
# the hour is passed, then minute, sec, usec and nsec is set to 0. If the hour
# and minute is passed, then sec, usec and nsec is set to 0. The +options+ parameter
# takes a hash with any of these keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>,
# <tt>:hour</tt>, <tt>:min</tt>, <tt>:sec</tt>, <tt>:usec</tt>, <tt>:nsec</tt>,
# <tt>:offset</tt>. Pass either <tt>:usec</tt> or <tt>:nsec</tt>, not both.
#
# Time.new(2012, 8, 29, 22, 35, 0).change(day: 1) # => Time.new(2012, 8, 1, 22, 35, 0)
# Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, day: 1) # => Time.new(1981, 8, 1, 22, 35, 0)
# Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, hour: 0) # => Time.new(1981, 8, 29, 0, 0, 0)
def change(options)
new_year = options.fetch(:year, year)
new_month = options.fetch(:month, month)
new_day = options.fetch(:day, day)
new_hour = options.fetch(:hour, hour)
new_min = options.fetch(:min, options[:hour] ? 0 : min)
new_sec = options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec)
new_offset = options.fetch(:offset, nil)
if new_nsec = options[:nsec]
raise ArgumentError, "Can't change both :nsec and :usec at the same time: #{options.inspect}" if options[:usec]
new_usec = Rational(new_nsec, 1000)
else
new_usec = options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000))
end
raise ArgumentError, "argument out of range" if new_usec >= 1000000
new_sec += Rational(new_usec, 1000000)
if new_offset
::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec, new_offset)
elsif utc?
::Time.utc(new_year, new_month, new_day, new_hour, new_min, new_sec)
elsif zone&.respond_to?(:utc_to_local)
::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec, zone)
elsif zone
::Time.local(new_year, new_month, new_day, new_hour, new_min, new_sec)
else
::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec, utc_offset)
end
end
# Uses Date to provide precise Time calculations for years, months, and days
# according to the proleptic Gregorian calendar. The +options+ parameter
# takes a hash with any of these keys: <tt>:years</tt>, <tt>:months</tt>,
# <tt>:weeks</tt>, <tt>:days</tt>, <tt>:hours</tt>, <tt>:minutes</tt>,
# <tt>:seconds</tt>.
#
# Time.new(2015, 8, 1, 14, 35, 0).advance(seconds: 1) # => 2015-08-01 14:35:01 -0700
# Time.new(2015, 8, 1, 14, 35, 0).advance(minutes: 1) # => 2015-08-01 14:36:00 -0700
# Time.new(2015, 8, 1, 14, 35, 0).advance(hours: 1) # => 2015-08-01 15:35:00 -0700
# Time.new(2015, 8, 1, 14, 35, 0).advance(days: 1) # => 2015-08-02 14:35:00 -0700
# Time.new(2015, 8, 1, 14, 35, 0).advance(weeks: 1) # => 2015-08-08 14:35:00 -0700
def advance(options)
unless options[:weeks].nil?
options[:weeks], partial_weeks = options[:weeks].divmod(1)
options[:days] = options.fetch(:days, 0) + 7 * partial_weeks
end
unless options[:days].nil?
options[:days], partial_days = options[:days].divmod(1)
options[:hours] = options.fetch(:hours, 0) + 24 * partial_days
end
d = to_date.gregorian.advance(options)
time_advanced_by_date = change(year: d.year, month: d.month, day: d.day)
seconds_to_advance = \
options.fetch(:seconds, 0) +
options.fetch(:minutes, 0) * 60 +
options.fetch(:hours, 0) * 3600
if seconds_to_advance.zero?
time_advanced_by_date
else
time_advanced_by_date.since(seconds_to_advance)
end
end
# Returns a new Time representing the time a number of seconds ago, this is basically a wrapper around the Numeric extension
def ago(seconds)
since(-seconds)
end
# Returns a new Time representing the time a number of seconds since the instance time
def since(seconds)
self + seconds
rescue
to_datetime.since(seconds)
end
alias :in :since
# Returns a new Time representing the start of the day (0:00)
def beginning_of_day
change(hour: 0)
end
alias :midnight :beginning_of_day
alias :at_midnight :beginning_of_day
alias :at_beginning_of_day :beginning_of_day
# Returns a new Time representing the middle of the day (12:00)
def middle_of_day
change(hour: 12)
end
alias :midday :middle_of_day
alias :noon :middle_of_day
alias :at_midday :middle_of_day
alias :at_noon :middle_of_day
alias :at_middle_of_day :middle_of_day
# Returns a new Time representing the end of the day, 23:59:59.999999
def end_of_day
change(
hour: 23,
min: 59,
sec: 59,
usec: Rational(999999999, 1000)
)
end
alias :at_end_of_day :end_of_day
# Returns a new Time representing the start of the hour (x:00)
def beginning_of_hour
change(min: 0)
end
alias :at_beginning_of_hour :beginning_of_hour
# Returns a new Time representing the end of the hour, x:59:59.999999
def end_of_hour
change(
min: 59,
sec: 59,
usec: Rational(999999999, 1000)
)
end
alias :at_end_of_hour :end_of_hour
# Returns a new Time representing the start of the minute (x:xx:00)
def beginning_of_minute
change(sec: 0)
end
alias :at_beginning_of_minute :beginning_of_minute
# Returns a new Time representing the end of the minute, x:xx:59.999999
def end_of_minute
change(
sec: 59,
usec: Rational(999999999, 1000)
)
end
alias :at_end_of_minute :end_of_minute
def plus_with_duration(other) #:nodoc:
if ActiveSupport::Duration === other
other.since(self)
else
plus_without_duration(other)
end
end
alias_method :plus_without_duration, :+
alias_method :+, :plus_with_duration
def minus_with_duration(other) #:nodoc:
if ActiveSupport::Duration === other
other.until(self)
else
minus_without_duration(other)
end
end
alias_method :minus_without_duration, :-
alias_method :-, :minus_with_duration
# Time#- can also be used to determine the number of seconds between two Time instances.
# We're layering on additional behavior so that ActiveSupport::TimeWithZone instances
# are coerced into values that Time#- will recognize
def minus_with_coercion(other)
other = other.comparable_time if other.respond_to?(:comparable_time)
other.is_a?(DateTime) ? to_f - other.to_f : minus_without_coercion(other)
end
alias_method :minus_without_coercion, :-
alias_method :-, :minus_with_coercion
# Layers additional behavior on Time#<=> so that DateTime and ActiveSupport::TimeWithZone instances
# can be chronologically compared with a Time
def compare_with_coercion(other)
# we're avoiding Time#to_datetime and Time#to_time because they're expensive
if other.class == Time
compare_without_coercion(other)
elsif other.is_a?(Time)
compare_without_coercion(other.to_time)
else
to_datetime <=> other
end
end
alias_method :compare_without_coercion, :<=>
alias_method :<=>, :compare_with_coercion
# Layers additional behavior on Time#eql? so that ActiveSupport::TimeWithZone instances
# can be eql? to an equivalent Time
def eql_with_coercion(other)
# if other is an ActiveSupport::TimeWithZone, coerce a Time instance from it so we can do eql? comparison
other = other.comparable_time if other.respond_to?(:comparable_time)
eql_without_coercion(other)
end
alias_method :eql_without_coercion, :eql?
alias_method :eql?, :eql_with_coercion
# Returns a new time the specified number of days ago.
def prev_day(days = 1)
advance(days: -days)
end
# Returns a new time the specified number of days in the future.
def next_day(days = 1)
advance(days: days)
end
# Returns a new time the specified number of months ago.
def prev_month(months = 1)
advance(months: -months)
end
# Returns a new time the specified number of months in the future.
def next_month(months = 1)
advance(months: months)
end
# Returns a new time the specified number of years ago.
def prev_year(years = 1)
advance(years: -years)
end
# Returns a new time the specified number of years in the future.
def next_year(years = 1)
advance(years: years)
end
end

View File

@ -1,16 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/date_and_time/compatibility"
require "active_support/core_ext/module/redefine_method"
class Time
include DateAndTime::Compatibility
silence_redefinition_of_method :to_time
# Either return +self+ or the time in the local system timezone depending
# on the setting of +ActiveSupport.to_time_preserves_timezone+.
def to_time
preserve_timezone ? self : getlocal
end
end

View File

@ -1,74 +0,0 @@
# frozen_string_literal: true
require "time"
require "active_support/inflector/methods"
require "active_support/values/time_zone"
class Time
DATE_FORMATS = {
db: "%Y-%m-%d %H:%M:%S",
inspect: "%Y-%m-%d %H:%M:%S.%9N %z",
number: "%Y%m%d%H%M%S",
nsec: "%Y%m%d%H%M%S%9N",
usec: "%Y%m%d%H%M%S%6N",
time: "%H:%M",
short: "%d %b %H:%M",
long: "%B %d, %Y %H:%M",
long_ordinal: lambda { |time|
day_format = ActiveSupport::Inflector.ordinalize(time.day)
time.strftime("%B #{day_format}, %Y %H:%M")
},
rfc822: lambda { |time|
offset_format = time.formatted_offset(false)
time.strftime("%a, %d %b %Y %H:%M:%S #{offset_format}")
},
iso8601: lambda { |time| time.iso8601 }
}
# Converts to a formatted string. See DATE_FORMATS for built-in formats.
#
# This method is aliased to <tt>to_s</tt>.
#
# time = Time.now # => 2007-01-18 06:10:17 -06:00
#
# time.to_formatted_s(:time) # => "06:10"
# time.to_s(:time) # => "06:10"
#
# time.to_formatted_s(:db) # => "2007-01-18 06:10:17"
# time.to_formatted_s(:number) # => "20070118061017"
# time.to_formatted_s(:short) # => "18 Jan 06:10"
# time.to_formatted_s(:long) # => "January 18, 2007 06:10"
# time.to_formatted_s(:long_ordinal) # => "January 18th, 2007 06:10"
# time.to_formatted_s(:rfc822) # => "Thu, 18 Jan 2007 06:10:17 -0600"
# time.to_formatted_s(:iso8601) # => "2007-01-18T06:10:17-06:00"
#
# == Adding your own time formats to +to_formatted_s+
# You can add your own formats to the Time::DATE_FORMATS hash.
# Use the format name as the hash key and either a strftime string
# or Proc instance that takes a time argument as the value.
#
# # config/initializers/time_formats.rb
# Time::DATE_FORMATS[:month_and_year] = '%B %Y'
# Time::DATE_FORMATS[:short_ordinal] = ->(time) { time.strftime("%B #{time.day.ordinalize}") }
def to_formatted_s(format = :default)
if formatter = DATE_FORMATS[format]
formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
else
to_default_s
end
end
alias_method :to_default_s, :to_s
alias_method :to_s, :to_formatted_s
# Returns a formatted string of the offset from UTC, or an alternative
# string if the time zone is already UTC.
#
# Time.local(2000).formatted_offset # => "-06:00"
# Time.local(2000).formatted_offset(false) # => "-0600"
def formatted_offset(colon = true, alternate_utc_string = nil)
utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon)
end
# Aliased to +xmlschema+ for compatibility with +DateTime+
alias_method :rfc3339, :xmlschema
end

View File

@ -1,113 +0,0 @@
# frozen_string_literal: true
require "active_support/time_with_zone"
require "active_support/core_ext/time/acts_like"
require "active_support/core_ext/date_and_time/zones"
class Time
include DateAndTime::Zones
class << self
attr_accessor :zone_default
# Returns the TimeZone for the current request, if this has been set (via Time.zone=).
# If <tt>Time.zone</tt> has not been set for the current request, returns the TimeZone specified in <tt>config.time_zone</tt>.
def zone
Thread.current[:time_zone] || zone_default
end
# Sets <tt>Time.zone</tt> to a TimeZone object for the current request/thread.
#
# This method accepts any of the following:
#
# * A Rails TimeZone object.
# * An identifier for a Rails TimeZone object (e.g., "Eastern Time (US & Canada)", <tt>-5.hours</tt>).
# * A TZInfo::Timezone object.
# * An identifier for a TZInfo::Timezone object (e.g., "America/New_York").
#
# Here's an example of how you might set <tt>Time.zone</tt> on a per request basis and reset it when the request is done.
# <tt>current_user.time_zone</tt> just needs to return a string identifying the user's preferred time zone:
#
# class ApplicationController < ActionController::Base
# around_action :set_time_zone
#
# def set_time_zone
# if logged_in?
# Time.use_zone(current_user.time_zone) { yield }
# else
# yield
# end
# end
# end
def zone=(time_zone)
Thread.current[:time_zone] = find_zone!(time_zone)
end
# Allows override of <tt>Time.zone</tt> locally inside supplied block;
# resets <tt>Time.zone</tt> to existing value when done.
#
# class ApplicationController < ActionController::Base
# around_action :set_time_zone
#
# private
#
# def set_time_zone
# Time.use_zone(current_user.timezone) { yield }
# end
# end
#
# NOTE: This won't affect any <tt>ActiveSupport::TimeWithZone</tt>
# objects that have already been created, e.g. any model timestamp
# attributes that have been read before the block will remain in
# the application's default timezone.
def use_zone(time_zone)
new_zone = find_zone!(time_zone)
begin
old_zone, ::Time.zone = ::Time.zone, new_zone
yield
ensure
::Time.zone = old_zone
end
end
# Returns a TimeZone instance matching the time zone provided.
# Accepts the time zone in any format supported by <tt>Time.zone=</tt>.
# Raises an +ArgumentError+ for invalid time zones.
#
# Time.find_zone! "America/New_York" # => #<ActiveSupport::TimeZone @name="America/New_York" ...>
# Time.find_zone! "EST" # => #<ActiveSupport::TimeZone @name="EST" ...>
# Time.find_zone! -5.hours # => #<ActiveSupport::TimeZone @name="Bogota" ...>
# Time.find_zone! nil # => nil
# Time.find_zone! false # => false
# Time.find_zone! "NOT-A-TIMEZONE" # => ArgumentError: Invalid Timezone: NOT-A-TIMEZONE
def find_zone!(time_zone)
if !time_zone || time_zone.is_a?(ActiveSupport::TimeZone)
time_zone
else
# Look up the timezone based on the identifier (unless we've been
# passed a TZInfo::Timezone)
unless time_zone.respond_to?(:period_for_local)
time_zone = ActiveSupport::TimeZone[time_zone] || TZInfo::Timezone.get(time_zone)
end
# Return if a TimeZone instance, or wrap in a TimeZone instance if a TZInfo::Timezone
if time_zone.is_a?(ActiveSupport::TimeZone)
time_zone
else
ActiveSupport::TimeZone.create(time_zone.name, nil, time_zone)
end
end
rescue TZInfo::InvalidTimezoneIdentifier
raise ArgumentError, "Invalid Timezone: #{time_zone}"
end
# Returns a TimeZone instance matching the time zone provided.
# Accepts the time zone in any format supported by <tt>Time.zone=</tt>.
# Returns +nil+ for invalid time zones.
#
# Time.find_zone "America/New_York" # => #<ActiveSupport::TimeZone @name="America/New_York" ...>
# Time.find_zone "NOT-A-TIMEZONE" # => nil
def find_zone(time_zone)
find_zone!(time_zone) rescue nil
end
end
end

View File

@ -1,29 +0,0 @@
# frozen_string_literal: true
require "uri"
if RUBY_VERSION < "2.6.0"
require "active_support/core_ext/module/redefine_method"
URI::Parser.class_eval do
silence_redefinition_of_method :unescape
def unescape(str, escaped = /%[a-fA-F\d]{2}/)
# TODO: Are we actually sure that ASCII == UTF-8?
# YK: My initial experiments say yes, but let's be sure please
enc = str.encoding
enc = Encoding::UTF_8 if enc == Encoding::US_ASCII
str.dup.force_encoding(Encoding::ASCII_8BIT).gsub(escaped) { |match| [match[1, 2].hex].pack("C") }.force_encoding(enc)
end
end
end
module URI
class << self
def parser
ActiveSupport::Deprecation.warn(<<-MSG.squish)
URI.parser is deprecated and will be removed in Rails 7.0.
Use `URI::DEFAULT_PARSER` instead.
MSG
URI::DEFAULT_PARSER
end
end
end

View File

@ -1,210 +0,0 @@
# frozen_string_literal: true
require "active_support/callbacks"
require "active_support/core_ext/enumerable"
require "active_support/core_ext/module/delegation"
module ActiveSupport
# Abstract super class that provides a thread-isolated attributes singleton, which resets automatically
# before and after each request. This allows you to keep all the per-request attributes easily
# available to the whole system.
#
# The following full app-like example demonstrates how to use a Current class to
# facilitate easy access to the global, per-request attributes without passing them deeply
# around everywhere:
#
# # app/models/current.rb
# class Current < ActiveSupport::CurrentAttributes
# attribute :account, :user
# attribute :request_id, :user_agent, :ip_address
#
# resets { Time.zone = nil }
#
# def user=(user)
# super
# self.account = user.account
# Time.zone = user.time_zone
# end
# end
#
# # app/controllers/concerns/authentication.rb
# module Authentication
# extend ActiveSupport::Concern
#
# included do
# before_action :authenticate
# end
#
# private
# def authenticate
# if authenticated_user = User.find_by(id: cookies.encrypted[:user_id])
# Current.user = authenticated_user
# else
# redirect_to new_session_url
# end
# end
# end
#
# # app/controllers/concerns/set_current_request_details.rb
# module SetCurrentRequestDetails
# extend ActiveSupport::Concern
#
# included do
# before_action do
# Current.request_id = request.uuid
# Current.user_agent = request.user_agent
# Current.ip_address = request.ip
# end
# end
# end
#
# class ApplicationController < ActionController::Base
# include Authentication
# include SetCurrentRequestDetails
# end
#
# class MessagesController < ApplicationController
# def create
# Current.account.messages.create(message_params)
# end
# end
#
# class Message < ApplicationRecord
# belongs_to :creator, default: -> { Current.user }
# after_create { |message| Event.create(record: message) }
# end
#
# class Event < ApplicationRecord
# before_create do
# self.request_id = Current.request_id
# self.user_agent = Current.user_agent
# self.ip_address = Current.ip_address
# end
# end
#
# A word of caution: It's easy to overdo a global singleton like Current and tangle your model as a result.
# Current should only be used for a few, top-level globals, like account, user, and request details.
# The attributes stuck in Current should be used by more or less all actions on all requests. If you start
# sticking controller-specific attributes in there, you're going to create a mess.
class CurrentAttributes
include ActiveSupport::Callbacks
define_callbacks :reset
class << self
# Returns singleton instance for this class in this thread. If none exists, one is created.
def instance
current_instances[current_instances_key] ||= new
end
# Declares one or more attributes that will be given both class and instance accessor methods.
def attribute(*names)
generated_attribute_methods.module_eval do
names.each do |name|
define_method(name) do
attributes[name.to_sym]
end
define_method("#{name}=") do |attribute|
attributes[name.to_sym] = attribute
end
end
end
names.each do |name|
define_singleton_method(name) do
instance.public_send(name)
end
define_singleton_method("#{name}=") do |attribute|
instance.public_send("#{name}=", attribute)
end
end
end
# Calls this block before #reset is called on the instance. Used for resetting external collaborators that depend on current values.
def before_reset(&block)
set_callback :reset, :before, &block
end
# Calls this block after #reset is called on the instance. Used for resetting external collaborators, like Time.zone.
def resets(&block)
set_callback :reset, :after, &block
end
alias_method :after_reset, :resets
delegate :set, :reset, to: :instance
def reset_all # :nodoc:
current_instances.each_value(&:reset)
end
def clear_all # :nodoc:
reset_all
current_instances.clear
end
private
def generated_attribute_methods
@generated_attribute_methods ||= Module.new.tap { |mod| include mod }
end
def current_instances
Thread.current[:current_attributes_instances] ||= {}
end
def current_instances_key
@current_instances_key ||= name.to_sym
end
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
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
end
attr_accessor :attributes
def initialize
@attributes = {}
end
# Expose one or more attributes within a block. Old values are returned after the block concludes.
# Example demonstrating the common use of needing to set Current attributes outside the request-cycle:
#
# class Chat::PublicationJob < ApplicationJob
# def perform(attributes, room_number, creator)
# Current.set(person: creator) do
# Chat::Publisher.publish(attributes: attributes, room_number: room_number)
# end
# end
# end
def set(set_attributes)
old_attributes = compute_attributes(set_attributes.keys)
assign_attributes(set_attributes)
yield
ensure
assign_attributes(old_attributes)
end
# Reset all attributes. Should be called before and after actions, when used as a per-request singleton.
def reset
run_callbacks :reset do
self.attributes = {}
end
end
private
def assign_attributes(new_attributes)
new_attributes.each { |key, value| public_send("#{key}=", value) }
end
def compute_attributes(keys)
keys.index_with { |key| public_send(key) }
end
end
end

View File

@ -1,13 +0,0 @@
# frozen_string_literal: true
module ActiveSupport::CurrentAttributes::TestHelper # :nodoc:
def before_setup
ActiveSupport::CurrentAttributes.reset_all
super
end
def after_teardown
super
ActiveSupport::CurrentAttributes.reset_all
end
end

View File

@ -1,51 +0,0 @@
# frozen_string_literal: true
require "singleton"
module ActiveSupport
# \Deprecation specifies the API used by Rails to deprecate methods, instance
# variables, objects and constants.
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.
require "active_support/deprecation/instance_delegator"
require "active_support/deprecation/behaviors"
require "active_support/deprecation/reporting"
require "active_support/deprecation/disallowed"
require "active_support/deprecation/constant_accessor"
require "active_support/deprecation/method_wrappers"
require "active_support/deprecation/proxy_wrappers"
require "active_support/core_ext/module/deprecation"
require "concurrent/atomic/thread_local_var"
include Singleton
include InstanceDelegator
include Behavior
include Reporting
include Disallowed
include MethodWrapper
# The version number in which the deprecated behavior will be removed, by default.
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')
def initialize(deprecation_horizon = "7.0", gem_name = "Rails")
self.gem_name = gem_name
self.deprecation_horizon = deprecation_horizon
# By default, warnings are not silenced and debugging is off.
self.silenced = false
self.debug = false
@silenced_thread = Concurrent::ThreadLocalVar.new(false)
@explicitly_allowed_warnings = Concurrent::ThreadLocalVar.new(nil)
end
end
end

View File

@ -1,122 +0,0 @@
# frozen_string_literal: true
require "active_support/notifications"
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.
class DeprecationException < StandardError
end
class Deprecation
# Default warning behaviors per Rails.env.
DEFAULT_BEHAVIORS = {
raise: ->(message, callstack, deprecation_horizon, gem_name) {
e = DeprecationException.new(message)
e.set_backtrace(callstack.map(&:to_s))
raise e
},
stderr: ->(message, callstack, deprecation_horizon, gem_name) {
$stderr.puts(message)
$stderr.puts callstack.join("\n ") if debug
},
log: ->(message, callstack, deprecation_horizon, gem_name) {
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, deprecation_horizon, gem_name) {
notification_name = "deprecation.#{gem_name.underscore.tr('/', '_')}"
ActiveSupport::Notifications.instrument(notification_name,
message: message,
callstack: callstack,
gem_name: gem_name,
deprecation_horizon: deprecation_horizon)
},
silence: ->(message, callstack, deprecation_horizon, gem_name) { },
}
# 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 <tt>$stderr</tt>.
# [+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.
module Behavior
# Whether to print a backtrace along with the warning.
attr_accessor :debug
# Returns the current behavior or if one isn't set, defaults to +:stderr+.
def behavior
@behavior ||= [DEFAULT_BEHAVIORS[:stderr]]
end
# Returns the current behavior for disallowed deprecations or if one isn't set, defaults to +:raise+.
def disallowed_behavior
@disallowed_behavior ||= [DEFAULT_BEHAVIORS[:raise]]
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 <tt>$stderr</tt>.
# [+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, deprecation_horizon, gem_name) {
# # custom stuff
# }
def behavior=(behavior)
@behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || arity_coerce(b) }
end
# Sets the behavior for disallowed deprecations (those configured by
# ActiveSupport::Deprecation.disallowed_warnings=) to the specified
# value. As with +behavior=+, this can be a single value, array, or an
# object that responds to +call+.
def disallowed_behavior=(behavior)
@disallowed_behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || arity_coerce(b) }
end
private
def arity_coerce(behavior)
unless behavior.respond_to?(:call)
raise ArgumentError, "#{behavior.inspect} is not a valid deprecation behavior."
end
if behavior.arity == 4 || behavior.arity == -1
behavior
else
-> message, callstack, _, _ { behavior.call(message, callstack) }
end
end
end
end
end

View File

@ -1,52 +0,0 @@
# frozen_string_literal: true
module ActiveSupport
class Deprecation
# DeprecatedConstantAccessor transforms a constant into a deprecated one by
# hooking +const_missing+.
#
# 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 same object as the new one rather
# than a proxy object, so it can be used transparently in +rescue+ blocks
# etc.
#
# 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)
# include ActiveSupport::Deprecation::DeprecatedConstantAccessor
# deprecate_constant '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"]
module DeprecatedConstantAccessor
def self.included(base)
require "active_support/inflector/methods"
extension = Module.new do
def const_missing(missing_const_name)
if class_variable_defined?(:@@_deprecated_constants)
if (replacement = class_variable_get(:@@_deprecated_constants)[missing_const_name.to_s])
replacement[:deprecator].warn(replacement[:message] || "#{name}::#{missing_const_name} is deprecated! Use #{replacement[:new]} instead.", caller_locations)
return ActiveSupport::Inflector.constantize(replacement[:new].to_s)
end
end
super
end
def deprecate_constant(const_name, new_constant, message: nil, deprecator: ActiveSupport::Deprecation.instance)
class_variable_set(:@@_deprecated_constants, {}) unless class_variable_defined?(:@@_deprecated_constants)
class_variable_get(:@@_deprecated_constants)[const_name.to_s] = { new: new_constant, message: message, deprecator: deprecator }
end
end
base.singleton_class.prepend extension
end
end
end
end

View File

@ -1,56 +0,0 @@
# frozen_string_literal: true
module ActiveSupport
class Deprecation
module Disallowed
# Sets the criteria used to identify deprecation messages which should be
# disallowed. Can be an array containing strings, symbols, or regular
# expressions. (Symbols are treated as strings). These are compared against
# the text of the generated deprecation warning.
#
# Additionally the scalar symbol +:all+ may be used to treat all
# deprecations as disallowed.
#
# Deprecations matching a substring or regular expression will be handled
# using the configured +ActiveSupport::Deprecation.disallowed_behavior+
# rather than +ActiveSupport::Deprecation.behavior+
attr_writer :disallowed_warnings
# Returns the configured criteria used to identify deprecation messages
# which should be treated as disallowed.
def disallowed_warnings
@disallowed_warnings ||= []
end
private
def deprecation_disallowed?(message)
disallowed = ActiveSupport::Deprecation.disallowed_warnings
return false if explicitly_allowed?(message)
return true if disallowed == :all
disallowed.any? do |rule|
case rule
when String, Symbol
message.include?(rule.to_s)
when Regexp
rule.match?(message)
end
end
end
def explicitly_allowed?(message)
allowances = @explicitly_allowed_warnings.value
return false unless allowances
return true if allowances == :all
allowances = [allowances] unless allowances.kind_of?(Array)
allowances.any? do |rule|
case rule
when String, Symbol
message.include?(rule.to_s)
when Regexp
rule.match?(message)
end
end
end
end
end
end

View File

@ -1,38 +0,0 @@
# frozen_string_literal: true
require "active_support/core_ext/module/delegation"
module ActiveSupport
class Deprecation
module InstanceDelegator # :nodoc:
def self.included(base)
base.extend(ClassMethods)
base.singleton_class.prepend(OverrideDelegators)
base.public_class_method :new
end
module ClassMethods # :nodoc:
def include(included_module)
included_module.instance_methods.each { |m| method_added(m) }
super
end
def method_added(method_name)
singleton_class.delegate(method_name, to: :instance)
end
end
module OverrideDelegators # :nodoc:
def warn(message = nil, callstack = nil)
callstack ||= caller_locations(2)
super
end
def deprecation_warning(deprecated_method_name, message = nil, caller_backtrace = nil)
caller_backtrace ||= caller_locations(2)
super
end
end
end
end
end

Some files were not shown because too many files have changed in this diff Show More