Fix broken comments indentation caused by rubocop auto-correct [ci skip]
All indentation was normalized by rubocop auto-correct at 80e66cc4d90bf8c15d1a5f6e3152e90147f00772. But comments was still kept absolute position. This commit aligns comments with method definitions for consistency.
This commit is contained in:
parent
92703a9ea5
commit
3464cd5c28
@ -20,6 +20,10 @@ Style/BracesAroundHashParameters:
|
||||
Style/CaseIndentation:
|
||||
Enabled: true
|
||||
|
||||
# Align comments with method definitions.
|
||||
Style/CommentIndentation:
|
||||
Enabled: true
|
||||
|
||||
# No extra empty lines.
|
||||
Style/EmptyLines:
|
||||
Enabled: true
|
||||
|
@ -832,15 +832,15 @@ def mail(headers = {}, &block)
|
||||
|
||||
protected
|
||||
|
||||
# Used by #mail to set the content type of the message.
|
||||
#
|
||||
# It will use the given +user_content_type+, or multipart if the mail
|
||||
# message has any attachments. If the attachments are inline, the content
|
||||
# type will be "multipart/related", otherwise "multipart/mixed".
|
||||
#
|
||||
# If there is no content type passed in via headers, and there are no
|
||||
# attachments, or the message is multipart, then the default content type is
|
||||
# used.
|
||||
# Used by #mail to set the content type of the message.
|
||||
#
|
||||
# It will use the given +user_content_type+, or multipart if the mail
|
||||
# message has any attachments. If the attachments are inline, the content
|
||||
# type will be "multipart/related", otherwise "multipart/mixed".
|
||||
#
|
||||
# If there is no content type passed in via headers, and there are no
|
||||
# attachments, or the message is multipart, then the default content type is
|
||||
# used.
|
||||
def set_content_type(m, user_content_type, class_default)
|
||||
params = m.content_type_parameters || {}
|
||||
case
|
||||
@ -859,16 +859,16 @@ def set_content_type(m, user_content_type, class_default)
|
||||
end
|
||||
end
|
||||
|
||||
# Translates the +subject+ using Rails I18n class under <tt>[mailer_scope, action_name]</tt> scope.
|
||||
# If it does not find a translation for the +subject+ under the specified scope it will default to a
|
||||
# humanized version of the <tt>action_name</tt>.
|
||||
# If the subject has interpolations, you can pass them through the +interpolations+ parameter.
|
||||
# Translates the +subject+ using Rails I18n class under <tt>[mailer_scope, action_name]</tt> scope.
|
||||
# If it does not find a translation for the +subject+ under the specified scope it will default to a
|
||||
# humanized version of the <tt>action_name</tt>.
|
||||
# If the subject has interpolations, you can pass them through the +interpolations+ parameter.
|
||||
def default_i18n_subject(interpolations = {})
|
||||
mailer_scope = self.class.mailer_name.tr("/", ".")
|
||||
I18n.t(:subject, interpolations.merge(scope: [mailer_scope, action_name], default: action_name.humanize))
|
||||
end
|
||||
|
||||
# Emails do not support relative path links.
|
||||
# Emails do not support relative path links.
|
||||
def self.supports_path?
|
||||
false
|
||||
end
|
||||
@ -950,7 +950,7 @@ def insert_part(container, response, charset)
|
||||
container.add_part(part)
|
||||
end
|
||||
|
||||
# This and #instrument_name is for caching instrument
|
||||
# This and #instrument_name is for caching instrument
|
||||
def instrument_payload(key)
|
||||
{
|
||||
mailer: mailer_name,
|
||||
|
@ -171,12 +171,12 @@ def modules_for_helpers(args)
|
||||
end
|
||||
|
||||
private
|
||||
# Makes all the (instance) methods in the helper module available to templates
|
||||
# rendered through this controller.
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>module</tt> - The module to include into the current helper module
|
||||
# for the class
|
||||
# Makes all the (instance) methods in the helper module available to templates
|
||||
# rendered through this controller.
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>module</tt> - The module to include into the current helper module
|
||||
# for the class
|
||||
def add_template_helper(mod)
|
||||
_helpers.module_eval { include mod }
|
||||
end
|
||||
|
@ -39,10 +39,10 @@ def determine_template_etag(options)
|
||||
end
|
||||
end
|
||||
|
||||
# Pick the template digest to include in the ETag. If the +:template+ option
|
||||
# is present, use the named template. If +:template+ is nil or absent, use
|
||||
# the default controller/action template. If +:template+ is false, omit the
|
||||
# template digest from the ETag.
|
||||
# Pick the template digest to include in the ETag. If the +:template+ option
|
||||
# is present, use the named template. If +:template+ is nil or absent, use
|
||||
# the default controller/action template. If +:template+ is false, omit the
|
||||
# template digest from the ETag.
|
||||
def pick_template_for_etag(options)
|
||||
unless options[:template] == false
|
||||
options[:template] || "#{controller_path}/#{action_name}"
|
||||
|
@ -108,7 +108,7 @@ def all_helpers_from_path(path)
|
||||
end
|
||||
|
||||
private
|
||||
# Extract helper names from files in <tt>app/helpers/**/*_helper.rb</tt>
|
||||
# Extract helper names from files in <tt>app/helpers/**/*_helper.rb</tt>
|
||||
def all_application_helpers
|
||||
all_helpers_from_path(helpers_path)
|
||||
end
|
||||
|
@ -128,13 +128,13 @@ def name
|
||||
end
|
||||
|
||||
private
|
||||
# Determine the wrapper model from the controller's name. By convention,
|
||||
# this could be done by trying to find the defined model that has the
|
||||
# same singular name as the controller. For example, +UsersController+
|
||||
# will try to find if the +User+ model exists.
|
||||
#
|
||||
# This method also does namespace lookup. Foo::Bar::UsersController will
|
||||
# try to find Foo::Bar::User, Foo::User and finally User.
|
||||
# Determine the wrapper model from the controller's name. By convention,
|
||||
# this could be done by trying to find the defined model that has the
|
||||
# same singular name as the controller. For example, +UsersController+
|
||||
# will try to find if the +User+ model exists.
|
||||
#
|
||||
# This method also does namespace lookup. Foo::Bar::UsersController will
|
||||
# try to find Foo::Bar::User, Foo::User and finally User.
|
||||
def _default_wrap_model #:nodoc:
|
||||
return nil if klass.anonymous?
|
||||
model_name = klass.name.sub(/Controller$/, "").classify
|
||||
|
@ -72,14 +72,14 @@ def _set_rendered_content_type(format)
|
||||
end
|
||||
end
|
||||
|
||||
# Normalize arguments by catching blocks and setting them on :update.
|
||||
# Normalize arguments by catching blocks and setting them on :update.
|
||||
def _normalize_args(action=nil, options={}, &blk) #:nodoc:
|
||||
options = super
|
||||
options[:update] = blk if block_given?
|
||||
options
|
||||
end
|
||||
|
||||
# Normalize both text and status options.
|
||||
# Normalize both text and status options.
|
||||
def _normalize_options(options) #:nodoc:
|
||||
_normalize_text(options)
|
||||
|
||||
@ -118,7 +118,7 @@ def _normalize_text(options)
|
||||
end
|
||||
end
|
||||
|
||||
# Process controller specific options, as status, content-type and location.
|
||||
# Process controller specific options, as status, content-type and location.
|
||||
def _process_options(options) #:nodoc:
|
||||
status, content_type, location = options.values_at(:status, :content_type, :location)
|
||||
|
||||
|
@ -115,8 +115,8 @@ def env; @req.env.dup; end
|
||||
|
||||
private
|
||||
|
||||
# Converts an HTTP header name to an environment variable name if it is
|
||||
# not contained within the headers hash.
|
||||
# Converts an HTTP header name to an environment variable name if it is
|
||||
# not contained within the headers hash.
|
||||
def env_name(key)
|
||||
key = key.to_s
|
||||
if key =~ HTTP_HEADER
|
||||
|
@ -10,7 +10,7 @@
|
||||
module ActionDispatch
|
||||
module Journey
|
||||
class Parser < Racc::Parser
|
||||
##### State transition tables begin ###
|
||||
##### State transition tables begin ###
|
||||
|
||||
racc_action_table = [
|
||||
13, 15, 14, 7, 21, 16, 8, 19, 13, 15,
|
||||
@ -128,9 +128,9 @@ class Parser < Racc::Parser
|
||||
|
||||
Racc_debug_parser = false
|
||||
|
||||
##### State transition tables end #####
|
||||
##### State transition tables end #####
|
||||
|
||||
# reduce 0 omitted
|
||||
# reduce 0 omitted
|
||||
|
||||
def _reduce_1(val, _values)
|
||||
Cat.new(val.first, val.last)
|
||||
@ -140,13 +140,13 @@ def _reduce_2(val, _values)
|
||||
val.first
|
||||
end
|
||||
|
||||
# reduce 3 omitted
|
||||
# reduce 3 omitted
|
||||
|
||||
# reduce 4 omitted
|
||||
# reduce 4 omitted
|
||||
|
||||
# reduce 5 omitted
|
||||
# reduce 5 omitted
|
||||
|
||||
# reduce 6 omitted
|
||||
# reduce 6 omitted
|
||||
|
||||
def _reduce_7(val, _values)
|
||||
Group.new(val[1])
|
||||
@ -164,13 +164,13 @@ def _reduce_10(val, _values)
|
||||
Star.new(Symbol.new(val.last))
|
||||
end
|
||||
|
||||
# reduce 11 omitted
|
||||
# reduce 11 omitted
|
||||
|
||||
# reduce 12 omitted
|
||||
# reduce 12 omitted
|
||||
|
||||
# reduce 13 omitted
|
||||
# reduce 13 omitted
|
||||
|
||||
# reduce 14 omitted
|
||||
# reduce 14 omitted
|
||||
|
||||
def _reduce_15(val, _values)
|
||||
Slash.new("/")
|
||||
|
@ -264,19 +264,19 @@ def handle_positional_args(controller_options, inner_options, args, result, path
|
||||
end
|
||||
|
||||
private
|
||||
# Create a url helper allowing ordered parameters to be associated
|
||||
# with corresponding dynamic segments, so you can do:
|
||||
#
|
||||
# foo_url(bar, baz, bang)
|
||||
#
|
||||
# Instead of:
|
||||
#
|
||||
# foo_url(bar: bar, baz: baz, bang: bang)
|
||||
#
|
||||
# Also allow options hash, so you can do:
|
||||
#
|
||||
# foo_url(bar, baz, bang, sort_by: 'baz')
|
||||
#
|
||||
# Create a url helper allowing ordered parameters to be associated
|
||||
# with corresponding dynamic segments, so you can do:
|
||||
#
|
||||
# foo_url(bar, baz, bang)
|
||||
#
|
||||
# Instead of:
|
||||
#
|
||||
# foo_url(bar: bar, baz: baz, bang: bang)
|
||||
#
|
||||
# Also allow options hash, so you can do:
|
||||
#
|
||||
# foo_url(bar, baz, bang, sort_by: 'baz')
|
||||
#
|
||||
def define_url_helper(mod, route, name, opts, route_key, url_strategy)
|
||||
helper = UrlHelper.create(route, opts, route_key, url_strategy)
|
||||
mod.module_eval do
|
||||
|
@ -246,7 +246,7 @@ module ActionDispatch
|
||||
class DebugExceptions
|
||||
private
|
||||
remove_method :stderr_logger
|
||||
# Silence logger
|
||||
# Silence logger
|
||||
def stderr_logger
|
||||
nil
|
||||
end
|
||||
|
@ -28,7 +28,7 @@ def with_layout
|
||||
|
||||
protected
|
||||
|
||||
# 3) Set view_context to self
|
||||
# 3) Set view_context to self
|
||||
def view_context
|
||||
self
|
||||
end
|
||||
|
@ -41,8 +41,8 @@ def instantiate_builder(builder_class, item, value, text, html_options)
|
||||
sanitize_attribute_name(value), text, value, html_options)
|
||||
end
|
||||
|
||||
# Generate default options for collection helpers, such as :checked and
|
||||
# :disabled.
|
||||
# Generate default options for collection helpers, such as :checked and
|
||||
# :disabled.
|
||||
def default_html_options_for_collection(item, value) #:nodoc:
|
||||
html_options = @html_options.dup
|
||||
|
||||
|
@ -28,10 +28,10 @@ def render
|
||||
|
||||
private
|
||||
|
||||
# Grouped choices look like this:
|
||||
#
|
||||
# [nil, []]
|
||||
# { nil => [] }
|
||||
# Grouped choices look like this:
|
||||
#
|
||||
# [nil, []]
|
||||
# { nil => [] }
|
||||
def grouped_choices?
|
||||
!@choices.empty? && @choices.first.respond_to?(:last) && Array === @choices.first.last
|
||||
end
|
||||
|
@ -224,12 +224,12 @@ def inherited(klass) # :nodoc:
|
||||
module LayoutConditions # :nodoc:
|
||||
private
|
||||
|
||||
# Determines whether the current action has a layout definition by
|
||||
# checking the action name against the :only and :except conditions
|
||||
# set by the <tt>layout</tt> method.
|
||||
#
|
||||
# ==== Returns
|
||||
# * <tt>Boolean</tt> - True if the action has a layout definition, false otherwise.
|
||||
# Determines whether the current action has a layout definition by
|
||||
# checking the action name against the :only and :except conditions
|
||||
# set by the <tt>layout</tt> method.
|
||||
#
|
||||
# ==== Returns
|
||||
# * <tt>Boolean</tt> - True if the action has a layout definition, false otherwise.
|
||||
def _conditional_layout?
|
||||
return unless super
|
||||
|
||||
@ -334,11 +334,11 @@ def _layout(formats)
|
||||
|
||||
private
|
||||
|
||||
# If no layout is supplied, look for a template named the return
|
||||
# value of this method.
|
||||
#
|
||||
# ==== Returns
|
||||
# * <tt>String</tt> - A template name
|
||||
# If no layout is supplied, look for a template named the return
|
||||
# value of this method.
|
||||
#
|
||||
# ==== Returns
|
||||
# * <tt>String</tt> - A template name
|
||||
def _implied_layout_name # :nodoc:
|
||||
controller_path
|
||||
end
|
||||
|
@ -350,13 +350,13 @@ def render_partial
|
||||
end
|
||||
end
|
||||
|
||||
# Sets up instance variables needed for rendering a partial. This method
|
||||
# finds the options and details and extracts them. The method also contains
|
||||
# logic that handles the type of object passed in as the partial.
|
||||
#
|
||||
# If +options[:partial]+ is a string, then the +@path+ instance variable is
|
||||
# set to that string. Otherwise, the +options[:partial]+ object must
|
||||
# respond to +to_partial_path+ in order to setup the path.
|
||||
# Sets up instance variables needed for rendering a partial. This method
|
||||
# finds the options and details and extracts them. The method also contains
|
||||
# logic that handles the type of object passed in as the partial.
|
||||
#
|
||||
# If +options[:partial]+ is a string, then the +@path+ instance variable is
|
||||
# set to that string. Otherwise, the +options[:partial]+ object must
|
||||
# respond to +to_partial_path+ in order to setup the path.
|
||||
def setup(context, options, block)
|
||||
@view = context
|
||||
@options = options
|
||||
@ -466,13 +466,13 @@ def collection_without_template
|
||||
end
|
||||
end
|
||||
|
||||
# Obtains the path to where the object's partial is located. If the object
|
||||
# responds to +to_partial_path+, then +to_partial_path+ will be called and
|
||||
# will provide the path. If the object does not respond to +to_partial_path+,
|
||||
# then an +ArgumentError+ is raised.
|
||||
#
|
||||
# If +prefix_partial_path_with_controller_namespace+ is true, then this
|
||||
# method will prefix the partial paths with a namespace.
|
||||
# Obtains the path to where the object's partial is located. If the object
|
||||
# responds to +to_partial_path+, then +to_partial_path+ will be called and
|
||||
# will provide the path. If the object does not respond to +to_partial_path+,
|
||||
# then an +ArgumentError+ is raised.
|
||||
#
|
||||
# If +prefix_partial_path_with_controller_namespace+ is true, then this
|
||||
# method will prefix the partial paths with a namespace.
|
||||
def partial_path(object = @object)
|
||||
object = object.to_model if object.respond_to?(:to_model)
|
||||
|
||||
|
@ -27,8 +27,8 @@ def each(&block)
|
||||
|
||||
private
|
||||
|
||||
# This is the same logging logic as in ShowExceptions middleware.
|
||||
# TODO Once "exceptron" is in, refactor this piece to simply re-use exceptron.
|
||||
# This is the same logging logic as in ShowExceptions middleware.
|
||||
# TODO Once "exceptron" is in, refactor this piece to simply re-use exceptron.
|
||||
def log_error(exception) #:nodoc:
|
||||
logger = ActionView::Base.logger
|
||||
return unless logger
|
||||
|
@ -16,7 +16,7 @@ def render(context, options)
|
||||
|
||||
private
|
||||
|
||||
# Determine the template to be rendered using the given options.
|
||||
# Determine the template to be rendered using the given options.
|
||||
def determine_template(options)
|
||||
keys = options.has_key?(:locals) ? options[:locals].keys : []
|
||||
|
||||
@ -44,8 +44,8 @@ def determine_template(options)
|
||||
end
|
||||
end
|
||||
|
||||
# Renders the given template. A string representing the layout can be
|
||||
# supplied as well.
|
||||
# Renders the given template. A string representing the layout can be
|
||||
# supplied as well.
|
||||
def render_template(template, layout_name = nil, locals = nil) #:nodoc:
|
||||
view, locals = @view, locals || {}
|
||||
|
||||
@ -69,9 +69,9 @@ def render_with_layout(path, locals) #:nodoc:
|
||||
end
|
||||
end
|
||||
|
||||
# This is the method which actually finds the layout using details in the lookup
|
||||
# context object. If no layout is found, it checks if at least a layout with
|
||||
# the given name exists across all details before raising the error.
|
||||
# This is the method which actually finds the layout using details in the lookup
|
||||
# context object. If no layout is found, it checks if at least a layout with
|
||||
# the given name exists across all details before raising the error.
|
||||
def find_layout(layout, keys, formats)
|
||||
resolve_layout(layout, keys, formats)
|
||||
end
|
||||
|
@ -256,7 +256,7 @@ def inside_path?(path, filename)
|
||||
filename.start_with?(path)
|
||||
end
|
||||
|
||||
# Helper for building query glob string based on resolver's pattern.
|
||||
# Helper for building query glob string based on resolver's pattern.
|
||||
def build_query(path, details)
|
||||
query = @pattern.dup
|
||||
|
||||
@ -281,14 +281,14 @@ def escape_entry(entry)
|
||||
entry.gsub(/[*?{}\[\]]/, '\\\\\\&'.freeze)
|
||||
end
|
||||
|
||||
# Returns the file mtime from the filesystem.
|
||||
# Returns the file mtime from the filesystem.
|
||||
def mtime(p)
|
||||
File.mtime(p)
|
||||
end
|
||||
|
||||
# Extract handler, formats and variant from path. If a format cannot be found neither
|
||||
# from the path, or the handler, we should return the array of formats given
|
||||
# to the resolver.
|
||||
# Extract handler, formats and variant from path. If a format cannot be found neither
|
||||
# from the path, or the handler, we should return the array of formats given
|
||||
# to the resolver.
|
||||
def extract_handler_and_format_and_variant(path, default_formats)
|
||||
pieces = File.basename(path).split(".".freeze)
|
||||
pieces.shift
|
||||
|
@ -22,8 +22,8 @@ def _prefixes # :nodoc:
|
||||
|
||||
private
|
||||
|
||||
# Override this method in your controller if you want to change paths prefixes for finding views.
|
||||
# Prefixes defined here will still be added to parents' <tt>._prefixes</tt>.
|
||||
# Override this method in your controller if you want to change paths prefixes for finding views.
|
||||
# Prefixes defined here will still be added to parents' <tt>._prefixes</tt>.
|
||||
def local_prefixes
|
||||
[controller_path]
|
||||
end
|
||||
|
@ -264,7 +264,7 @@ module ActionDispatch
|
||||
class DebugExceptions
|
||||
private
|
||||
remove_method :stderr_logger
|
||||
# Silence logger
|
||||
# Silence logger
|
||||
def stderr_logger
|
||||
nil
|
||||
end
|
||||
|
@ -227,7 +227,7 @@ def index() self.response_body = "success" end
|
||||
end
|
||||
|
||||
class ActionMissingRespondToActionController < AbstractController::Base
|
||||
# No actions
|
||||
# No actions
|
||||
private
|
||||
def action_missing(action_name)
|
||||
self.response_body = "success"
|
||||
|
@ -598,7 +598,7 @@ def test_with_array_containing_single_name_irregular_plural
|
||||
end
|
||||
end
|
||||
|
||||
# Tests for uncountable names
|
||||
# Tests for uncountable names
|
||||
def test_uncountable_resource
|
||||
with_test_routes do
|
||||
@series.save
|
||||
|
@ -19,8 +19,8 @@ def cast_value(value)
|
||||
fast_string_to_time(value) || fallback_string_to_time(value)
|
||||
end
|
||||
|
||||
# '0.123456' -> 123456
|
||||
# '1.123456' -> 123456
|
||||
# '0.123456' -> 123456
|
||||
# '1.123456' -> 123456
|
||||
def microseconds(time)
|
||||
time[:sec_fraction] ? (time[:sec_fraction] * 1_000_000).to_i : 0
|
||||
end
|
||||
|
@ -64,7 +64,7 @@ def new_time(year, mon, mday, hour, min, sec, microsec, offset = nil)
|
||||
|
||||
ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
|
||||
|
||||
# Doesn't handle time zones.
|
||||
# Doesn't handle time zones.
|
||||
def fast_string_to_time(string)
|
||||
if string =~ ISO_DATETIME
|
||||
microsec = ($7.to_r * 1_000_000).to_i
|
||||
|
@ -105,9 +105,9 @@ def assert_valid_value(*)
|
||||
|
||||
private
|
||||
|
||||
# Convenience method for types which do not need separate type casting
|
||||
# behavior for user and database inputs. Called by Value#cast for
|
||||
# values except +nil+.
|
||||
# Convenience method for types which do not need separate type casting
|
||||
# behavior for user and database inputs. Called by Value#cast for
|
||||
# values except +nil+.
|
||||
def cast_value(value) # :doc:
|
||||
value
|
||||
end
|
||||
|
@ -46,7 +46,7 @@ def test_errors_full_messages_uses_format
|
||||
# are used to generate tests to keep things DRY
|
||||
#
|
||||
COMMON_CASES = [
|
||||
# [ case, validation_options, generate_message_options]
|
||||
# [ case, validation_options, generate_message_options]
|
||||
[ "given no options", {}, {}],
|
||||
[ "given custom message", { message: "custom" }, { message: "custom" }],
|
||||
[ "given if condition", { if: lambda { true } }, {}],
|
||||
|
@ -24,161 +24,161 @@ def init_internals # :nodoc:
|
||||
super
|
||||
end
|
||||
|
||||
# Active Record implements aggregation through a macro-like class method called #composed_of
|
||||
# for representing attributes as value objects. It expresses relationships like "Account [is]
|
||||
# composed of Money [among other things]" or "Person [is] composed of [an] address". Each call
|
||||
# to the macro adds a description of how the value objects are created from the attributes of
|
||||
# the entity object (when the entity is initialized either as a new object or from finding an
|
||||
# existing object) and how it can be turned back into attributes (when the entity is saved to
|
||||
# the database).
|
||||
#
|
||||
# class Customer < ActiveRecord::Base
|
||||
# composed_of :balance, class_name: "Money", mapping: %w(amount currency)
|
||||
# composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
|
||||
# end
|
||||
#
|
||||
# The customer class now has the following methods to manipulate the value objects:
|
||||
# * <tt>Customer#balance, Customer#balance=(money)</tt>
|
||||
# * <tt>Customer#address, Customer#address=(address)</tt>
|
||||
#
|
||||
# These methods will operate with value objects like the ones described below:
|
||||
#
|
||||
# class Money
|
||||
# include Comparable
|
||||
# attr_reader :amount, :currency
|
||||
# EXCHANGE_RATES = { "USD_TO_DKK" => 6 }
|
||||
#
|
||||
# def initialize(amount, currency = "USD")
|
||||
# @amount, @currency = amount, currency
|
||||
# end
|
||||
#
|
||||
# def exchange_to(other_currency)
|
||||
# exchanged_amount = (amount * EXCHANGE_RATES["#{currency}_TO_#{other_currency}"]).floor
|
||||
# Money.new(exchanged_amount, other_currency)
|
||||
# end
|
||||
#
|
||||
# def ==(other_money)
|
||||
# amount == other_money.amount && currency == other_money.currency
|
||||
# end
|
||||
#
|
||||
# def <=>(other_money)
|
||||
# if currency == other_money.currency
|
||||
# amount <=> other_money.amount
|
||||
# else
|
||||
# amount <=> other_money.exchange_to(currency).amount
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# class Address
|
||||
# attr_reader :street, :city
|
||||
# def initialize(street, city)
|
||||
# @street, @city = street, city
|
||||
# end
|
||||
#
|
||||
# def close_to?(other_address)
|
||||
# city == other_address.city
|
||||
# end
|
||||
#
|
||||
# def ==(other_address)
|
||||
# city == other_address.city && street == other_address.street
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# Now it's possible to access attributes from the database through the value objects instead. If
|
||||
# you choose to name the composition the same as the attribute's name, it will be the only way to
|
||||
# access that attribute. That's the case with our +balance+ attribute. You interact with the value
|
||||
# objects just like you would with any other attribute:
|
||||
#
|
||||
# customer.balance = Money.new(20) # sets the Money value object and the attribute
|
||||
# customer.balance # => Money value object
|
||||
# customer.balance.exchange_to("DKK") # => Money.new(120, "DKK")
|
||||
# customer.balance > Money.new(10) # => true
|
||||
# customer.balance == Money.new(20) # => true
|
||||
# customer.balance < Money.new(5) # => false
|
||||
#
|
||||
# Value objects can also be composed of multiple attributes, such as the case of Address. The order
|
||||
# of the mappings will determine the order of the parameters.
|
||||
#
|
||||
# customer.address_street = "Hyancintvej"
|
||||
# customer.address_city = "Copenhagen"
|
||||
# customer.address # => Address.new("Hyancintvej", "Copenhagen")
|
||||
#
|
||||
# customer.address = Address.new("May Street", "Chicago")
|
||||
# customer.address_street # => "May Street"
|
||||
# customer.address_city # => "Chicago"
|
||||
#
|
||||
# == Writing value objects
|
||||
#
|
||||
# Value objects are immutable and interchangeable objects that represent a given value, such as
|
||||
# a Money object representing $5. Two Money objects both representing $5 should be equal (through
|
||||
# methods such as <tt>==</tt> and <tt><=></tt> from Comparable if ranking makes sense). This is
|
||||
# unlike entity objects where equality is determined by identity. An entity class such as Customer can
|
||||
# easily have two different objects that both have an address on Hyancintvej. Entity identity is
|
||||
# determined by object or relational unique identifiers (such as primary keys). Normal
|
||||
# ActiveRecord::Base classes are entity objects.
|
||||
#
|
||||
# It's also important to treat the value objects as immutable. Don't allow the Money object to have
|
||||
# its amount changed after creation. Create a new Money object with the new value instead. The
|
||||
# <tt>Money#exchange_to</tt> method is an example of this. It returns a new value object instead of changing
|
||||
# its own values. Active Record won't persist value objects that have been changed through means
|
||||
# other than the writer method.
|
||||
#
|
||||
# The immutable requirement is enforced by Active Record by freezing any object assigned as a value
|
||||
# object. Attempting to change it afterwards will result in a +RuntimeError+.
|
||||
#
|
||||
# Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not
|
||||
# keeping value objects immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable
|
||||
#
|
||||
# == Custom constructors and converters
|
||||
#
|
||||
# By default value objects are initialized by calling the <tt>new</tt> constructor of the value
|
||||
# class passing each of the mapped attributes, in the order specified by the <tt>:mapping</tt>
|
||||
# option, as arguments. If the value class doesn't support this convention then #composed_of allows
|
||||
# a custom constructor to be specified.
|
||||
#
|
||||
# When a new value is assigned to the value object, the default assumption is that the new value
|
||||
# is an instance of the value class. Specifying a custom converter allows the new value to be automatically
|
||||
# converted to an instance of value class if necessary.
|
||||
#
|
||||
# For example, the +NetworkResource+ model has +network_address+ and +cidr_range+ attributes that should be
|
||||
# aggregated using the +NetAddr::CIDR+ value class (http://www.rubydoc.info/gems/netaddr/1.5.0/NetAddr/CIDR).
|
||||
# The constructor for the value class is called +create+ and it expects a CIDR address string as a parameter.
|
||||
# New values can be assigned to the value object using either another +NetAddr::CIDR+ object, a string
|
||||
# or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to meet
|
||||
# these requirements:
|
||||
#
|
||||
# class NetworkResource < ActiveRecord::Base
|
||||
# composed_of :cidr,
|
||||
# class_name: 'NetAddr::CIDR',
|
||||
# mapping: [ %w(network_address network), %w(cidr_range bits) ],
|
||||
# allow_nil: true,
|
||||
# constructor: Proc.new { |network_address, cidr_range| NetAddr::CIDR.create("#{network_address}/#{cidr_range}") },
|
||||
# converter: Proc.new { |value| NetAddr::CIDR.create(value.is_a?(Array) ? value.join('/') : value) }
|
||||
# end
|
||||
#
|
||||
# # This calls the :constructor
|
||||
# network_resource = NetworkResource.new(network_address: '192.168.0.1', cidr_range: 24)
|
||||
#
|
||||
# # These assignments will both use the :converter
|
||||
# network_resource.cidr = [ '192.168.2.1', 8 ]
|
||||
# network_resource.cidr = '192.168.0.1/24'
|
||||
#
|
||||
# # This assignment won't use the :converter as the value is already an instance of the value class
|
||||
# network_resource.cidr = NetAddr::CIDR.create('192.168.2.1/8')
|
||||
#
|
||||
# # Saving and then reloading will use the :constructor on reload
|
||||
# network_resource.save
|
||||
# network_resource.reload
|
||||
#
|
||||
# == Finding records by a value object
|
||||
#
|
||||
# Once a #composed_of relationship is specified for a model, records can be loaded from the database
|
||||
# by specifying an instance of the value object in the conditions hash. The following example
|
||||
# finds all customers with +balance_amount+ equal to 20 and +balance_currency+ equal to "USD":
|
||||
#
|
||||
# Customer.where(balance: Money.new(20, "USD"))
|
||||
#
|
||||
# Active Record implements aggregation through a macro-like class method called #composed_of
|
||||
# for representing attributes as value objects. It expresses relationships like "Account [is]
|
||||
# composed of Money [among other things]" or "Person [is] composed of [an] address". Each call
|
||||
# to the macro adds a description of how the value objects are created from the attributes of
|
||||
# the entity object (when the entity is initialized either as a new object or from finding an
|
||||
# existing object) and how it can be turned back into attributes (when the entity is saved to
|
||||
# the database).
|
||||
#
|
||||
# class Customer < ActiveRecord::Base
|
||||
# composed_of :balance, class_name: "Money", mapping: %w(amount currency)
|
||||
# composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
|
||||
# end
|
||||
#
|
||||
# The customer class now has the following methods to manipulate the value objects:
|
||||
# * <tt>Customer#balance, Customer#balance=(money)</tt>
|
||||
# * <tt>Customer#address, Customer#address=(address)</tt>
|
||||
#
|
||||
# These methods will operate with value objects like the ones described below:
|
||||
#
|
||||
# class Money
|
||||
# include Comparable
|
||||
# attr_reader :amount, :currency
|
||||
# EXCHANGE_RATES = { "USD_TO_DKK" => 6 }
|
||||
#
|
||||
# def initialize(amount, currency = "USD")
|
||||
# @amount, @currency = amount, currency
|
||||
# end
|
||||
#
|
||||
# def exchange_to(other_currency)
|
||||
# exchanged_amount = (amount * EXCHANGE_RATES["#{currency}_TO_#{other_currency}"]).floor
|
||||
# Money.new(exchanged_amount, other_currency)
|
||||
# end
|
||||
#
|
||||
# def ==(other_money)
|
||||
# amount == other_money.amount && currency == other_money.currency
|
||||
# end
|
||||
#
|
||||
# def <=>(other_money)
|
||||
# if currency == other_money.currency
|
||||
# amount <=> other_money.amount
|
||||
# else
|
||||
# amount <=> other_money.exchange_to(currency).amount
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# class Address
|
||||
# attr_reader :street, :city
|
||||
# def initialize(street, city)
|
||||
# @street, @city = street, city
|
||||
# end
|
||||
#
|
||||
# def close_to?(other_address)
|
||||
# city == other_address.city
|
||||
# end
|
||||
#
|
||||
# def ==(other_address)
|
||||
# city == other_address.city && street == other_address.street
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# Now it's possible to access attributes from the database through the value objects instead. If
|
||||
# you choose to name the composition the same as the attribute's name, it will be the only way to
|
||||
# access that attribute. That's the case with our +balance+ attribute. You interact with the value
|
||||
# objects just like you would with any other attribute:
|
||||
#
|
||||
# customer.balance = Money.new(20) # sets the Money value object and the attribute
|
||||
# customer.balance # => Money value object
|
||||
# customer.balance.exchange_to("DKK") # => Money.new(120, "DKK")
|
||||
# customer.balance > Money.new(10) # => true
|
||||
# customer.balance == Money.new(20) # => true
|
||||
# customer.balance < Money.new(5) # => false
|
||||
#
|
||||
# Value objects can also be composed of multiple attributes, such as the case of Address. The order
|
||||
# of the mappings will determine the order of the parameters.
|
||||
#
|
||||
# customer.address_street = "Hyancintvej"
|
||||
# customer.address_city = "Copenhagen"
|
||||
# customer.address # => Address.new("Hyancintvej", "Copenhagen")
|
||||
#
|
||||
# customer.address = Address.new("May Street", "Chicago")
|
||||
# customer.address_street # => "May Street"
|
||||
# customer.address_city # => "Chicago"
|
||||
#
|
||||
# == Writing value objects
|
||||
#
|
||||
# Value objects are immutable and interchangeable objects that represent a given value, such as
|
||||
# a Money object representing $5. Two Money objects both representing $5 should be equal (through
|
||||
# methods such as <tt>==</tt> and <tt><=></tt> from Comparable if ranking makes sense). This is
|
||||
# unlike entity objects where equality is determined by identity. An entity class such as Customer can
|
||||
# easily have two different objects that both have an address on Hyancintvej. Entity identity is
|
||||
# determined by object or relational unique identifiers (such as primary keys). Normal
|
||||
# ActiveRecord::Base classes are entity objects.
|
||||
#
|
||||
# It's also important to treat the value objects as immutable. Don't allow the Money object to have
|
||||
# its amount changed after creation. Create a new Money object with the new value instead. The
|
||||
# <tt>Money#exchange_to</tt> method is an example of this. It returns a new value object instead of changing
|
||||
# its own values. Active Record won't persist value objects that have been changed through means
|
||||
# other than the writer method.
|
||||
#
|
||||
# The immutable requirement is enforced by Active Record by freezing any object assigned as a value
|
||||
# object. Attempting to change it afterwards will result in a +RuntimeError+.
|
||||
#
|
||||
# Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not
|
||||
# keeping value objects immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable
|
||||
#
|
||||
# == Custom constructors and converters
|
||||
#
|
||||
# By default value objects are initialized by calling the <tt>new</tt> constructor of the value
|
||||
# class passing each of the mapped attributes, in the order specified by the <tt>:mapping</tt>
|
||||
# option, as arguments. If the value class doesn't support this convention then #composed_of allows
|
||||
# a custom constructor to be specified.
|
||||
#
|
||||
# When a new value is assigned to the value object, the default assumption is that the new value
|
||||
# is an instance of the value class. Specifying a custom converter allows the new value to be automatically
|
||||
# converted to an instance of value class if necessary.
|
||||
#
|
||||
# For example, the +NetworkResource+ model has +network_address+ and +cidr_range+ attributes that should be
|
||||
# aggregated using the +NetAddr::CIDR+ value class (http://www.rubydoc.info/gems/netaddr/1.5.0/NetAddr/CIDR).
|
||||
# The constructor for the value class is called +create+ and it expects a CIDR address string as a parameter.
|
||||
# New values can be assigned to the value object using either another +NetAddr::CIDR+ object, a string
|
||||
# or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to meet
|
||||
# these requirements:
|
||||
#
|
||||
# class NetworkResource < ActiveRecord::Base
|
||||
# composed_of :cidr,
|
||||
# class_name: 'NetAddr::CIDR',
|
||||
# mapping: [ %w(network_address network), %w(cidr_range bits) ],
|
||||
# allow_nil: true,
|
||||
# constructor: Proc.new { |network_address, cidr_range| NetAddr::CIDR.create("#{network_address}/#{cidr_range}") },
|
||||
# converter: Proc.new { |value| NetAddr::CIDR.create(value.is_a?(Array) ? value.join('/') : value) }
|
||||
# end
|
||||
#
|
||||
# # This calls the :constructor
|
||||
# network_resource = NetworkResource.new(network_address: '192.168.0.1', cidr_range: 24)
|
||||
#
|
||||
# # These assignments will both use the :converter
|
||||
# network_resource.cidr = [ '192.168.2.1', 8 ]
|
||||
# network_resource.cidr = '192.168.0.1/24'
|
||||
#
|
||||
# # This assignment won't use the :converter as the value is already an instance of the value class
|
||||
# network_resource.cidr = NetAddr::CIDR.create('192.168.2.1/8')
|
||||
#
|
||||
# # Saving and then reloading will use the :constructor on reload
|
||||
# network_resource.save
|
||||
# network_resource.reload
|
||||
#
|
||||
# == Finding records by a value object
|
||||
#
|
||||
# Once a #composed_of relationship is specified for a model, records can be loaded from the database
|
||||
# by specifying an instance of the value object in the conditions hash. The following example
|
||||
# finds all customers with +balance_amount+ equal to 20 and +balance_currency+ equal to "USD":
|
||||
#
|
||||
# Customer.where(balance: Money.new(20, "USD"))
|
||||
#
|
||||
module ClassMethods
|
||||
# Adds reader and writer methods for manipulating a value object:
|
||||
# <tt>composed_of :address</tt> adds <tt>address</tt> and <tt>address=(new_address)</tt> methods.
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -106,7 +106,7 @@ def preload(records, associations, preload_scope = nil)
|
||||
|
||||
private
|
||||
|
||||
# Loads all the given data into +records+ for the +association+.
|
||||
# Loads all the given data into +records+ for the +association+.
|
||||
def preloaders_on(association, records, scope)
|
||||
case association
|
||||
when Hash
|
||||
@ -132,18 +132,18 @@ def preloaders_for_hash(association, records, scope)
|
||||
}
|
||||
end
|
||||
|
||||
# Loads all the given data into +records+ for a singular +association+.
|
||||
#
|
||||
# Functions by instantiating a preloader class such as Preloader::HasManyThrough and
|
||||
# call the +run+ method for each passed in class in the +records+ argument.
|
||||
#
|
||||
# Not all records have the same class, so group then preload group on the reflection
|
||||
# itself so that if various subclass share the same association then we do not split
|
||||
# them unnecessarily
|
||||
#
|
||||
# Additionally, polymorphic belongs_to associations can have multiple associated
|
||||
# classes, depending on the polymorphic_type field. So we group by the classes as
|
||||
# well.
|
||||
# Loads all the given data into +records+ for a singular +association+.
|
||||
#
|
||||
# Functions by instantiating a preloader class such as Preloader::HasManyThrough and
|
||||
# call the +run+ method for each passed in class in the +records+ argument.
|
||||
#
|
||||
# Not all records have the same class, so group then preload group on the reflection
|
||||
# itself so that if various subclass share the same association then we do not split
|
||||
# them unnecessarily
|
||||
#
|
||||
# Additionally, polymorphic belongs_to associations can have multiple associated
|
||||
# classes, depending on the polymorphic_type field. So we group by the classes as
|
||||
# well.
|
||||
def preloaders_for_one(association, records, scope)
|
||||
grouped_records(association, records).flat_map do |reflection, klasses|
|
||||
klasses.map do |rhs_klass, rs|
|
||||
@ -187,10 +187,10 @@ def self.preloaded_records; []; end
|
||||
def self.owners; []; end
|
||||
end
|
||||
|
||||
# Returns a class containing the logic needed to load preload the data
|
||||
# and attach it to a relation. For example +Preloader::Association+ or
|
||||
# +Preloader::HasManyThrough+. The class returned implements a `run` method
|
||||
# that accepts a preloader.
|
||||
# Returns a class containing the logic needed to load preload the data
|
||||
# and attach it to a relation. For example +Preloader::Association+ or
|
||||
# +Preloader::HasManyThrough+. The class returned implements a `run` method
|
||||
# that accepts a preloader.
|
||||
def preloader_for(reflection, owners, rhs_klass)
|
||||
return NullPreloader unless rhs_klass
|
||||
|
||||
|
@ -29,17 +29,17 @@ def _assign_attributes(attributes) # :nodoc:
|
||||
assign_multiparameter_attributes(multi_parameter_attributes) unless multi_parameter_attributes.empty?
|
||||
end
|
||||
|
||||
# Assign any deferred nested attributes after the base attributes have been set.
|
||||
# Assign any deferred nested attributes after the base attributes have been set.
|
||||
def assign_nested_parameter_attributes(pairs)
|
||||
pairs.each { |k, v| _assign_attribute(k, v) }
|
||||
end
|
||||
|
||||
# Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
|
||||
# by calling new on the column type or aggregation type (through composed_of) object with these parameters.
|
||||
# So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
|
||||
# written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
|
||||
# parentheses to have the parameters typecasted before they're used in the constructor. Use i for Integer and
|
||||
# f for Float. If all the values for a given attribute are empty, the attribute will be set to +nil+.
|
||||
# Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
|
||||
# by calling new on the column type or aggregation type (through composed_of) object with these parameters.
|
||||
# So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
|
||||
# written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
|
||||
# parentheses to have the parameters typecasted before they're used in the constructor. Use i for Integer and
|
||||
# f for Float. If all the values for a given attribute are empty, the attribute will be set to +nil+.
|
||||
def assign_multiparameter_attributes(pairs)
|
||||
execute_callstack_for_multiparameter_attributes(
|
||||
extract_callstack_for_multiparameter_attributes(pairs)
|
||||
|
@ -416,8 +416,8 @@ def attribute_method?(attr_name) # :nodoc:
|
||||
|
||||
private
|
||||
|
||||
# Returns a Hash of the Arel::Attributes and attribute values that have been
|
||||
# typecasted for use in an Arel insert/update method.
|
||||
# Returns a Hash of the Arel::Attributes and attribute values that have been
|
||||
# typecasted for use in an Arel insert/update method.
|
||||
def arel_attributes_with_values(attribute_names)
|
||||
attrs = {}
|
||||
arel_table = self.class.arel_table
|
||||
@ -428,15 +428,15 @@ def arel_attributes_with_values(attribute_names)
|
||||
attrs
|
||||
end
|
||||
|
||||
# Filters the primary keys and readonly attributes from the attribute names.
|
||||
# Filters the primary keys and readonly attributes from the attribute names.
|
||||
def attributes_for_update(attribute_names)
|
||||
attribute_names.reject do |name|
|
||||
readonly_attribute?(name)
|
||||
end
|
||||
end
|
||||
|
||||
# Filters out the primary keys, from the attribute names, when the primary
|
||||
# key is to be generated (e.g. the id attribute has no value).
|
||||
# Filters out the primary keys, from the attribute names, when the primary
|
||||
# key is to be generated (e.g. the id attribute has no value).
|
||||
def attributes_for_create(attribute_names)
|
||||
attribute_names.reject do |name|
|
||||
pk_attribute?(name) && id.nil?
|
||||
|
@ -63,7 +63,7 @@ def attributes_before_type_cast
|
||||
|
||||
private
|
||||
|
||||
# Handle *_before_type_cast for method_missing.
|
||||
# Handle *_before_type_cast for method_missing.
|
||||
def attribute_before_type_cast(attribute_name)
|
||||
read_attribute_before_type_cast(attribute_name)
|
||||
end
|
||||
|
@ -6,24 +6,24 @@ module Read
|
||||
module ClassMethods
|
||||
protected
|
||||
|
||||
# We want to generate the methods via module_eval rather than
|
||||
# define_method, because define_method is slower on dispatch.
|
||||
# Evaluating many similar methods may use more memory as the instruction
|
||||
# sequences are duplicated and cached (in MRI). define_method may
|
||||
# be slower on dispatch, but if you're careful about the closure
|
||||
# created, then define_method will consume much less memory.
|
||||
#
|
||||
# But sometimes the database might return columns with
|
||||
# characters that are not allowed in normal method names (like
|
||||
# 'my_column(omg)'. So to work around this we first define with
|
||||
# the __temp__ identifier, and then use alias method to rename
|
||||
# it to what we want.
|
||||
#
|
||||
# We are also defining a constant to hold the frozen string of
|
||||
# the attribute name. Using a constant means that we do not have
|
||||
# to allocate an object on each call to the attribute method.
|
||||
# Making it frozen means that it doesn't get duped when used to
|
||||
# key the @attributes in read_attribute.
|
||||
# We want to generate the methods via module_eval rather than
|
||||
# define_method, because define_method is slower on dispatch.
|
||||
# Evaluating many similar methods may use more memory as the instruction
|
||||
# sequences are duplicated and cached (in MRI). define_method may
|
||||
# be slower on dispatch, but if you're careful about the closure
|
||||
# created, then define_method will consume much less memory.
|
||||
#
|
||||
# But sometimes the database might return columns with
|
||||
# characters that are not allowed in normal method names (like
|
||||
# 'my_column(omg)'. So to work around this we first define with
|
||||
# the __temp__ identifier, and then use alias method to rename
|
||||
# it to what we want.
|
||||
#
|
||||
# We are also defining a constant to hold the frozen string of
|
||||
# the attribute name. Using a constant means that we do not have
|
||||
# to allocate an object on each call to the attribute method.
|
||||
# Making it frozen means that it doesn't get duped when used to
|
||||
# key the @attributes in read_attribute.
|
||||
def define_method_attribute(name)
|
||||
safe_name = name.unpack("h*".freeze).first
|
||||
temp_method = "__temp__#{safe_name}"
|
||||
|
@ -37,7 +37,7 @@ def raw_write_attribute(attr_name, value) # :nodoc:
|
||||
end
|
||||
|
||||
private
|
||||
# Handle *= for method_missing.
|
||||
# Handle *= for method_missing.
|
||||
def attribute=(attribute_name, value)
|
||||
write_attribute(attribute_name, value)
|
||||
end
|
||||
|
@ -158,33 +158,33 @@ def synchronize(&block)
|
||||
@lock.synchronize(&block)
|
||||
end
|
||||
|
||||
# Test if the queue currently contains any elements.
|
||||
# Test if the queue currently contains any elements.
|
||||
def any?
|
||||
!@queue.empty?
|
||||
end
|
||||
|
||||
# A thread can remove an element from the queue without
|
||||
# waiting if and only if the number of currently available
|
||||
# connections is strictly greater than the number of waiting
|
||||
# threads.
|
||||
# A thread can remove an element from the queue without
|
||||
# waiting if and only if the number of currently available
|
||||
# connections is strictly greater than the number of waiting
|
||||
# threads.
|
||||
def can_remove_no_wait?
|
||||
@queue.size > @num_waiting
|
||||
end
|
||||
|
||||
# Removes and returns the head of the queue if possible, or nil.
|
||||
# Removes and returns the head of the queue if possible, or nil.
|
||||
def remove
|
||||
@queue.shift
|
||||
end
|
||||
|
||||
# Remove and return the head the queue if the number of
|
||||
# available elements is strictly greater than the number of
|
||||
# threads currently waiting. Otherwise, return nil.
|
||||
# Remove and return the head the queue if the number of
|
||||
# available elements is strictly greater than the number of
|
||||
# threads currently waiting. Otherwise, return nil.
|
||||
def no_wait_poll
|
||||
remove if can_remove_no_wait?
|
||||
end
|
||||
|
||||
# Waits on the queue up to +timeout+ seconds, then removes and
|
||||
# returns the head of the queue.
|
||||
# Waits on the queue up to +timeout+ seconds, then removes and
|
||||
# returns the head of the queue.
|
||||
def wait_poll(timeout)
|
||||
@num_waiting += 1
|
||||
|
||||
@ -582,8 +582,8 @@ def num_waiting_in_queue # :nodoc:
|
||||
end
|
||||
|
||||
private
|
||||
#--
|
||||
# this is unfortunately not concurrent
|
||||
#--
|
||||
# this is unfortunately not concurrent
|
||||
def bulk_make_new_connections(num_new_conns_needed)
|
||||
num_new_conns_needed.times do
|
||||
# try_to_checkout_new_connection will not exceed pool's @size limit
|
||||
@ -594,19 +594,19 @@ def bulk_make_new_connections(num_new_conns_needed)
|
||||
end
|
||||
end
|
||||
|
||||
#--
|
||||
# From the discussion on GitHub:
|
||||
# https://github.com/rails/rails/pull/14938#commitcomment-6601951
|
||||
# This hook-in method allows for easier monkey-patching fixes needed by
|
||||
# JRuby users that use Fibers.
|
||||
#--
|
||||
# From the discussion on GitHub:
|
||||
# https://github.com/rails/rails/pull/14938#commitcomment-6601951
|
||||
# This hook-in method allows for easier monkey-patching fixes needed by
|
||||
# JRuby users that use Fibers.
|
||||
def connection_cache_key(thread)
|
||||
thread
|
||||
end
|
||||
|
||||
# Take control of all existing connections so a "group" action such as
|
||||
# reload/disconnect can be performed safely. It is no longer enough to
|
||||
# wrap it in +synchronize+ because some pool's actions are allowed
|
||||
# to be performed outside of the main +synchronize+ block.
|
||||
# Take control of all existing connections so a "group" action such as
|
||||
# reload/disconnect can be performed safely. It is no longer enough to
|
||||
# wrap it in +synchronize+ because some pool's actions are allowed
|
||||
# to be performed outside of the main +synchronize+ block.
|
||||
def with_exclusively_acquired_all_connections(raise_on_acquisition_timeout = true)
|
||||
with_new_connections_blocked do
|
||||
attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout)
|
||||
@ -658,8 +658,8 @@ def attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout =
|
||||
end
|
||||
end
|
||||
|
||||
#--
|
||||
# Must be called in a synchronize block.
|
||||
#--
|
||||
# Must be called in a synchronize block.
|
||||
def checkout_for_exclusive_access(checkout_timeout)
|
||||
checkout(checkout_timeout)
|
||||
rescue ConnectionTimeoutError
|
||||
@ -690,17 +690,17 @@ def with_new_connections_blocked
|
||||
synchronize { @new_cons_enabled = previous_value }
|
||||
end
|
||||
|
||||
# Acquire a connection by one of 1) immediately removing one
|
||||
# from the queue of available connections, 2) creating a new
|
||||
# connection if the pool is not at capacity, 3) waiting on the
|
||||
# queue for a connection to become available.
|
||||
#
|
||||
# Raises:
|
||||
# - ActiveRecord::ConnectionTimeoutError if a connection could not be acquired
|
||||
#
|
||||
#--
|
||||
# Implementation detail: the connection returned by +acquire_connection+
|
||||
# will already be "+connection.lease+ -ed" to the current thread.
|
||||
# Acquire a connection by one of 1) immediately removing one
|
||||
# from the queue of available connections, 2) creating a new
|
||||
# connection if the pool is not at capacity, 3) waiting on the
|
||||
# queue for a connection to become available.
|
||||
#
|
||||
# Raises:
|
||||
# - ActiveRecord::ConnectionTimeoutError if a connection could not be acquired
|
||||
#
|
||||
#--
|
||||
# Implementation detail: the connection returned by +acquire_connection+
|
||||
# will already be "+connection.lease+ -ed" to the current thread.
|
||||
def acquire_connection(checkout_timeout)
|
||||
# NOTE: we rely on +@available.poll+ and +try_to_checkout_new_connection+ to
|
||||
# +conn.lease+ the returned connection (and to do this in a +synchronized+
|
||||
@ -716,8 +716,8 @@ def acquire_connection(checkout_timeout)
|
||||
end
|
||||
end
|
||||
|
||||
#--
|
||||
# if owner_thread param is omitted, this must be called in synchronize block
|
||||
#--
|
||||
# if owner_thread param is omitted, this must be called in synchronize block
|
||||
def remove_connection_from_thread_cache(conn, owner_thread = conn.owner)
|
||||
@thread_cached_conns.delete_pair(connection_cache_key(owner_thread), conn)
|
||||
end
|
||||
@ -729,11 +729,11 @@ def new_connection
|
||||
end
|
||||
end
|
||||
|
||||
# If the pool is not at a +@size+ limit, establish new connection. Connecting
|
||||
# to the DB is done outside main synchronized section.
|
||||
#--
|
||||
# Implementation constraint: a newly established connection returned by this
|
||||
# method must be in the +.leased+ state.
|
||||
# If the pool is not at a +@size+ limit, establish new connection. Connecting
|
||||
# to the DB is done outside main synchronized section.
|
||||
#--
|
||||
# Implementation constraint: a newly established connection returned by this
|
||||
# method must be in the +.leased+ state.
|
||||
def try_to_checkout_new_connection
|
||||
# first in synchronized section check if establishing new conns is allowed
|
||||
# and increment @now_connecting, to prevent overstepping this pool's @size
|
||||
|
@ -85,8 +85,8 @@ def cache_sql(sql, binds)
|
||||
result.dup
|
||||
end
|
||||
|
||||
# If arel is locked this is a SELECT ... FOR UPDATE or somesuch. Such
|
||||
# queries should not be cached.
|
||||
# If arel is locked this is a SELECT ... FOR UPDATE or somesuch. Such
|
||||
# queries should not be cached.
|
||||
def locked?(arel)
|
||||
arel.respond_to?(:locked) && arel.locked
|
||||
end
|
||||
|
@ -818,8 +818,8 @@ def remove_timestamps_sql(table_name, options = {})
|
||||
|
||||
private
|
||||
|
||||
# MySQL is too stupid to create a temporary table for use subquery, so we have
|
||||
# to give it some prompting in the form of a subsubquery. Ugh!
|
||||
# MySQL is too stupid to create a temporary table for use subquery, so we have
|
||||
# to give it some prompting in the form of a subsubquery. Ugh!
|
||||
def subquery_for(key, select)
|
||||
subsubselect = select.clone
|
||||
subsubselect.projections = [key]
|
||||
|
@ -63,15 +63,15 @@ def uri_parser
|
||||
@uri_parser ||= URI::Parser.new
|
||||
end
|
||||
|
||||
# Converts the query parameters of the URI into a hash.
|
||||
#
|
||||
# "localhost?pool=5&reaping_frequency=2"
|
||||
# # => { "pool" => "5", "reaping_frequency" => "2" }
|
||||
#
|
||||
# returns empty hash if no query present.
|
||||
#
|
||||
# "localhost"
|
||||
# # => {}
|
||||
# Converts the query parameters of the URI into a hash.
|
||||
#
|
||||
# "localhost?pool=5&reaping_frequency=2"
|
||||
# # => { "pool" => "5", "reaping_frequency" => "2" }
|
||||
#
|
||||
# returns empty hash if no query present.
|
||||
#
|
||||
# "localhost"
|
||||
# # => {}
|
||||
def query_hash
|
||||
Hash[(@query || "").split("&").map { |pair| pair.split("=") }]
|
||||
end
|
||||
@ -92,7 +92,7 @@ def raw_config
|
||||
end
|
||||
end
|
||||
|
||||
# Returns name of the database.
|
||||
# Returns name of the database.
|
||||
def database_from_path
|
||||
if @adapter == "sqlite3"
|
||||
# 'sqlite3:/foo' is absolute, because that makes sense. The
|
||||
@ -192,26 +192,26 @@ def spec(config)
|
||||
|
||||
private
|
||||
|
||||
# Returns fully resolved connection, accepts hash, string or symbol.
|
||||
# Always returns a hash.
|
||||
#
|
||||
# == Examples
|
||||
#
|
||||
# Symbol representing current environment.
|
||||
#
|
||||
# Resolver.new("production" => {}).resolve_connection(:production)
|
||||
# # => {}
|
||||
#
|
||||
# One layer deep hash of connection values.
|
||||
#
|
||||
# Resolver.new({}).resolve_connection("adapter" => "sqlite3")
|
||||
# # => { "adapter" => "sqlite3" }
|
||||
#
|
||||
# Connection URL.
|
||||
#
|
||||
# Resolver.new({}).resolve_connection("postgresql://localhost/foo")
|
||||
# # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" }
|
||||
#
|
||||
# Returns fully resolved connection, accepts hash, string or symbol.
|
||||
# Always returns a hash.
|
||||
#
|
||||
# == Examples
|
||||
#
|
||||
# Symbol representing current environment.
|
||||
#
|
||||
# Resolver.new("production" => {}).resolve_connection(:production)
|
||||
# # => {}
|
||||
#
|
||||
# One layer deep hash of connection values.
|
||||
#
|
||||
# Resolver.new({}).resolve_connection("adapter" => "sqlite3")
|
||||
# # => { "adapter" => "sqlite3" }
|
||||
#
|
||||
# Connection URL.
|
||||
#
|
||||
# Resolver.new({}).resolve_connection("postgresql://localhost/foo")
|
||||
# # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" }
|
||||
#
|
||||
def resolve_connection(spec)
|
||||
case spec
|
||||
when Symbol
|
||||
@ -223,13 +223,13 @@ def resolve_connection(spec)
|
||||
end
|
||||
end
|
||||
|
||||
# Takes the environment such as +:production+ or +:development+.
|
||||
# This requires that the @configurations was initialized with a key that
|
||||
# matches.
|
||||
#
|
||||
# Resolver.new("production" => {}).resolve_symbol_connection(:production)
|
||||
# # => {}
|
||||
#
|
||||
# Takes the environment such as +:production+ or +:development+.
|
||||
# This requires that the @configurations was initialized with a key that
|
||||
# matches.
|
||||
#
|
||||
# Resolver.new("production" => {}).resolve_symbol_connection(:production)
|
||||
# # => {}
|
||||
#
|
||||
def resolve_symbol_connection(spec)
|
||||
if config = configurations[spec.to_s]
|
||||
resolve_connection(config).merge("name" => spec.to_s)
|
||||
@ -238,10 +238,10 @@ def resolve_symbol_connection(spec)
|
||||
end
|
||||
end
|
||||
|
||||
# Accepts a hash. Expands the "url" key that contains a
|
||||
# URL database connection to a full connection
|
||||
# hash and merges with the rest of the hash.
|
||||
# Connection details inside of the "url" key win any merge conflicts
|
||||
# Accepts a hash. Expands the "url" key that contains a
|
||||
# URL database connection to a full connection
|
||||
# hash and merges with the rest of the hash.
|
||||
# Connection details inside of the "url" key win any merge conflicts
|
||||
def resolve_hash_connection(spec)
|
||||
if spec["url"] && spec["url"] !~ /^jdbc:/
|
||||
connection_hash = resolve_url_connection(spec.delete("url"))
|
||||
@ -250,11 +250,11 @@ def resolve_hash_connection(spec)
|
||||
spec
|
||||
end
|
||||
|
||||
# Takes a connection URL.
|
||||
#
|
||||
# Resolver.new({}).resolve_url_connection("postgresql://localhost/foo")
|
||||
# # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" }
|
||||
#
|
||||
# Takes a connection URL.
|
||||
#
|
||||
# Resolver.new({}).resolve_url_connection("postgresql://localhost/foo")
|
||||
# # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" }
|
||||
#
|
||||
def resolve_url_connection(url)
|
||||
ConnectionUrlResolver.new(url).to_hash
|
||||
end
|
||||
|
@ -527,7 +527,7 @@ def extract_value_from_default(default) # :nodoc:
|
||||
case default
|
||||
# Quoted types
|
||||
when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
|
||||
# The default 'now'::date is CURRENT_DATE
|
||||
# The default 'now'::date is CURRENT_DATE
|
||||
if $1 == "now".freeze && $2 == "date".freeze
|
||||
nil
|
||||
else
|
||||
@ -542,9 +542,9 @@ def extract_value_from_default(default) # :nodoc:
|
||||
# Object identifier types
|
||||
when /\A-?\d+\z/
|
||||
$1
|
||||
else
|
||||
# Anything else is blank, some user type, or some function
|
||||
# and we can't know the value of that, so return nil.
|
||||
else
|
||||
# Anything else is blank, some user type, or some function
|
||||
# and we can't know the value of that, so return nil.
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
@ -75,14 +75,14 @@ def body
|
||||
"#{finder}(#{attributes_hash})"
|
||||
end
|
||||
|
||||
# The parameters in the signature may have reserved Ruby words, in order
|
||||
# to prevent errors, we start each param name with `_`.
|
||||
# The parameters in the signature may have reserved Ruby words, in order
|
||||
# to prevent errors, we start each param name with `_`.
|
||||
def signature
|
||||
attribute_names.map { |name| "_#{name}" }.join(", ")
|
||||
end
|
||||
|
||||
# Given that the parameters starts with `_`, the finder needs to use the
|
||||
# same parameter name.
|
||||
# Given that the parameters starts with `_`, the finder needs to use the
|
||||
# same parameter name.
|
||||
def attributes_hash
|
||||
"{" + attribute_names.map { |name| ":#{name} => _#{name}" }.join(",") + "}"
|
||||
end
|
||||
|
@ -132,8 +132,8 @@ def sti_name
|
||||
|
||||
protected
|
||||
|
||||
# Returns the class type of the record using the current module as a prefix. So descendants of
|
||||
# MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
|
||||
# Returns the class type of the record using the current module as a prefix. So descendants of
|
||||
# MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
|
||||
def compute_type(type_name)
|
||||
if type_name.match(/^::/)
|
||||
# If the type is prefixed with a scope operator then we assume that
|
||||
@ -156,9 +156,9 @@ def compute_type(type_name)
|
||||
|
||||
private
|
||||
|
||||
# Called by +instantiate+ to decide which class to use for a new
|
||||
# record instance. For single-table inheritance, we check the record
|
||||
# for a +type+ column and return the corresponding class.
|
||||
# Called by +instantiate+ to decide which class to use for a new
|
||||
# record instance. For single-table inheritance, we check the record
|
||||
# for a +type+ column and return the corresponding class.
|
||||
def discriminate_class_for_record(record)
|
||||
if using_single_table_inheritance?(record)
|
||||
find_sti_class(record[inheritance_column])
|
||||
@ -199,8 +199,8 @@ def type_condition(table = arel_table)
|
||||
sti_column.in(sti_names)
|
||||
end
|
||||
|
||||
# Detect the subclass from the inheritance column of attrs. If the inheritance column value
|
||||
# is not self or a valid subclass, raises ActiveRecord::SubclassNotFound
|
||||
# Detect the subclass from the inheritance column of attrs. If the inheritance column value
|
||||
# is not self or a valid subclass, raises ActiveRecord::SubclassNotFound
|
||||
def subclass_from_attributes(attrs)
|
||||
attrs = attrs.to_h if attrs.respond_to?(:permitted?)
|
||||
if attrs.is_a?(Hash)
|
||||
@ -225,11 +225,11 @@ def initialize_internals_callback
|
||||
ensure_proper_type
|
||||
end
|
||||
|
||||
# Sets the attribute used for single table inheritance to this class name if this is not the
|
||||
# ActiveRecord::Base descendant.
|
||||
# Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to
|
||||
# do Reply.new without having to set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself.
|
||||
# No such attribute would be set for objects of the Message class in that example.
|
||||
# Sets the attribute used for single table inheritance to this class name if this is not the
|
||||
# ActiveRecord::Base descendant.
|
||||
# Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to
|
||||
# do Reply.new without having to set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself.
|
||||
# No such attribute would be set for objects of the Message class in that example.
|
||||
def ensure_proper_type
|
||||
klass = self.class
|
||||
if klass.finder_needs_type_condition?
|
||||
|
@ -168,10 +168,10 @@ def update_counters(id, counters)
|
||||
|
||||
private
|
||||
|
||||
# We need to apply this decorator here, rather than on module inclusion. The closure
|
||||
# created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
|
||||
# sub class being decorated. As such, changes to `lock_optimistically`, or
|
||||
# `locking_column` would not be picked up.
|
||||
# We need to apply this decorator here, rather than on module inclusion. The closure
|
||||
# created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
|
||||
# sub class being decorated. As such, changes to `lock_optimistically`, or
|
||||
# `locking_column` would not be picked up.
|
||||
def inherited(subclass)
|
||||
subclass.class_eval do
|
||||
is_lock_column = ->(name, _) { lock_optimistically && name == locking_column }
|
||||
|
@ -1163,7 +1163,7 @@ def load_migrated
|
||||
|
||||
private
|
||||
|
||||
# Used for running a specific migration.
|
||||
# Used for running a specific migration.
|
||||
def run_without_lock
|
||||
migration = migrations.detect { |m| m.version == @target_version }
|
||||
raise UnknownMigrationVersionError.new(@target_version) if migration.nil?
|
||||
@ -1172,7 +1172,7 @@ def run_without_lock
|
||||
record_environment
|
||||
end
|
||||
|
||||
# Used for running multiple migrations up to or down to a certain value.
|
||||
# Used for running multiple migrations up to or down to a certain value.
|
||||
def migrate_without_lock
|
||||
if invalid_target?
|
||||
raise UnknownMigrationVersionError.new(@target_version)
|
||||
@ -1185,7 +1185,7 @@ def migrate_without_lock
|
||||
record_environment
|
||||
end
|
||||
|
||||
# Stores the current environment in the database.
|
||||
# Stores the current environment in the database.
|
||||
def record_environment
|
||||
return if down?
|
||||
ActiveRecord::InternalMetadata[:environment] = ActiveRecord::Migrator.current_environment
|
||||
@ -1195,7 +1195,7 @@ def ran?(migration)
|
||||
migrated.include?(migration.version.to_i)
|
||||
end
|
||||
|
||||
# Return true if a valid version is not provided.
|
||||
# Return true if a valid version is not provided.
|
||||
def invalid_target?
|
||||
!target && @target_version && @target_version > 0
|
||||
end
|
||||
@ -1272,7 +1272,7 @@ def down?
|
||||
@direction == :down
|
||||
end
|
||||
|
||||
# Wrap the migration in a transaction only if supported by the adapter.
|
||||
# Wrap the migration in a transaction only if supported by the adapter.
|
||||
def ddl_transaction(migration)
|
||||
if use_transaction?(migration)
|
||||
Base.transaction { yield }
|
||||
|
@ -225,7 +225,7 @@ def invert_remove_foreign_key(args)
|
||||
[:add_foreign_key, reversed_args]
|
||||
end
|
||||
|
||||
# Forwards any missing method call to the \target.
|
||||
# Forwards any missing method call to the \target.
|
||||
def method_missing(method, *args, &block)
|
||||
if @delegate.respond_to?(method)
|
||||
@delegate.send(method, *args, &block)
|
||||
|
@ -397,13 +397,13 @@ def reload_schema_from_cache
|
||||
end
|
||||
end
|
||||
|
||||
# Guesses the table name, but does not decorate it with prefix and suffix information.
|
||||
# Guesses the table name, but does not decorate it with prefix and suffix information.
|
||||
def undecorated_table_name(class_name = base_class.name)
|
||||
table_name = class_name.to_s.demodulize.underscore
|
||||
pluralize_table_names ? table_name.pluralize : table_name
|
||||
end
|
||||
|
||||
# Computes and returns a table name according to default conventions.
|
||||
# Computes and returns a table name according to default conventions.
|
||||
def compute_table_name
|
||||
base = base_class
|
||||
if self == base
|
||||
|
@ -341,17 +341,17 @@ def accepts_nested_attributes_for(*attr_names)
|
||||
|
||||
private
|
||||
|
||||
# Generates a writer method for this association. Serves as a point for
|
||||
# accessing the objects in the association. For example, this method
|
||||
# could generate the following:
|
||||
#
|
||||
# def pirate_attributes=(attributes)
|
||||
# assign_nested_attributes_for_one_to_one_association(:pirate, attributes)
|
||||
# end
|
||||
#
|
||||
# This redirects the attempts to write objects in an association through
|
||||
# the helper methods defined below. Makes it seem like the nested
|
||||
# associations are just regular associations.
|
||||
# Generates a writer method for this association. Serves as a point for
|
||||
# accessing the objects in the association. For example, this method
|
||||
# could generate the following:
|
||||
#
|
||||
# def pirate_attributes=(attributes)
|
||||
# assign_nested_attributes_for_one_to_one_association(:pirate, attributes)
|
||||
# end
|
||||
#
|
||||
# This redirects the attempts to write objects in an association through
|
||||
# the helper methods defined below. Makes it seem like the nested
|
||||
# associations are just regular associations.
|
||||
def generate_association_writer(association_name, type)
|
||||
generated_association_methods.module_eval <<-eoruby, __FILE__, __LINE__ + 1
|
||||
if method_defined?(:#{association_name}_attributes=)
|
||||
@ -375,23 +375,23 @@ def _destroy
|
||||
|
||||
private
|
||||
|
||||
# Attribute hash keys that should not be assigned as normal attributes.
|
||||
# These hash keys are nested attributes implementation details.
|
||||
# Attribute hash keys that should not be assigned as normal attributes.
|
||||
# These hash keys are nested attributes implementation details.
|
||||
UNASSIGNABLE_KEYS = %w( id _destroy )
|
||||
|
||||
# Assigns the given attributes to the association.
|
||||
#
|
||||
# If an associated record does not yet exist, one will be instantiated. If
|
||||
# an associated record already exists, the method's behavior depends on
|
||||
# the value of the update_only option. If update_only is +false+ and the
|
||||
# given attributes include an <tt>:id</tt> that matches the existing record's
|
||||
# id, then the existing record will be modified. If no <tt>:id</tt> is provided
|
||||
# it will be replaced with a new record. If update_only is +true+ the existing
|
||||
# record will be modified regardless of whether an <tt>:id</tt> is provided.
|
||||
#
|
||||
# If the given attributes include a matching <tt>:id</tt> attribute, or
|
||||
# update_only is true, and a <tt>:_destroy</tt> key set to a truthy value,
|
||||
# then the existing record will be marked for destruction.
|
||||
# Assigns the given attributes to the association.
|
||||
#
|
||||
# If an associated record does not yet exist, one will be instantiated. If
|
||||
# an associated record already exists, the method's behavior depends on
|
||||
# the value of the update_only option. If update_only is +false+ and the
|
||||
# given attributes include an <tt>:id</tt> that matches the existing record's
|
||||
# id, then the existing record will be modified. If no <tt>:id</tt> is provided
|
||||
# it will be replaced with a new record. If update_only is +true+ the existing
|
||||
# record will be modified regardless of whether an <tt>:id</tt> is provided.
|
||||
#
|
||||
# If the given attributes include a matching <tt>:id</tt> attribute, or
|
||||
# update_only is true, and a <tt>:_destroy</tt> key set to a truthy value,
|
||||
# then the existing record will be marked for destruction.
|
||||
def assign_nested_attributes_for_one_to_one_association(association_name, attributes)
|
||||
options = self.nested_attributes_options[association_name]
|
||||
if attributes.respond_to?(:permitted?)
|
||||
@ -424,33 +424,33 @@ def assign_nested_attributes_for_one_to_one_association(association_name, attrib
|
||||
end
|
||||
end
|
||||
|
||||
# Assigns the given attributes to the collection association.
|
||||
#
|
||||
# Hashes with an <tt>:id</tt> value matching an existing associated record
|
||||
# will update that record. Hashes without an <tt>:id</tt> value will build
|
||||
# a new record for the association. Hashes with a matching <tt>:id</tt>
|
||||
# value and a <tt>:_destroy</tt> key set to a truthy value will mark the
|
||||
# matched record for destruction.
|
||||
#
|
||||
# For example:
|
||||
#
|
||||
# assign_nested_attributes_for_collection_association(:people, {
|
||||
# '1' => { id: '1', name: 'Peter' },
|
||||
# '2' => { name: 'John' },
|
||||
# '3' => { id: '2', _destroy: true }
|
||||
# })
|
||||
#
|
||||
# Will update the name of the Person with ID 1, build a new associated
|
||||
# person with the name 'John', and mark the associated Person with ID 2
|
||||
# for destruction.
|
||||
#
|
||||
# Also accepts an Array of attribute hashes:
|
||||
#
|
||||
# assign_nested_attributes_for_collection_association(:people, [
|
||||
# { id: '1', name: 'Peter' },
|
||||
# { name: 'John' },
|
||||
# { id: '2', _destroy: true }
|
||||
# ])
|
||||
# Assigns the given attributes to the collection association.
|
||||
#
|
||||
# Hashes with an <tt>:id</tt> value matching an existing associated record
|
||||
# will update that record. Hashes without an <tt>:id</tt> value will build
|
||||
# a new record for the association. Hashes with a matching <tt>:id</tt>
|
||||
# value and a <tt>:_destroy</tt> key set to a truthy value will mark the
|
||||
# matched record for destruction.
|
||||
#
|
||||
# For example:
|
||||
#
|
||||
# assign_nested_attributes_for_collection_association(:people, {
|
||||
# '1' => { id: '1', name: 'Peter' },
|
||||
# '2' => { name: 'John' },
|
||||
# '3' => { id: '2', _destroy: true }
|
||||
# })
|
||||
#
|
||||
# Will update the name of the Person with ID 1, build a new associated
|
||||
# person with the name 'John', and mark the associated Person with ID 2
|
||||
# for destruction.
|
||||
#
|
||||
# Also accepts an Array of attribute hashes:
|
||||
#
|
||||
# assign_nested_attributes_for_collection_association(:people, [
|
||||
# { id: '1', name: 'Peter' },
|
||||
# { name: 'John' },
|
||||
# { id: '2', _destroy: true }
|
||||
# ])
|
||||
def assign_nested_attributes_for_collection_association(association_name, attributes_collection)
|
||||
options = self.nested_attributes_options[association_name]
|
||||
if attributes_collection.respond_to?(:permitted?)
|
||||
@ -511,12 +511,12 @@ def assign_nested_attributes_for_collection_association(association_name, attrib
|
||||
end
|
||||
end
|
||||
|
||||
# Takes in a limit and checks if the attributes_collection has too many
|
||||
# records. It accepts limit in the form of symbol, proc, or
|
||||
# number-like object (anything that can be compared with an integer).
|
||||
#
|
||||
# Raises TooManyRecords error if the attributes_collection is
|
||||
# larger than the limit.
|
||||
# Takes in a limit and checks if the attributes_collection has too many
|
||||
# records. It accepts limit in the form of symbol, proc, or
|
||||
# number-like object (anything that can be compared with an integer).
|
||||
#
|
||||
# Raises TooManyRecords error if the attributes_collection is
|
||||
# larger than the limit.
|
||||
def check_record_limit!(limit, attributes_collection)
|
||||
if limit
|
||||
limit = \
|
||||
@ -535,30 +535,30 @@ def check_record_limit!(limit, attributes_collection)
|
||||
end
|
||||
end
|
||||
|
||||
# Updates a record with the +attributes+ or marks it for destruction if
|
||||
# +allow_destroy+ is +true+ and has_destroy_flag? returns +true+.
|
||||
# Updates a record with the +attributes+ or marks it for destruction if
|
||||
# +allow_destroy+ is +true+ and has_destroy_flag? returns +true+.
|
||||
def assign_to_or_mark_for_destruction(record, attributes, allow_destroy)
|
||||
record.assign_attributes(attributes.except(*UNASSIGNABLE_KEYS))
|
||||
record.mark_for_destruction if has_destroy_flag?(attributes) && allow_destroy
|
||||
end
|
||||
|
||||
# Determines if a hash contains a truthy _destroy key.
|
||||
# Determines if a hash contains a truthy _destroy key.
|
||||
def has_destroy_flag?(hash)
|
||||
Type::Boolean.new.cast(hash["_destroy"])
|
||||
end
|
||||
|
||||
# Determines if a new record should be rejected by checking
|
||||
# has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
|
||||
# association and evaluates to +true+.
|
||||
# Determines if a new record should be rejected by checking
|
||||
# has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
|
||||
# association and evaluates to +true+.
|
||||
def reject_new_record?(association_name, attributes)
|
||||
will_be_destroyed?(association_name, attributes) || call_reject_if(association_name, attributes)
|
||||
end
|
||||
|
||||
# Determines if a record with the particular +attributes+ should be
|
||||
# rejected by calling the reject_if Symbol or Proc (if defined).
|
||||
# The reject_if option is defined by +accepts_nested_attributes_for+.
|
||||
#
|
||||
# Returns false if there is a +destroy_flag+ on the attributes.
|
||||
# Determines if a record with the particular +attributes+ should be
|
||||
# rejected by calling the reject_if Symbol or Proc (if defined).
|
||||
# The reject_if option is defined by +accepts_nested_attributes_for+.
|
||||
#
|
||||
# Returns false if there is a +destroy_flag+ on the attributes.
|
||||
def call_reject_if(association_name, attributes)
|
||||
return false if will_be_destroyed?(association_name, attributes)
|
||||
|
||||
@ -570,7 +570,7 @@ def call_reject_if(association_name, attributes)
|
||||
end
|
||||
end
|
||||
|
||||
# Only take into account the destroy flag if <tt>:allow_destroy</tt> is true
|
||||
# Only take into account the destroy flag if <tt>:allow_destroy</tt> is true
|
||||
def will_be_destroyed?(association_name, attributes)
|
||||
allow_destroy?(association_name) && has_destroy_flag?(attributes)
|
||||
end
|
||||
|
@ -325,13 +325,13 @@ def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
|
||||
end]
|
||||
end
|
||||
|
||||
# Converts the given keys to the value that the database adapter returns as
|
||||
# a usable column name:
|
||||
#
|
||||
# column_alias_for("users.id") # => "users_id"
|
||||
# column_alias_for("sum(id)") # => "sum_id"
|
||||
# column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
|
||||
# column_alias_for("count(*)") # => "count_all"
|
||||
# Converts the given keys to the value that the database adapter returns as
|
||||
# a usable column name:
|
||||
#
|
||||
# column_alias_for("users.id") # => "users_id"
|
||||
# column_alias_for("sum(id)") # => "sum_id"
|
||||
# column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
|
||||
# column_alias_for("count(*)") # => "count_all"
|
||||
def column_alias_for(keys)
|
||||
if keys.respond_to? :name
|
||||
keys = "#{keys.relation.name}.#{keys.name}"
|
||||
|
@ -1150,22 +1150,22 @@ def preprocess_order_args(order_args)
|
||||
end.flatten!
|
||||
end
|
||||
|
||||
# Checks to make sure that the arguments are not blank. Note that if some
|
||||
# blank-like object were initially passed into the query method, then this
|
||||
# method will not raise an error.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# Post.references() # raises an error
|
||||
# Post.references([]) # does not raise an error
|
||||
#
|
||||
# This particular method should be called with a method_name and the args
|
||||
# passed into that method as an input. For example:
|
||||
#
|
||||
# def references(*args)
|
||||
# check_if_method_has_arguments!("references", args)
|
||||
# ...
|
||||
# end
|
||||
# Checks to make sure that the arguments are not blank. Note that if some
|
||||
# blank-like object were initially passed into the query method, then this
|
||||
# method will not raise an error.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# Post.references() # raises an error
|
||||
# Post.references([]) # does not raise an error
|
||||
#
|
||||
# This particular method should be called with a method_name and the args
|
||||
# passed into that method as an input. For example:
|
||||
#
|
||||
# def references(*args)
|
||||
# check_if_method_has_arguments!("references", args)
|
||||
# ...
|
||||
# end
|
||||
def check_if_method_has_arguments!(method_name, args)
|
||||
if args.blank?
|
||||
raise ArgumentError, "The method .#{method_name}() must contain arguments."
|
||||
|
@ -7,20 +7,20 @@ module Sanitization
|
||||
module ClassMethods
|
||||
protected
|
||||
|
||||
# Accepts an array or string of SQL conditions and sanitizes
|
||||
# them into a valid SQL fragment for a WHERE clause.
|
||||
#
|
||||
# sanitize_sql_for_conditions(["name=? and group_id=?", "foo'bar", 4])
|
||||
# # => "name='foo''bar' and group_id=4"
|
||||
#
|
||||
# sanitize_sql_for_conditions(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])
|
||||
# # => "name='foo''bar' and group_id='4'"
|
||||
#
|
||||
# sanitize_sql_for_conditions(["name='%s' and group_id='%s'", "foo'bar", 4])
|
||||
# # => "name='foo''bar' and group_id='4'"
|
||||
#
|
||||
# sanitize_sql_for_conditions("name='foo''bar' and group_id='4'")
|
||||
# # => "name='foo''bar' and group_id='4'"
|
||||
# Accepts an array or string of SQL conditions and sanitizes
|
||||
# them into a valid SQL fragment for a WHERE clause.
|
||||
#
|
||||
# sanitize_sql_for_conditions(["name=? and group_id=?", "foo'bar", 4])
|
||||
# # => "name='foo''bar' and group_id=4"
|
||||
#
|
||||
# sanitize_sql_for_conditions(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])
|
||||
# # => "name='foo''bar' and group_id='4'"
|
||||
#
|
||||
# sanitize_sql_for_conditions(["name='%s' and group_id='%s'", "foo'bar", 4])
|
||||
# # => "name='foo''bar' and group_id='4'"
|
||||
#
|
||||
# sanitize_sql_for_conditions("name='foo''bar' and group_id='4'")
|
||||
# # => "name='foo''bar' and group_id='4'"
|
||||
def sanitize_sql_for_conditions(condition)
|
||||
return nil if condition.blank?
|
||||
|
||||
@ -33,20 +33,20 @@ def sanitize_sql_for_conditions(condition)
|
||||
alias :sanitize_conditions :sanitize_sql
|
||||
deprecate sanitize_conditions: :sanitize_sql
|
||||
|
||||
# Accepts an array, hash, or string of SQL conditions and sanitizes
|
||||
# them into a valid SQL fragment for a SET clause.
|
||||
#
|
||||
# sanitize_sql_for_assignment(["name=? and group_id=?", nil, 4])
|
||||
# # => "name=NULL and group_id=4"
|
||||
#
|
||||
# sanitize_sql_for_assignment(["name=:name and group_id=:group_id", name: nil, group_id: 4])
|
||||
# # => "name=NULL and group_id=4"
|
||||
#
|
||||
# Post.send(:sanitize_sql_for_assignment, { name: nil, group_id: 4 })
|
||||
# # => "`posts`.`name` = NULL, `posts`.`group_id` = 4"
|
||||
#
|
||||
# sanitize_sql_for_assignment("name=NULL and group_id='4'")
|
||||
# # => "name=NULL and group_id='4'"
|
||||
# Accepts an array, hash, or string of SQL conditions and sanitizes
|
||||
# them into a valid SQL fragment for a SET clause.
|
||||
#
|
||||
# sanitize_sql_for_assignment(["name=? and group_id=?", nil, 4])
|
||||
# # => "name=NULL and group_id=4"
|
||||
#
|
||||
# sanitize_sql_for_assignment(["name=:name and group_id=:group_id", name: nil, group_id: 4])
|
||||
# # => "name=NULL and group_id=4"
|
||||
#
|
||||
# Post.send(:sanitize_sql_for_assignment, { name: nil, group_id: 4 })
|
||||
# # => "`posts`.`name` = NULL, `posts`.`group_id` = 4"
|
||||
#
|
||||
# sanitize_sql_for_assignment("name=NULL and group_id='4'")
|
||||
# # => "name=NULL and group_id='4'"
|
||||
def sanitize_sql_for_assignment(assignments, default_table_name = self.table_name)
|
||||
case assignments
|
||||
when Array; sanitize_sql_array(assignments)
|
||||
@ -55,14 +55,14 @@ def sanitize_sql_for_assignment(assignments, default_table_name = self.table_nam
|
||||
end
|
||||
end
|
||||
|
||||
# Accepts an array, or string of SQL conditions and sanitizes
|
||||
# them into a valid SQL fragment for an ORDER clause.
|
||||
#
|
||||
# sanitize_sql_for_order(["field(id, ?)", [1,3,2]])
|
||||
# # => "field(id, 1,3,2)"
|
||||
#
|
||||
# sanitize_sql_for_order("id ASC")
|
||||
# # => "id ASC"
|
||||
# Accepts an array, or string of SQL conditions and sanitizes
|
||||
# them into a valid SQL fragment for an ORDER clause.
|
||||
#
|
||||
# sanitize_sql_for_order(["field(id, ?)", [1,3,2]])
|
||||
# # => "field(id, 1,3,2)"
|
||||
#
|
||||
# sanitize_sql_for_order("id ASC")
|
||||
# # => "id ASC"
|
||||
def sanitize_sql_for_order(condition)
|
||||
if condition.is_a?(Array) && condition.first.to_s.include?("?")
|
||||
sanitize_sql_array(condition)
|
||||
@ -71,21 +71,21 @@ def sanitize_sql_for_order(condition)
|
||||
end
|
||||
end
|
||||
|
||||
# Accepts a hash of SQL conditions and replaces those attributes
|
||||
# that correspond to a {#composed_of}[rdoc-ref:Aggregations::ClassMethods#composed_of]
|
||||
# relationship with their expanded aggregate attribute values.
|
||||
#
|
||||
# Given:
|
||||
#
|
||||
# class Person < ActiveRecord::Base
|
||||
# composed_of :address, class_name: "Address",
|
||||
# mapping: [%w(address_street street), %w(address_city city)]
|
||||
# end
|
||||
#
|
||||
# Then:
|
||||
#
|
||||
# { address: Address.new("813 abc st.", "chicago") }
|
||||
# # => { address_street: "813 abc st.", address_city: "chicago" }
|
||||
# Accepts a hash of SQL conditions and replaces those attributes
|
||||
# that correspond to a {#composed_of}[rdoc-ref:Aggregations::ClassMethods#composed_of]
|
||||
# relationship with their expanded aggregate attribute values.
|
||||
#
|
||||
# Given:
|
||||
#
|
||||
# class Person < ActiveRecord::Base
|
||||
# composed_of :address, class_name: "Address",
|
||||
# mapping: [%w(address_street street), %w(address_city city)]
|
||||
# end
|
||||
#
|
||||
# Then:
|
||||
#
|
||||
# { address: Address.new("813 abc st.", "chicago") }
|
||||
# # => { address_street: "813 abc st.", address_city: "chicago" }
|
||||
def expand_hash_conditions_for_aggregates(attrs)
|
||||
expanded_attrs = {}
|
||||
attrs.each do |attr, value|
|
||||
@ -105,10 +105,10 @@ def expand_hash_conditions_for_aggregates(attrs)
|
||||
expanded_attrs
|
||||
end
|
||||
|
||||
# Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
|
||||
#
|
||||
# sanitize_sql_hash_for_assignment({ status: nil, group_id: 1 }, "posts")
|
||||
# # => "`posts`.`status` = NULL, `posts`.`group_id` = 1"
|
||||
# Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
|
||||
#
|
||||
# sanitize_sql_hash_for_assignment({ status: nil, group_id: 1 }, "posts")
|
||||
# # => "`posts`.`status` = NULL, `posts`.`group_id` = 1"
|
||||
def sanitize_sql_hash_for_assignment(attrs, table)
|
||||
c = connection
|
||||
attrs.map do |attr, value|
|
||||
@ -117,36 +117,36 @@ def sanitize_sql_hash_for_assignment(attrs, table)
|
||||
end.join(", ")
|
||||
end
|
||||
|
||||
# Sanitizes a +string+ so that it is safe to use within an SQL
|
||||
# LIKE statement. This method uses +escape_character+ to escape all occurrences of "\", "_" and "%".
|
||||
#
|
||||
# sanitize_sql_like("100%")
|
||||
# # => "100\\%"
|
||||
#
|
||||
# sanitize_sql_like("snake_cased_string")
|
||||
# # => "snake\\_cased\\_string"
|
||||
#
|
||||
# sanitize_sql_like("100%", "!")
|
||||
# # => "100!%"
|
||||
#
|
||||
# sanitize_sql_like("snake_cased_string", "!")
|
||||
# # => "snake!_cased!_string"
|
||||
# Sanitizes a +string+ so that it is safe to use within an SQL
|
||||
# LIKE statement. This method uses +escape_character+ to escape all occurrences of "\", "_" and "%".
|
||||
#
|
||||
# sanitize_sql_like("100%")
|
||||
# # => "100\\%"
|
||||
#
|
||||
# sanitize_sql_like("snake_cased_string")
|
||||
# # => "snake\\_cased\\_string"
|
||||
#
|
||||
# sanitize_sql_like("100%", "!")
|
||||
# # => "100!%"
|
||||
#
|
||||
# sanitize_sql_like("snake_cased_string", "!")
|
||||
# # => "snake!_cased!_string"
|
||||
def sanitize_sql_like(string, escape_character = "\\")
|
||||
pattern = Regexp.union(escape_character, "%", "_")
|
||||
string.gsub(pattern) { |x| [escape_character, x].join }
|
||||
end
|
||||
|
||||
# Accepts an array of conditions. The array has each value
|
||||
# sanitized and interpolated into the SQL statement.
|
||||
#
|
||||
# sanitize_sql_array(["name=? and group_id=?", "foo'bar", 4])
|
||||
# # => "name='foo''bar' and group_id=4"
|
||||
#
|
||||
# sanitize_sql_array(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])
|
||||
# # => "name='foo''bar' and group_id=4"
|
||||
#
|
||||
# sanitize_sql_array(["name='%s' and group_id='%s'", "foo'bar", 4])
|
||||
# # => "name='foo''bar' and group_id='4'"
|
||||
# Accepts an array of conditions. The array has each value
|
||||
# sanitized and interpolated into the SQL statement.
|
||||
#
|
||||
# sanitize_sql_array(["name=? and group_id=?", "foo'bar", 4])
|
||||
# # => "name='foo''bar' and group_id=4"
|
||||
#
|
||||
# sanitize_sql_array(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])
|
||||
# # => "name='foo''bar' and group_id=4"
|
||||
#
|
||||
# sanitize_sql_array(["name='%s' and group_id='%s'", "foo'bar", 4])
|
||||
# # => "name='foo''bar' and group_id='4'"
|
||||
def sanitize_sql_array(ary)
|
||||
statement, *values = ary
|
||||
if values.first.is_a?(Hash) && /:\w+/.match?(statement)
|
||||
|
@ -46,47 +46,47 @@ def before_remove_const #:nodoc:
|
||||
|
||||
protected
|
||||
|
||||
# Use this macro in your model to set a default scope for all operations on
|
||||
# the model.
|
||||
#
|
||||
# class Article < ActiveRecord::Base
|
||||
# default_scope { where(published: true) }
|
||||
# end
|
||||
#
|
||||
# Article.all # => SELECT * FROM articles WHERE published = true
|
||||
#
|
||||
# The #default_scope is also applied while creating/building a record.
|
||||
# It is not applied while updating a record.
|
||||
#
|
||||
# Article.new.published # => true
|
||||
# Article.create.published # => true
|
||||
#
|
||||
# (You can also pass any object which responds to +call+ to the
|
||||
# +default_scope+ macro, and it will be called when building the
|
||||
# default scope.)
|
||||
#
|
||||
# If you use multiple #default_scope declarations in your model then
|
||||
# they will be merged together:
|
||||
#
|
||||
# class Article < ActiveRecord::Base
|
||||
# default_scope { where(published: true) }
|
||||
# default_scope { where(rating: 'G') }
|
||||
# end
|
||||
#
|
||||
# Article.all # => SELECT * FROM articles WHERE published = true AND rating = 'G'
|
||||
#
|
||||
# This is also the case with inheritance and module includes where the
|
||||
# parent or module defines a #default_scope and the child or including
|
||||
# class defines a second one.
|
||||
#
|
||||
# If you need to do more complex things with a default scope, you can
|
||||
# alternatively define it as a class method:
|
||||
#
|
||||
# class Article < ActiveRecord::Base
|
||||
# def self.default_scope
|
||||
# # Should return a scope, you can call 'super' here etc.
|
||||
# end
|
||||
# end
|
||||
# Use this macro in your model to set a default scope for all operations on
|
||||
# the model.
|
||||
#
|
||||
# class Article < ActiveRecord::Base
|
||||
# default_scope { where(published: true) }
|
||||
# end
|
||||
#
|
||||
# Article.all # => SELECT * FROM articles WHERE published = true
|
||||
#
|
||||
# The #default_scope is also applied while creating/building a record.
|
||||
# It is not applied while updating a record.
|
||||
#
|
||||
# Article.new.published # => true
|
||||
# Article.create.published # => true
|
||||
#
|
||||
# (You can also pass any object which responds to +call+ to the
|
||||
# +default_scope+ macro, and it will be called when building the
|
||||
# default scope.)
|
||||
#
|
||||
# If you use multiple #default_scope declarations in your model then
|
||||
# they will be merged together:
|
||||
#
|
||||
# class Article < ActiveRecord::Base
|
||||
# default_scope { where(published: true) }
|
||||
# default_scope { where(rating: 'G') }
|
||||
# end
|
||||
#
|
||||
# Article.all # => SELECT * FROM articles WHERE published = true AND rating = 'G'
|
||||
#
|
||||
# This is also the case with inheritance and module includes where the
|
||||
# parent or module defines a #default_scope and the child or including
|
||||
# class defines a second one.
|
||||
#
|
||||
# If you need to do more complex things with a default scope, you can
|
||||
# alternatively define it as a class method:
|
||||
#
|
||||
# class Article < ActiveRecord::Base
|
||||
# def self.default_scope
|
||||
# # Should return a scope, you can call 'super' here etc.
|
||||
# end
|
||||
# end
|
||||
def default_scope(scope = nil)
|
||||
scope = Proc.new if block_given?
|
||||
|
||||
@ -130,9 +130,9 @@ def ignore_default_scope=(ignore) # :nodoc:
|
||||
ScopeRegistry.set_value_for(:ignore_default_scope, base_class, ignore)
|
||||
end
|
||||
|
||||
# The ignore_default_scope flag is used to prevent an infinite recursion
|
||||
# situation where a default scope references a scope which has a default
|
||||
# scope which references a scope...
|
||||
# The ignore_default_scope flag is used to prevent an infinite recursion
|
||||
# situation where a default scope references a scope which has a default
|
||||
# scope which references a scope...
|
||||
def evaluate_default_scope # :nodoc:
|
||||
return if ignore_default_scope?
|
||||
|
||||
|
@ -409,7 +409,7 @@ def with_transaction_returning_status
|
||||
|
||||
protected
|
||||
|
||||
# Save the new record state and id of a record so it can be restored later if a transaction fails.
|
||||
# Save the new record state and id of a record so it can be restored later if a transaction fails.
|
||||
def remember_transaction_record_state #:nodoc:
|
||||
@_start_transaction_state[:id] = id
|
||||
@_start_transaction_state.reverse_merge!(
|
||||
@ -420,18 +420,18 @@ def remember_transaction_record_state #:nodoc:
|
||||
@_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) + 1
|
||||
end
|
||||
|
||||
# Clear the new record state and id of a record.
|
||||
# Clear the new record state and id of a record.
|
||||
def clear_transaction_record_state #:nodoc:
|
||||
@_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
|
||||
force_clear_transaction_record_state if @_start_transaction_state[:level] < 1
|
||||
end
|
||||
|
||||
# Force to clear the transaction record state.
|
||||
# Force to clear the transaction record state.
|
||||
def force_clear_transaction_record_state #:nodoc:
|
||||
@_start_transaction_state.clear
|
||||
end
|
||||
|
||||
# Restore the new record state and id of a record that was previously saved by a call to save_record_state.
|
||||
# Restore the new record state and id of a record that was previously saved by a call to save_record_state.
|
||||
def restore_transaction_record_state(force = false) #:nodoc:
|
||||
unless @_start_transaction_state.empty?
|
||||
transaction_level = (@_start_transaction_state[:level] || 0) - 1
|
||||
@ -449,12 +449,12 @@ def restore_transaction_record_state(force = false) #:nodoc:
|
||||
end
|
||||
end
|
||||
|
||||
# Determine if a record was created or destroyed in a transaction. State should be one of :new_record or :destroyed.
|
||||
# Determine if a record was created or destroyed in a transaction. State should be one of :new_record or :destroyed.
|
||||
def transaction_record_state(state) #:nodoc:
|
||||
@_start_transaction_state[state]
|
||||
end
|
||||
|
||||
# Determine if a transaction included an action for :create, :update, or :destroy. Used in filtering callbacks.
|
||||
# Determine if a transaction included an action for :create, :update, or :destroy. Used in filtering callbacks.
|
||||
def transaction_include_any_action?(actions) #:nodoc:
|
||||
actions.any? do |action|
|
||||
case action
|
||||
@ -478,23 +478,23 @@ def has_transactional_callbacks? # :nodoc:
|
||||
!_rollback_callbacks.empty? || !_commit_callbacks.empty? || !_before_commit_callbacks.empty?
|
||||
end
|
||||
|
||||
# Updates the attributes on this particular Active Record object so that
|
||||
# if it's associated with a transaction, then the state of the Active Record
|
||||
# object will be updated to reflect the current state of the transaction.
|
||||
#
|
||||
# The +@transaction_state+ variable stores the states of the associated
|
||||
# transaction. This relies on the fact that a transaction can only be in
|
||||
# one rollback or commit (otherwise a list of states would be required).
|
||||
# Each Active Record object inside of a transaction carries that transaction's
|
||||
# TransactionState.
|
||||
#
|
||||
# This method checks to see if the ActiveRecord object's state reflects
|
||||
# the TransactionState, and rolls back or commits the Active Record object
|
||||
# as appropriate.
|
||||
#
|
||||
# Since Active Record objects can be inside multiple transactions, this
|
||||
# method recursively goes through the parent of the TransactionState and
|
||||
# checks if the Active Record object reflects the state of the object.
|
||||
# Updates the attributes on this particular Active Record object so that
|
||||
# if it's associated with a transaction, then the state of the Active Record
|
||||
# object will be updated to reflect the current state of the transaction.
|
||||
#
|
||||
# The +@transaction_state+ variable stores the states of the associated
|
||||
# transaction. This relies on the fact that a transaction can only be in
|
||||
# one rollback or commit (otherwise a list of states would be required).
|
||||
# Each Active Record object inside of a transaction carries that transaction's
|
||||
# TransactionState.
|
||||
#
|
||||
# This method checks to see if the ActiveRecord object's state reflects
|
||||
# the TransactionState, and rolls back or commits the Active Record object
|
||||
# as appropriate.
|
||||
#
|
||||
# Since Active Record objects can be inside multiple transactions, this
|
||||
# method recursively goes through the parent of the TransactionState and
|
||||
# checks if the Active Record object reflects the state of the object.
|
||||
def sync_with_transaction_state
|
||||
update_attributes_from_transaction_state(@transaction_state)
|
||||
end
|
||||
|
@ -17,9 +17,9 @@ def create_migration_file
|
||||
protected
|
||||
attr_reader :migration_action, :join_tables
|
||||
|
||||
# Sets the default migration template that is being used for the generation of the migration.
|
||||
# Depending on command line arguments, the migration template and the table name instance
|
||||
# variables are set up.
|
||||
# Sets the default migration template that is being used for the generation of the migration.
|
||||
# Depending on command line arguments, the migration template and the table name instance
|
||||
# variables are set up.
|
||||
def set_local_assigns!
|
||||
@migration_template = "migration.rb"
|
||||
case file_name
|
||||
|
@ -130,19 +130,19 @@ def test_associations_work_with_reserved_words
|
||||
#the following functions were added to DRY test cases
|
||||
|
||||
private
|
||||
# custom fixture loader, uses FixtureSet#create_fixtures and appends base_path to the current file's path
|
||||
# custom fixture loader, uses FixtureSet#create_fixtures and appends base_path to the current file's path
|
||||
def create_test_fixtures(*fixture_names)
|
||||
ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT + "/reserved_words", fixture_names)
|
||||
end
|
||||
|
||||
# custom drop table, uses execute on connection to drop a table if it exists. note: escapes table_name
|
||||
# custom drop table, uses execute on connection to drop a table if it exists. note: escapes table_name
|
||||
def drop_tables_directly(table_names, connection = @connection)
|
||||
table_names.each do |name|
|
||||
connection.drop_table name, if_exists: true
|
||||
end
|
||||
end
|
||||
|
||||
# custom create table, uses execute on connection to create a table, note: escapes table_name, does NOT escape columns
|
||||
# custom create table, uses execute on connection to create a table, note: escapes table_name, does NOT escape columns
|
||||
def create_tables_directly (tables, connection = @connection)
|
||||
tables.each do |table_name, column_properties|
|
||||
connection.execute("CREATE TABLE `#{table_name}` ( #{column_properties} )")
|
||||
|
@ -119,11 +119,11 @@ def get_prepared_statement_cache(connection)
|
||||
.instance_variable_get(:@cache)[Process.pid]
|
||||
end
|
||||
|
||||
# Rails will automatically clear the prepared statements on the connection
|
||||
# that runs the migration, so we use two connections to simulate what would
|
||||
# actually happen on a production system; we'd have one connection running the
|
||||
# migration from the rake task ("ddl_connection" here), and we'd have another
|
||||
# connection in a web worker.
|
||||
# Rails will automatically clear the prepared statements on the connection
|
||||
# that runs the migration, so we use two connections to simulate what would
|
||||
# actually happen on a production system; we'd have one connection running the
|
||||
# migration from the rake task ("ddl_connection" here), and we'd have another
|
||||
# connection in a web worker.
|
||||
def with_two_connections
|
||||
run_without_connection do |original_connection|
|
||||
ActiveRecord::Base.establish_connection(original_connection.merge(pool_size: 2))
|
||||
|
@ -511,9 +511,10 @@ def test_rollback_when_saving_a_frozen_record
|
||||
topic = Topic.new(title: "test")
|
||||
topic.freeze
|
||||
e = assert_raise(RuntimeError) { topic.save }
|
||||
assert_match(/frozen/i, e.message) # Not good enough, but we can't do much
|
||||
# about it since there is no specific error
|
||||
# for frozen objects.
|
||||
# Not good enough, but we can't do much
|
||||
# about it since there is no specific error
|
||||
# for frozen objects.
|
||||
assert_match(/frozen/i, e.message)
|
||||
assert !topic.persisted?, "not persisted"
|
||||
assert_nil topic.id
|
||||
assert topic.frozen?, "not frozen"
|
||||
|
@ -36,7 +36,7 @@ def replied_topic
|
||||
# are used to generate tests to keep things DRY
|
||||
#
|
||||
COMMON_CASES = [
|
||||
# [ case, validation_options, generate_message_options]
|
||||
# [ case, validation_options, generate_message_options]
|
||||
[ "given no options", {}, {}],
|
||||
[ "given custom message", { message: "custom" }, { message: "custom" }],
|
||||
[ "given if condition", { if: lambda { true } }, {}],
|
||||
|
@ -149,7 +149,7 @@ def test_does_not_dump_view_as_table
|
||||
end
|
||||
end
|
||||
|
||||
# sqlite dose not support CREATE, INSERT, and DELETE for VIEW
|
||||
# sqlite dose not support CREATE, INSERT, and DELETE for VIEW
|
||||
if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter)
|
||||
class UpdateableViewTest < ActiveRecord::TestCase
|
||||
self.use_transactional_tests = false
|
||||
|
@ -102,9 +102,9 @@ def __run_callbacks__(callbacks, &block)
|
||||
end
|
||||
end
|
||||
|
||||
# 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.
|
||||
# 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)
|
||||
end
|
||||
|
||||
@ -367,15 +367,15 @@ def invert_lambda(l)
|
||||
lambda { |*args, &blk| !l.call(*args, &blk) }
|
||||
end
|
||||
|
||||
# Filters support:
|
||||
#
|
||||
# Symbols:: A method to call.
|
||||
# Strings:: Some content to evaluate.
|
||||
# Procs:: A proc to call with the object.
|
||||
# Objects:: An object with a <tt>before_foo</tt> method on it to call.
|
||||
#
|
||||
# All of these objects are converted into a lambda and handled
|
||||
# the same after this point.
|
||||
# Filters support:
|
||||
#
|
||||
# Symbols:: A method to call.
|
||||
# Strings:: Some content to evaluate.
|
||||
# Procs:: A proc to call with the object.
|
||||
# Objects:: An object with a <tt>before_foo</tt> method on it to call.
|
||||
#
|
||||
# All of these objects are converted into a lambda and handled
|
||||
# the same after this point.
|
||||
def make_lambda(filter)
|
||||
case filter
|
||||
when Symbol
|
||||
@ -422,9 +422,9 @@ def conditions_lambdas
|
||||
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
|
||||
# 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
|
||||
def initialize(&call)
|
||||
@call = call
|
||||
@ -458,7 +458,7 @@ def call(arg)
|
||||
end
|
||||
end
|
||||
|
||||
# An Array with a compile method.
|
||||
# An Array with a compile method.
|
||||
class CallbackChain #:nodoc:#
|
||||
include Enumerable
|
||||
|
||||
|
@ -199,7 +199,7 @@ def yield_shares(purpose: nil, compatible: [], block_share: false)
|
||||
|
||||
private
|
||||
|
||||
# Must be called within synchronize
|
||||
# Must be called within synchronize
|
||||
def busy_for_exclusive?(purpose)
|
||||
busy_for_sharing?(purpose) ||
|
||||
@sharing.size > (@sharing[Thread.current] > 0 ? 1 : 0)
|
||||
|
@ -84,7 +84,7 @@ def finished?
|
||||
scanner.eos?
|
||||
end
|
||||
|
||||
# Parses number which can be a float with either comma or period.
|
||||
# Parses number which can be a float with either comma or period.
|
||||
def number
|
||||
PERIOD_OR_COMMA.match?(scanner[1]) ? scanner[1].tr(COMMA, PERIOD).to_f : scanner[1].to_i
|
||||
end
|
||||
@ -97,7 +97,7 @@ def raise_parsing_error(reason = nil)
|
||||
raise ParsingError, "Invalid ISO 8601 duration: #{scanner.string.inspect} #{reason}".strip
|
||||
end
|
||||
|
||||
# Checks for various semantic errors as stated in ISO 8601 standard.
|
||||
# Checks for various semantic errors as stated in ISO 8601 standard.
|
||||
def validate!
|
||||
raise_parsing_error("is empty duration") if parts.empty?
|
||||
|
||||
|
@ -105,13 +105,13 @@ def updated_at(paths)
|
||||
@updated_at || max_mtime(paths) || Time.at(0)
|
||||
end
|
||||
|
||||
# This method returns the maximum mtime of the files in +paths+, or +nil+
|
||||
# if the array is empty.
|
||||
#
|
||||
# Files with a mtime in the future are ignored. Such abnormal situation
|
||||
# can happen for example if the user changes the clock by hand. It is
|
||||
# healthy to consider this edge case because with mtimes in the future
|
||||
# reloading is not triggered.
|
||||
# This method returns the maximum mtime of the files in +paths+, or +nil+
|
||||
# if the array is empty.
|
||||
#
|
||||
# Files with a mtime in the future are ignored. Such abnormal situation
|
||||
# can happen for example if the user changes the clock by hand. It is
|
||||
# healthy to consider this edge case because with mtimes in the future
|
||||
# reloading is not triggered.
|
||||
def max_mtime(paths)
|
||||
time_now = Time.now
|
||||
max_mtime = nil
|
||||
|
@ -356,11 +356,11 @@ def ordinalize(number)
|
||||
|
||||
private
|
||||
|
||||
# Mounts a regular expression, returned as a string to ease interpolation,
|
||||
# that will match part by part the given constant.
|
||||
#
|
||||
# const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?"
|
||||
# const_regexp("::") # => "::"
|
||||
# Mounts a regular expression, returned as a string to ease interpolation,
|
||||
# that will match part by part the given constant.
|
||||
#
|
||||
# const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?"
|
||||
# const_regexp("::") # => "::"
|
||||
def const_regexp(camel_cased_word) #:nodoc:
|
||||
parts = camel_cased_word.split("::".freeze)
|
||||
|
||||
@ -373,10 +373,10 @@ def const_regexp(camel_cased_word) #:nodoc:
|
||||
end
|
||||
end
|
||||
|
||||
# Applies inflection rules for +singularize+ and +pluralize+.
|
||||
#
|
||||
# apply_inflections('post', inflections.plurals) # => "posts"
|
||||
# apply_inflections('posts', inflections.singulars) # => "post"
|
||||
# Applies inflection rules for +singularize+ and +pluralize+.
|
||||
#
|
||||
# apply_inflections('post', inflections.plurals) # => "posts"
|
||||
# apply_inflections('posts', inflections.singulars) # => "post"
|
||||
def apply_inflections(word, rules)
|
||||
result = word.to_s.dup
|
||||
|
||||
|
@ -51,8 +51,8 @@ def generate_key(salt)
|
||||
|
||||
private
|
||||
|
||||
# To prevent users from using something insecure like "Password" we make sure that the
|
||||
# secret they've provided is at least 30 characters in length.
|
||||
# To prevent users from using something insecure like "Password" we make sure that the
|
||||
# secret they've provided is at least 30 characters in length.
|
||||
def ensure_secret_secure(secret)
|
||||
if secret.blank?
|
||||
raise ArgumentError, "A secret is required to generate an integrity hash " \
|
||||
|
@ -157,7 +157,7 @@ def _dasherize(key)
|
||||
"#{left}#{middle.tr('_ ', '--')}#{right}"
|
||||
end
|
||||
|
||||
# TODO: Add support for other encodings
|
||||
# TODO: Add support for other encodings
|
||||
def _parse_binary(bin, entity) #:nodoc:
|
||||
case entity["encoding"]
|
||||
when "base64"
|
||||
|
@ -52,12 +52,12 @@ def parse(data)
|
||||
|
||||
private
|
||||
|
||||
# Convert an XML element and merge into the hash
|
||||
#
|
||||
# hash::
|
||||
# Hash to merge the converted element into.
|
||||
# element::
|
||||
# XML element to merge into hash
|
||||
# Convert an XML element and merge into the hash
|
||||
#
|
||||
# hash::
|
||||
# Hash to merge the converted element into.
|
||||
# element::
|
||||
# XML element to merge into hash
|
||||
def merge_element!(hash, element, depth)
|
||||
raise "Document too deep!" if depth == 0
|
||||
delete_empty(hash)
|
||||
@ -68,10 +68,10 @@ def delete_empty(hash)
|
||||
hash.delete(CONTENT_KEY) if hash[CONTENT_KEY] == ""
|
||||
end
|
||||
|
||||
# Actually converts an XML document element into a data structure.
|
||||
#
|
||||
# element::
|
||||
# The document element to be collapsed.
|
||||
# Actually converts an XML document element into a data structure.
|
||||
#
|
||||
# element::
|
||||
# The document element to be collapsed.
|
||||
def collapse(element, depth)
|
||||
hash = get_attributes(element)
|
||||
|
||||
@ -88,12 +88,12 @@ def collapse(element, depth)
|
||||
end
|
||||
end
|
||||
|
||||
# Merge all the texts of an element into the hash
|
||||
#
|
||||
# hash::
|
||||
# Hash to add the converted element to.
|
||||
# element::
|
||||
# XML element whose texts are to me merged into the hash
|
||||
# Merge all the texts of an element into the hash
|
||||
#
|
||||
# hash::
|
||||
# Hash to add the converted element to.
|
||||
# element::
|
||||
# XML element whose texts are to me merged into the hash
|
||||
def merge_texts!(hash, element)
|
||||
delete_empty(hash)
|
||||
text_children = texts(element)
|
||||
@ -105,17 +105,17 @@ def merge_texts!(hash, element)
|
||||
end
|
||||
end
|
||||
|
||||
# Adds a new key/value pair to an existing Hash. If the key to be added
|
||||
# already exists and the existing value associated with key is not
|
||||
# an Array, it will be wrapped in an Array. Then the new value is
|
||||
# appended to that Array.
|
||||
#
|
||||
# hash::
|
||||
# Hash to add key/value pair to.
|
||||
# key::
|
||||
# Key to be added.
|
||||
# value::
|
||||
# Value to be associated with key.
|
||||
# Adds a new key/value pair to an existing Hash. If the key to be added
|
||||
# already exists and the existing value associated with key is not
|
||||
# an Array, it will be wrapped in an Array. Then the new value is
|
||||
# appended to that Array.
|
||||
#
|
||||
# hash::
|
||||
# Hash to add key/value pair to.
|
||||
# key::
|
||||
# Key to be added.
|
||||
# value::
|
||||
# Value to be associated with key.
|
||||
def merge!(hash, key, value)
|
||||
if hash.has_key?(key)
|
||||
if hash[key].instance_of?(Array)
|
||||
@ -131,11 +131,11 @@ def merge!(hash, key, value)
|
||||
hash
|
||||
end
|
||||
|
||||
# Converts the attributes array of an XML element into a hash.
|
||||
# Returns an empty Hash if node has no attributes.
|
||||
#
|
||||
# element::
|
||||
# XML element to extract attributes from.
|
||||
# Converts the attributes array of an XML element into a hash.
|
||||
# Returns an empty Hash if node has no attributes.
|
||||
#
|
||||
# element::
|
||||
# XML element to extract attributes from.
|
||||
def get_attributes(element)
|
||||
attribute_hash = {}
|
||||
attributes = element.attributes
|
||||
@ -146,10 +146,10 @@ def get_attributes(element)
|
||||
attribute_hash
|
||||
end
|
||||
|
||||
# Determines if a document element has text content
|
||||
#
|
||||
# element::
|
||||
# XML element to be checked.
|
||||
# Determines if a document element has text content
|
||||
#
|
||||
# element::
|
||||
# XML element to be checked.
|
||||
def texts(element)
|
||||
texts = []
|
||||
child_nodes = element.child_nodes
|
||||
@ -162,10 +162,10 @@ def texts(element)
|
||||
texts
|
||||
end
|
||||
|
||||
# Determines if a document element has text content
|
||||
#
|
||||
# element::
|
||||
# XML element to be checked.
|
||||
# Determines if a document element has text content
|
||||
#
|
||||
# element::
|
||||
# XML element to be checked.
|
||||
def empty_content?(element)
|
||||
text = ""
|
||||
child_nodes = element.child_nodes
|
||||
|
@ -245,12 +245,12 @@ def test_tableize
|
||||
end
|
||||
end
|
||||
|
||||
# FIXME: get following tests to pass on jruby, currently skipped
|
||||
#
|
||||
# Currently this fails because ActiveSupport::Multibyte::Unicode#tidy_bytes
|
||||
# required a specific Encoding::Converter(UTF-8 to UTF8-MAC) which unavailable on JRuby
|
||||
# causing our tests to error out.
|
||||
# related bug http://jira.codehaus.org/browse/JRUBY-7194
|
||||
# FIXME: get following tests to pass on jruby, currently skipped
|
||||
#
|
||||
# Currently this fails because ActiveSupport::Multibyte::Unicode#tidy_bytes
|
||||
# required a specific Encoding::Converter(UTF-8 to UTF8-MAC) which unavailable on JRuby
|
||||
# causing our tests to error out.
|
||||
# related bug http://jira.codehaus.org/browse/JRUBY-7194
|
||||
def test_parameterize
|
||||
jruby_skip "UTF-8 to UTF8-MAC Converter is unavailable"
|
||||
StringToParameterized.each do |some_string, parameterized_string|
|
||||
|
Loading…
Reference in New Issue
Block a user