From 3464cd5c288323ca115a4929d1e6b435c4afc8d4 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Wed, 14 Sep 2016 17:57:52 +0900 Subject: 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. --- .rubocop.yml | 4 + actionmailer/lib/action_mailer/base.rb | 30 +- actionpack/lib/abstract_controller/helpers.rb | 12 +- .../metal/etag_with_template_digest.rb | 8 +- actionpack/lib/action_controller/metal/helpers.rb | 2 +- .../lib/action_controller/metal/params_wrapper.rb | 14 +- .../lib/action_controller/metal/rendering.rb | 6 +- actionpack/lib/action_dispatch/http/headers.rb | 4 +- actionpack/lib/action_dispatch/journey/parser.rb | 22 +- .../lib/action_dispatch/routing/route_set.rb | 26 +- actionpack/test/abstract_unit.rb | 2 +- .../controller/new_base/render_context_test.rb | 2 +- .../action_view/helpers/tags/collection_helpers.rb | 4 +- actionview/lib/action_view/helpers/tags/select.rb | 8 +- actionview/lib/action_view/layouts.rb | 22 +- .../lib/action_view/renderer/partial_renderer.rb | 28 +- .../renderer/streaming_template_renderer.rb | 4 +- .../lib/action_view/renderer/template_renderer.rb | 12 +- actionview/lib/action_view/template/resolver.rb | 10 +- actionview/lib/action_view/view_paths.rb | 4 +- actionview/test/abstract_unit.rb | 2 +- .../abstract/abstract_controller_test.rb | 2 +- .../test/activerecord/polymorphic_routes_test.rb | 2 +- activemodel/lib/active_model/type/date_time.rb | 4 +- .../lib/active_model/type/helpers/time_value.rb | 2 +- activemodel/lib/active_model/type/value.rb | 6 +- .../test/cases/validations/i18n_validation_test.rb | 2 +- activerecord/lib/active_record/aggregations.rb | 310 ++-- activerecord/lib/active_record/associations.rb | 1752 ++++++++++---------- .../lib/active_record/associations/preloader.rb | 34 +- .../lib/active_record/attribute_assignment.rb | 14 +- .../lib/active_record/attribute_methods.rb | 10 +- .../attribute_methods/before_type_cast.rb | 2 +- .../lib/active_record/attribute_methods/read.rb | 36 +- .../lib/active_record/attribute_methods/write.rb | 2 +- .../abstract/connection_pool.rb | 84 +- .../connection_adapters/abstract/query_cache.rb | 4 +- .../connection_adapters/abstract_mysql_adapter.rb | 4 +- .../connection_specification.rb | 92 +- .../connection_adapters/postgresql_adapter.rb | 8 +- activerecord/lib/active_record/dynamic_matchers.rb | 8 +- activerecord/lib/active_record/inheritance.rb | 24 +- .../lib/active_record/locking/optimistic.rb | 8 +- activerecord/lib/active_record/migration.rb | 10 +- .../active_record/migration/command_recorder.rb | 2 +- activerecord/lib/active_record/model_schema.rb | 4 +- .../lib/active_record/nested_attributes.rb | 142 +- .../lib/active_record/relation/calculations.rb | 14 +- .../lib/active_record/relation/query_methods.rb | 32 +- activerecord/lib/active_record/sanitization.rb | 160 +- activerecord/lib/active_record/scoping/default.rb | 88 +- activerecord/lib/active_record/transactions.rb | 46 +- .../active_record/migration/migration_generator.rb | 6 +- .../cases/adapters/mysql2/reserved_word_test.rb | 6 +- activerecord/test/cases/hot_compatibility_test.rb | 10 +- activerecord/test/cases/transactions_test.rb | 7 +- .../test/cases/validations/i18n_validation_test.rb | 2 +- activerecord/test/cases/view_test.rb | 2 +- activesupport/lib/active_support/callbacks.rb | 32 +- .../lib/active_support/concurrency/share_lock.rb | 2 +- .../lib/active_support/duration/iso8601_parser.rb | 4 +- .../lib/active_support/file_update_checker.rb | 14 +- .../lib/active_support/inflector/methods.rb | 18 +- activesupport/lib/active_support/key_generator.rb | 4 +- activesupport/lib/active_support/xml_mini.rb | 2 +- activesupport/lib/active_support/xml_mini/jdom.rb | 80 +- activesupport/test/inflector_test.rb | 12 +- 67 files changed, 1670 insertions(+), 1665 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 8f619b7956..139e6a7efb 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -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 diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index fb6ff819c9..e9966c7ff5 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -832,15 +832,15 @@ module ActionMailer 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 @@ module ActionMailer end end - # Translates the +subject+ using Rails I18n class under [mailer_scope, action_name] scope. - # If it does not find a translation for the +subject+ under the specified scope it will default to a - # humanized version of the action_name. - # If the subject has interpolations, you can pass them through the +interpolations+ parameter. + # Translates the +subject+ using Rails I18n class under [mailer_scope, action_name] scope. + # If it does not find a translation for the +subject+ under the specified scope it will default to a + # humanized version of the action_name. + # 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 @@ module ActionMailer 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, diff --git a/actionpack/lib/abstract_controller/helpers.rb b/actionpack/lib/abstract_controller/helpers.rb index cb76898f59..ef3be7af83 100644 --- a/actionpack/lib/abstract_controller/helpers.rb +++ b/actionpack/lib/abstract_controller/helpers.rb @@ -171,12 +171,12 @@ module AbstractController end private - # Makes all the (instance) methods in the helper module available to templates - # rendered through this controller. - # - # ==== Parameters - # * module - 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 + # * module - The module to include into the current helper module + # for the class def add_template_helper(mod) _helpers.module_eval { include mod } end diff --git a/actionpack/lib/action_controller/metal/etag_with_template_digest.rb b/actionpack/lib/action_controller/metal/etag_with_template_digest.rb index 49b5f1090e..6c103bb042 100644 --- a/actionpack/lib/action_controller/metal/etag_with_template_digest.rb +++ b/actionpack/lib/action_controller/metal/etag_with_template_digest.rb @@ -39,10 +39,10 @@ module ActionController 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}" diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb index 7257bbfa95..476d081239 100644 --- a/actionpack/lib/action_controller/metal/helpers.rb +++ b/actionpack/lib/action_controller/metal/helpers.rb @@ -108,7 +108,7 @@ module ActionController end private - # Extract helper names from files in app/helpers/**/*_helper.rb + # Extract helper names from files in app/helpers/**/*_helper.rb def all_application_helpers all_helpers_from_path(helpers_path) end diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb index 745f449a05..9d1b740025 100644 --- a/actionpack/lib/action_controller/metal/params_wrapper.rb +++ b/actionpack/lib/action_controller/metal/params_wrapper.rb @@ -128,13 +128,13 @@ module ActionController 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 diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb index ac17d61b96..46ddf48926 100644 --- a/actionpack/lib/action_controller/metal/rendering.rb +++ b/actionpack/lib/action_controller/metal/rendering.rb @@ -72,14 +72,14 @@ module ActionController 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 @@ module ActionController 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) diff --git a/actionpack/lib/action_dispatch/http/headers.rb b/actionpack/lib/action_dispatch/http/headers.rb index 0528a6ad08..3c03976f03 100644 --- a/actionpack/lib/action_dispatch/http/headers.rb +++ b/actionpack/lib/action_dispatch/http/headers.rb @@ -115,8 +115,8 @@ module ActionDispatch 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 diff --git a/actionpack/lib/action_dispatch/journey/parser.rb b/actionpack/lib/action_dispatch/journey/parser.rb index 7ec9d63859..01ff2109cb 100644 --- a/actionpack/lib/action_dispatch/journey/parser.rb +++ b/actionpack/lib/action_dispatch/journey/parser.rb @@ -10,7 +10,7 @@ require "action_dispatch/journey/parser_extras" 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 @@ module ActionDispatch 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 @@ module ActionDispatch 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 @@ module ActionDispatch 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("/") diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 8d0bf680c8..5abf59402d 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -264,19 +264,19 @@ module ActionDispatch 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 diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index c5b2493e06..6d28753947 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -246,7 +246,7 @@ module ActionDispatch class DebugExceptions private remove_method :stderr_logger - # Silence logger + # Silence logger def stderr_logger nil end diff --git a/actionpack/test/controller/new_base/render_context_test.rb b/actionpack/test/controller/new_base/render_context_test.rb index 40149a41d3..b64468a94e 100644 --- a/actionpack/test/controller/new_base/render_context_test.rb +++ b/actionpack/test/controller/new_base/render_context_test.rb @@ -28,7 +28,7 @@ module RenderContext protected - # 3) Set view_context to self + # 3) Set view_context to self def view_context self end diff --git a/actionview/lib/action_view/helpers/tags/collection_helpers.rb b/actionview/lib/action_view/helpers/tags/collection_helpers.rb index 70d7c484eb..36575b2fd0 100644 --- a/actionview/lib/action_view/helpers/tags/collection_helpers.rb +++ b/actionview/lib/action_view/helpers/tags/collection_helpers.rb @@ -41,8 +41,8 @@ module ActionView 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 diff --git a/actionview/lib/action_view/helpers/tags/select.rb b/actionview/lib/action_view/helpers/tags/select.rb index 8cc34e3180..667c7e945a 100644 --- a/actionview/lib/action_view/helpers/tags/select.rb +++ b/actionview/lib/action_view/helpers/tags/select.rb @@ -28,10 +28,10 @@ module ActionView 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 diff --git a/actionview/lib/action_view/layouts.rb b/actionview/lib/action_view/layouts.rb index dd5f6e7300..344893f41a 100644 --- a/actionview/lib/action_view/layouts.rb +++ b/actionview/lib/action_view/layouts.rb @@ -224,12 +224,12 @@ module ActionView 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 layout method. - # - # ==== Returns - # * Boolean - 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 layout method. + # + # ==== Returns + # * Boolean - True if the action has a layout definition, false otherwise. def _conditional_layout? return unless super @@ -334,11 +334,11 @@ module ActionView private - # If no layout is supplied, look for a template named the return - # value of this method. - # - # ==== Returns - # * String - A template name + # If no layout is supplied, look for a template named the return + # value of this method. + # + # ==== Returns + # * String - A template name def _implied_layout_name # :nodoc: controller_path end diff --git a/actionview/lib/action_view/renderer/partial_renderer.rb b/actionview/lib/action_view/renderer/partial_renderer.rb index 4b6aecd187..dfe38c488f 100644 --- a/actionview/lib/action_view/renderer/partial_renderer.rb +++ b/actionview/lib/action_view/renderer/partial_renderer.rb @@ -350,13 +350,13 @@ module ActionView 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 @@ module ActionView 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) diff --git a/actionview/lib/action_view/renderer/streaming_template_renderer.rb b/actionview/lib/action_view/renderer/streaming_template_renderer.rb index 0323ebef6a..2434250b2d 100644 --- a/actionview/lib/action_view/renderer/streaming_template_renderer.rb +++ b/actionview/lib/action_view/renderer/streaming_template_renderer.rb @@ -27,8 +27,8 @@ module ActionView 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 diff --git a/actionview/lib/action_view/renderer/template_renderer.rb b/actionview/lib/action_view/renderer/template_renderer.rb index 331a5ea228..4bcf009e27 100644 --- a/actionview/lib/action_view/renderer/template_renderer.rb +++ b/actionview/lib/action_view/renderer/template_renderer.rb @@ -16,7 +16,7 @@ module ActionView 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 @@ module ActionView 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 @@ module ActionView 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 diff --git a/actionview/lib/action_view/template/resolver.rb b/actionview/lib/action_view/template/resolver.rb index 20c2d5c782..5a2948d5a9 100644 --- a/actionview/lib/action_view/template/resolver.rb +++ b/actionview/lib/action_view/template/resolver.rb @@ -256,7 +256,7 @@ module ActionView 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 @@ module ActionView 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 diff --git a/actionview/lib/action_view/view_paths.rb b/actionview/lib/action_view/view_paths.rb index 2ed62ca88f..b5cde5b43f 100644 --- a/actionview/lib/action_view/view_paths.rb +++ b/actionview/lib/action_view/view_paths.rb @@ -22,8 +22,8 @@ module ActionView 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' ._prefixes. + # 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' ._prefixes. def local_prefixes [controller_path] end diff --git a/actionview/test/abstract_unit.rb b/actionview/test/abstract_unit.rb index 3bb8d21e86..88c7189d22 100644 --- a/actionview/test/abstract_unit.rb +++ b/actionview/test/abstract_unit.rb @@ -264,7 +264,7 @@ module ActionDispatch class DebugExceptions private remove_method :stderr_logger - # Silence logger + # Silence logger def stderr_logger nil end diff --git a/actionview/test/actionpack/abstract/abstract_controller_test.rb b/actionview/test/actionpack/abstract/abstract_controller_test.rb index 863efd15fa..a2cd3deb58 100644 --- a/actionview/test/actionpack/abstract/abstract_controller_test.rb +++ b/actionview/test/actionpack/abstract/abstract_controller_test.rb @@ -227,7 +227,7 @@ module AbstractController end class ActionMissingRespondToActionController < AbstractController::Base - # No actions + # No actions private def action_missing(action_name) self.response_body = "success" diff --git a/actionview/test/activerecord/polymorphic_routes_test.rb b/actionview/test/activerecord/polymorphic_routes_test.rb index 952ed72aa9..8495949975 100644 --- a/actionview/test/activerecord/polymorphic_routes_test.rb +++ b/actionview/test/activerecord/polymorphic_routes_test.rb @@ -598,7 +598,7 @@ class PolymorphicRoutesTest < ActionController::TestCase end end - # Tests for uncountable names + # Tests for uncountable names def test_uncountable_resource with_test_routes do @series.save diff --git a/activemodel/lib/active_model/type/date_time.rb b/activemodel/lib/active_model/type/date_time.rb index f88c9e5071..5cb0077e45 100644 --- a/activemodel/lib/active_model/type/date_time.rb +++ b/activemodel/lib/active_model/type/date_time.rb @@ -19,8 +19,8 @@ module ActiveModel 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 diff --git a/activemodel/lib/active_model/type/helpers/time_value.rb b/activemodel/lib/active_model/type/helpers/time_value.rb index 64780ebbdc..d8c5d929a3 100644 --- a/activemodel/lib/active_model/type/helpers/time_value.rb +++ b/activemodel/lib/active_model/type/helpers/time_value.rb @@ -64,7 +64,7 @@ module ActiveModel 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 diff --git a/activemodel/lib/active_model/type/value.rb b/activemodel/lib/active_model/type/value.rb index 4d3a1b29d6..7e9ae92245 100644 --- a/activemodel/lib/active_model/type/value.rb +++ b/activemodel/lib/active_model/type/value.rb @@ -105,9 +105,9 @@ module ActiveModel 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 diff --git a/activemodel/test/cases/validations/i18n_validation_test.rb b/activemodel/test/cases/validations/i18n_validation_test.rb index 85212a80fc..f28cfc0ef5 100644 --- a/activemodel/test/cases/validations/i18n_validation_test.rb +++ b/activemodel/test/cases/validations/i18n_validation_test.rb @@ -46,7 +46,7 @@ class I18nValidationTest < ActiveModel::TestCase # 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 } }, {}], diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb index 8979b13286..5ca8fe576e 100644 --- a/activerecord/lib/active_record/aggregations.rb +++ b/activerecord/lib/active_record/aggregations.rb @@ -24,161 +24,161 @@ module ActiveRecord 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: - # * Customer#balance, Customer#balance=(money) - # * Customer#address, Customer#address=(address) - # - # 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 == and <=> 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 - # Money#exchange_to 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 new constructor of the value - # class passing each of the mapped attributes, in the order specified by the :mapping - # 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 :constructor and :converter 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: + # * Customer#balance, Customer#balance=(money) + # * Customer#address, Customer#address=(address) + # + # 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 == and <=> 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 + # Money#exchange_to 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 new constructor of the value + # class passing each of the mapped attributes, in the order specified by the :mapping + # 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 :constructor and :converter 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: # composed_of :address adds address and address=(new_address) methods. diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index dc6fe1640e..b5f1f1980a 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -274,882 +274,882 @@ module ActiveRecord @association_cache[name] = association end - # \Associations are a set of macro-like class methods for tying objects together through - # foreign keys. They express relationships like "Project has one Project Manager" - # or "Project belongs to a Portfolio". Each macro adds a number of methods to the - # class which are specialized according to the collection or association symbol and the - # options hash. It works much the same way as Ruby's own attr* - # methods. - # - # class Project < ActiveRecord::Base - # belongs_to :portfolio - # has_one :project_manager - # has_many :milestones - # has_and_belongs_to_many :categories - # end - # - # The project class now has the following methods (and more) to ease the traversal and - # manipulation of its relationships: - # * Project#portfolio, Project#portfolio=(portfolio), Project#portfolio.nil? - # * Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?, - # * Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone), - # Project#milestones.delete(milestone), Project#milestones.destroy(milestone), Project#milestones.find(milestone_id), - # Project#milestones.build, Project#milestones.create - # * Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1), - # Project#categories.delete(category1), Project#categories.destroy(category1) - # - # === A word of warning - # - # Don't create associations that have the same name as {instance methods}[rdoc-ref:ActiveRecord::Core] of - # ActiveRecord::Base. Since the association adds a method with that name to - # its model, using an association with the same name as one provided by ActiveRecord::Base will override the method inherited through ActiveRecord::Base and will break things. - # For instance, +attributes+ and +connection+ would be bad choices for association names, because those names already exist in the list of ActiveRecord::Base instance methods. - # - # == Auto-generated methods - # See also Instance Public methods below for more details. - # - # === Singular associations (one-to-one) - # | | belongs_to | - # generated methods | belongs_to | :polymorphic | has_one - # ----------------------------------+------------+--------------+--------- - # other(force_reload=false) | X | X | X - # other=(other) | X | X | X - # build_other(attributes={}) | X | | X - # create_other(attributes={}) | X | | X - # create_other!(attributes={}) | X | | X - # - # === Collection associations (one-to-many / many-to-many) - # | | | has_many - # generated methods | habtm | has_many | :through - # ----------------------------------+-------+----------+---------- - # others(force_reload=false) | X | X | X - # others=(other,other,...) | X | X | X - # other_ids | X | X | X - # other_ids=(id,id,...) | X | X | X - # others<< | X | X | X - # others.push | X | X | X - # others.concat | X | X | X - # others.build(attributes={}) | X | X | X - # others.create(attributes={}) | X | X | X - # others.create!(attributes={}) | X | X | X - # others.size | X | X | X - # others.length | X | X | X - # others.count | X | X | X - # others.sum(*args) | X | X | X - # others.empty? | X | X | X - # others.clear | X | X | X - # others.delete(other,other,...) | X | X | X - # others.delete_all | X | X | X - # others.destroy(other,other,...) | X | X | X - # others.destroy_all | X | X | X - # others.find(*args) | X | X | X - # others.exists? | X | X | X - # others.distinct | X | X | X - # others.reset | X | X | X - # - # === Overriding generated methods - # - # Association methods are generated in a module that is included into the model class, - # which allows you to easily override with your own methods and call the original - # generated method with +super+. For example: - # - # class Car < ActiveRecord::Base - # belongs_to :owner - # belongs_to :old_owner - # def owner=(new_owner) - # self.old_owner = self.owner - # super - # end - # end - # - # If your model class is Project, then the module is - # named Project::GeneratedAssociationMethods. The +GeneratedAssociationMethods+ module is - # included in the model class immediately after the (anonymous) generated attributes methods - # module, meaning an association will override the methods for an attribute with the same name. - # - # == Cardinality and associations - # - # Active Record associations can be used to describe one-to-one, one-to-many and many-to-many - # relationships between models. Each model uses an association to describe its role in - # the relation. The #belongs_to association is always used in the model that has - # the foreign key. - # - # === One-to-one - # - # Use #has_one in the base, and #belongs_to in the associated model. - # - # class Employee < ActiveRecord::Base - # has_one :office - # end - # class Office < ActiveRecord::Base - # belongs_to :employee # foreign key - employee_id - # end - # - # === One-to-many - # - # Use #has_many in the base, and #belongs_to in the associated model. - # - # class Manager < ActiveRecord::Base - # has_many :employees - # end - # class Employee < ActiveRecord::Base - # belongs_to :manager # foreign key - manager_id - # end - # - # === Many-to-many - # - # There are two ways to build a many-to-many relationship. - # - # The first way uses a #has_many association with the :through option and a join model, so - # there are two stages of associations. - # - # class Assignment < ActiveRecord::Base - # belongs_to :programmer # foreign key - programmer_id - # belongs_to :project # foreign key - project_id - # end - # class Programmer < ActiveRecord::Base - # has_many :assignments - # has_many :projects, through: :assignments - # end - # class Project < ActiveRecord::Base - # has_many :assignments - # has_many :programmers, through: :assignments - # end - # - # For the second way, use #has_and_belongs_to_many in both models. This requires a join table - # that has no corresponding model or primary key. - # - # class Programmer < ActiveRecord::Base - # has_and_belongs_to_many :projects # foreign keys in the join table - # end - # class Project < ActiveRecord::Base - # has_and_belongs_to_many :programmers # foreign keys in the join table - # end - # - # Choosing which way to build a many-to-many relationship is not always simple. - # If you need to work with the relationship model as its own entity, - # use #has_many :through. Use #has_and_belongs_to_many when working with legacy schemas or when - # you never work directly with the relationship itself. - # - # == Is it a #belongs_to or #has_one association? - # - # Both express a 1-1 relationship. The difference is mostly where to place the foreign - # key, which goes on the table for the class declaring the #belongs_to relationship. - # - # class User < ActiveRecord::Base - # # I reference an account. - # belongs_to :account - # end - # - # class Account < ActiveRecord::Base - # # One user references me. - # has_one :user - # end - # - # The tables for these classes could look something like: - # - # CREATE TABLE users ( - # id int NOT NULL auto_increment, - # account_id int default NULL, - # name varchar default NULL, - # PRIMARY KEY (id) - # ) - # - # CREATE TABLE accounts ( - # id int NOT NULL auto_increment, - # name varchar default NULL, - # PRIMARY KEY (id) - # ) - # - # == Unsaved objects and associations - # - # You can manipulate objects and associations before they are saved to the database, but - # there is some special behavior you should be aware of, mostly involving the saving of - # associated objects. - # - # You can set the :autosave option on a #has_one, #belongs_to, - # #has_many, or #has_and_belongs_to_many association. Setting it - # to +true+ will _always_ save the members, whereas setting it to +false+ will - # _never_ save the members. More details about :autosave option is available at - # AutosaveAssociation. - # - # === One-to-one associations - # - # * Assigning an object to a #has_one association automatically saves that object and - # the object being replaced (if there is one), in order to update their foreign - # keys - except if the parent object is unsaved (new_record? == true). - # * If either of these saves fail (due to one of the objects being invalid), an - # ActiveRecord::RecordNotSaved exception is raised and the assignment is - # cancelled. - # * If you wish to assign an object to a #has_one association without saving it, - # use the #build_association method (documented below). The object being - # replaced will still be saved to update its foreign key. - # * Assigning an object to a #belongs_to association does not save the object, since - # the foreign key field belongs on the parent. It does not save the parent either. - # - # === Collections - # - # * Adding an object to a collection (#has_many or #has_and_belongs_to_many) automatically - # saves that object, except if the parent object (the owner of the collection) is not yet - # stored in the database. - # * If saving any of the objects being added to a collection (via push or similar) - # fails, then push returns +false+. - # * If saving fails while replacing the collection (via association=), an - # ActiveRecord::RecordNotSaved exception is raised and the assignment is - # cancelled. - # * You can add an object to a collection without automatically saving it by using the - # collection.build method (documented below). - # * All unsaved (new_record? == true) members of the collection are automatically - # saved when the parent is saved. - # - # == Customizing the query - # - # \Associations are built from Relation objects, and you can use the Relation syntax - # to customize them. For example, to add a condition: - # - # class Blog < ActiveRecord::Base - # has_many :published_posts, -> { where(published: true) }, class_name: 'Post' - # end - # - # Inside the -> { ... } block you can use all of the usual Relation methods. - # - # === Accessing the owner object - # - # Sometimes it is useful to have access to the owner object when building the query. The owner - # is passed as a parameter to the block. For example, the following association would find all - # events that occur on the user's birthday: - # - # class User < ActiveRecord::Base - # has_many :birthday_events, ->(user) { where(starts_on: user.birthday) }, class_name: 'Event' - # end - # - # Note: Joining, eager loading and preloading of these associations is not fully possible. - # These operations happen before instance creation and the scope will be called with a +nil+ argument. - # This can lead to unexpected behavior and is deprecated. - # - # == Association callbacks - # - # Similar to the normal callbacks that hook into the life cycle of an Active Record object, - # you can also define callbacks that get triggered when you add an object to or remove an - # object from an association collection. - # - # class Project - # has_and_belongs_to_many :developers, after_add: :evaluate_velocity - # - # def evaluate_velocity(developer) - # ... - # end - # end - # - # It's possible to stack callbacks by passing them as an array. Example: - # - # class Project - # has_and_belongs_to_many :developers, - # after_add: [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}] - # end - # - # Possible callbacks are: +before_add+, +after_add+, +before_remove+ and +after_remove+. - # - # If any of the +before_add+ callbacks throw an exception, the object will not be - # added to the collection. - # - # Similarly, if any of the +before_remove+ callbacks throw an exception, the object - # will not be removed from the collection. - # - # == Association extensions - # - # The proxy objects that control the access to associations can be extended through anonymous - # modules. This is especially beneficial for adding new finders, creators, and other - # factory-type methods that are only used as part of this association. - # - # class Account < ActiveRecord::Base - # has_many :people do - # def find_or_create_by_name(name) - # first_name, last_name = name.split(" ", 2) - # find_or_create_by(first_name: first_name, last_name: last_name) - # end - # end - # end - # - # person = Account.first.people.find_or_create_by_name("David Heinemeier Hansson") - # person.first_name # => "David" - # person.last_name # => "Heinemeier Hansson" - # - # If you need to share the same extensions between many associations, you can use a named - # extension module. - # - # module FindOrCreateByNameExtension - # def find_or_create_by_name(name) - # first_name, last_name = name.split(" ", 2) - # find_or_create_by(first_name: first_name, last_name: last_name) - # end - # end - # - # class Account < ActiveRecord::Base - # has_many :people, -> { extending FindOrCreateByNameExtension } - # end - # - # class Company < ActiveRecord::Base - # has_many :people, -> { extending FindOrCreateByNameExtension } - # end - # - # Some extensions can only be made to work with knowledge of the association's internals. - # Extensions can access relevant state using the following methods (where +items+ is the - # name of the association): - # - # * record.association(:items).owner - Returns the object the association is part of. - # * record.association(:items).reflection - Returns the reflection object that describes the association. - # * record.association(:items).target - Returns the associated object for #belongs_to and #has_one, or - # the collection of associated objects for #has_many and #has_and_belongs_to_many. - # - # However, inside the actual extension code, you will not have access to the record as - # above. In this case, you can access proxy_association. For example, - # record.association(:items) and record.items.proxy_association will return - # the same object, allowing you to make calls like proxy_association.owner inside - # association extensions. - # - # == Association Join Models - # - # Has Many associations can be configured with the :through option to use an - # explicit join model to retrieve the data. This operates similarly to a - # #has_and_belongs_to_many association. The advantage is that you're able to add validations, - # callbacks, and extra attributes on the join model. Consider the following schema: - # - # class Author < ActiveRecord::Base - # has_many :authorships - # has_many :books, through: :authorships - # end - # - # class Authorship < ActiveRecord::Base - # belongs_to :author - # belongs_to :book - # end - # - # @author = Author.first - # @author.authorships.collect { |a| a.book } # selects all books that the author's authorships belong to - # @author.books # selects all books by using the Authorship join model - # - # You can also go through a #has_many association on the join model: - # - # class Firm < ActiveRecord::Base - # has_many :clients - # has_many :invoices, through: :clients - # end - # - # class Client < ActiveRecord::Base - # belongs_to :firm - # has_many :invoices - # end - # - # class Invoice < ActiveRecord::Base - # belongs_to :client - # end - # - # @firm = Firm.first - # @firm.clients.flat_map { |c| c.invoices } # select all invoices for all clients of the firm - # @firm.invoices # selects all invoices by going through the Client join model - # - # Similarly you can go through a #has_one association on the join model: - # - # class Group < ActiveRecord::Base - # has_many :users - # has_many :avatars, through: :users - # end - # - # class User < ActiveRecord::Base - # belongs_to :group - # has_one :avatar - # end - # - # class Avatar < ActiveRecord::Base - # belongs_to :user - # end - # - # @group = Group.first - # @group.users.collect { |u| u.avatar }.compact # select all avatars for all users in the group - # @group.avatars # selects all avatars by going through the User join model. - # - # An important caveat with going through #has_one or #has_many associations on the - # join model is that these associations are *read-only*. For example, the following - # would not work following the previous example: - # - # @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around - # @group.avatars.delete(@group.avatars.last) # so would this - # - # == Setting Inverses - # - # If you are using a #belongs_to on the join model, it is a good idea to set the - # :inverse_of option on the #belongs_to, which will mean that the following example - # works correctly (where tags is a #has_many :through association): - # - # @post = Post.first - # @tag = @post.tags.build name: "ruby" - # @tag.save - # - # The last line ought to save the through record (a Tagging). This will only work if the - # :inverse_of is set: - # - # class Tagging < ActiveRecord::Base - # belongs_to :post - # belongs_to :tag, inverse_of: :taggings - # end - # - # If you do not set the :inverse_of record, the association will - # do its best to match itself up with the correct inverse. Automatic - # inverse detection only works on #has_many, #has_one, and - # #belongs_to associations. - # - # Extra options on the associations, as defined in the - # AssociationReflection::INVALID_AUTOMATIC_INVERSE_OPTIONS constant, will - # also prevent the association's inverse from being found automatically. - # - # The automatic guessing of the inverse association uses a heuristic based - # on the name of the class, so it may not work for all associations, - # especially the ones with non-standard names. - # - # You can turn off the automatic detection of inverse associations by setting - # the :inverse_of option to false like so: - # - # class Tagging < ActiveRecord::Base - # belongs_to :tag, inverse_of: false - # end - # - # == Nested \Associations - # - # You can actually specify *any* association with the :through option, including an - # association which has a :through option itself. For example: - # - # class Author < ActiveRecord::Base - # has_many :posts - # has_many :comments, through: :posts - # has_many :commenters, through: :comments - # end - # - # class Post < ActiveRecord::Base - # has_many :comments - # end - # - # class Comment < ActiveRecord::Base - # belongs_to :commenter - # end - # - # @author = Author.first - # @author.commenters # => People who commented on posts written by the author - # - # An equivalent way of setting up this association this would be: - # - # class Author < ActiveRecord::Base - # has_many :posts - # has_many :commenters, through: :posts - # end - # - # class Post < ActiveRecord::Base - # has_many :comments - # has_many :commenters, through: :comments - # end - # - # class Comment < ActiveRecord::Base - # belongs_to :commenter - # end - # - # When using a nested association, you will not be able to modify the association because there - # is not enough information to know what modification to make. For example, if you tried to - # add a Commenter in the example above, there would be no way to tell how to set up the - # intermediate Post and Comment objects. - # - # == Polymorphic \Associations - # - # Polymorphic associations on models are not restricted on what types of models they - # can be associated with. Rather, they specify an interface that a #has_many association - # must adhere to. - # - # class Asset < ActiveRecord::Base - # belongs_to :attachable, polymorphic: true - # end - # - # class Post < ActiveRecord::Base - # has_many :assets, as: :attachable # The :as option specifies the polymorphic interface to use. - # end - # - # @asset.attachable = @post - # - # This works by using a type column in addition to a foreign key to specify the associated - # record. In the Asset example, you'd need an +attachable_id+ integer column and an - # +attachable_type+ string column. - # - # Using polymorphic associations in combination with single table inheritance (STI) is - # a little tricky. In order for the associations to work as expected, ensure that you - # store the base model for the STI models in the type column of the polymorphic - # association. To continue with the asset example above, suppose there are guest posts - # and member posts that use the posts table for STI. In this case, there must be a +type+ - # column in the posts table. - # - # Note: The attachable_type= method is being called when assigning an +attachable+. - # The +class_name+ of the +attachable+ is passed as a String. - # - # class Asset < ActiveRecord::Base - # belongs_to :attachable, polymorphic: true - # - # def attachable_type=(class_name) - # super(class_name.constantize.base_class.to_s) - # end - # end - # - # class Post < ActiveRecord::Base - # # because we store "Post" in attachable_type now dependent: :destroy will work - # has_many :assets, as: :attachable, dependent: :destroy - # end - # - # class GuestPost < Post - # end - # - # class MemberPost < Post - # end - # - # == Caching - # - # All of the methods are built on a simple caching principle that will keep the result - # of the last query around unless specifically instructed not to. The cache is even - # shared across methods to make it even cheaper to use the macro-added methods without - # worrying too much about performance at the first go. - # - # project.milestones # fetches milestones from the database - # project.milestones.size # uses the milestone cache - # project.milestones.empty? # uses the milestone cache - # project.milestones(true).size # fetches milestones from the database - # project.milestones # uses the milestone cache - # - # == Eager loading of associations - # - # Eager loading is a way to find objects of a certain class and a number of named associations. - # It is one of the easiest ways to prevent the dreaded N+1 problem in which fetching 100 - # posts that each need to display their author triggers 101 database queries. Through the - # use of eager loading, the number of queries will be reduced from 101 to 2. - # - # class Post < ActiveRecord::Base - # belongs_to :author - # has_many :comments - # end - # - # Consider the following loop using the class above: - # - # Post.all.each do |post| - # puts "Post: " + post.title - # puts "Written by: " + post.author.name - # puts "Last comment on: " + post.comments.first.created_on - # end - # - # To iterate over these one hundred posts, we'll generate 201 database queries. Let's - # first just optimize it for retrieving the author: - # - # Post.includes(:author).each do |post| - # - # This references the name of the #belongs_to association that also used the :author - # symbol. After loading the posts, +find+ will collect the +author_id+ from each one and load - # all of the referenced authors with one query. Doing so will cut down the number of queries - # from 201 to 102. - # - # We can improve upon the situation further by referencing both associations in the finder with: - # - # Post.includes(:author, :comments).each do |post| - # - # This will load all comments with a single query. This reduces the total number of queries - # to 3. In general, the number of queries will be 1 plus the number of associations - # named (except if some of the associations are polymorphic #belongs_to - see below). - # - # To include a deep hierarchy of associations, use a hash: - # - # Post.includes(:author, { comments: { author: :gravatar } }).each do |post| - # - # The above code will load all the comments and all of their associated - # authors and gravatars. You can mix and match any combination of symbols, - # arrays, and hashes to retrieve the associations you want to load. - # - # All of this power shouldn't fool you into thinking that you can pull out huge amounts - # of data with no performance penalty just because you've reduced the number of queries. - # The database still needs to send all the data to Active Record and it still needs to - # be processed. So it's no catch-all for performance problems, but it's a great way to - # cut down on the number of queries in a situation as the one described above. - # - # Since only one table is loaded at a time, conditions or orders cannot reference tables - # other than the main one. If this is the case, Active Record falls back to the previously - # used LEFT OUTER JOIN based strategy. For example: - # - # Post.includes([:author, :comments]).where(['comments.approved = ?', true]) - # - # This will result in a single SQL query with joins along the lines of: - # LEFT OUTER JOIN comments ON comments.post_id = posts.id and - # LEFT OUTER JOIN authors ON authors.id = posts.author_id. Note that using conditions - # like this can have unintended consequences. - # In the above example, posts with no approved comments are not returned at all because - # the conditions apply to the SQL statement as a whole and not just to the association. - # - # You must disambiguate column references for this fallback to happen, for example - # order: "author.name DESC" will work but order: "name DESC" will not. - # - # If you want to load all posts (including posts with no approved comments), then write - # your own LEFT OUTER JOIN query using ON: - # - # Post.joins("LEFT OUTER JOIN comments ON comments.post_id = posts.id AND comments.approved = '1'") - # - # In this case, it is usually more natural to include an association which has conditions defined on it: - # - # class Post < ActiveRecord::Base - # has_many :approved_comments, -> { where(approved: true) }, class_name: 'Comment' - # end - # - # Post.includes(:approved_comments) - # - # This will load posts and eager load the +approved_comments+ association, which contains - # only those comments that have been approved. - # - # If you eager load an association with a specified :limit option, it will be ignored, - # returning all the associated objects: - # - # class Picture < ActiveRecord::Base - # has_many :most_recent_comments, -> { order('id DESC').limit(10) }, class_name: 'Comment' - # end - # - # Picture.includes(:most_recent_comments).first.most_recent_comments # => returns all associated comments. - # - # Eager loading is supported with polymorphic associations. - # - # class Address < ActiveRecord::Base - # belongs_to :addressable, polymorphic: true - # end - # - # A call that tries to eager load the addressable model - # - # Address.includes(:addressable) - # - # This will execute one query to load the addresses and load the addressables with one - # query per addressable type. - # For example, if all the addressables are either of class Person or Company, then a total - # of 3 queries will be executed. The list of addressable types to load is determined on - # the back of the addresses loaded. This is not supported if Active Record has to fallback - # to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError. - # The reason is that the parent model's type is a column value so its corresponding table - # name cannot be put in the +FROM+/+JOIN+ clauses of that query. - # - # == Table Aliasing - # - # Active Record uses table aliasing in the case that a table is referenced multiple times - # in a join. If a table is referenced only once, the standard table name is used. The - # second time, the table is aliased as #{reflection_name}_#{parent_table_name}. - # Indexes are appended for any more successive uses of the table name. - # - # Post.joins(:comments) - # # => SELECT ... FROM posts INNER JOIN comments ON ... - # Post.joins(:special_comments) # STI - # # => SELECT ... FROM posts INNER JOIN comments ON ... AND comments.type = 'SpecialComment' - # Post.joins(:comments, :special_comments) # special_comments is the reflection name, posts is the parent table name - # # => SELECT ... FROM posts INNER JOIN comments ON ... INNER JOIN comments special_comments_posts - # - # Acts as tree example: - # - # TreeMixin.joins(:children) - # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ... - # TreeMixin.joins(children: :parent) - # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ... - # INNER JOIN parents_mixins ... - # TreeMixin.joins(children: {parent: :children}) - # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ... - # INNER JOIN parents_mixins ... - # INNER JOIN mixins childrens_mixins_2 - # - # Has and Belongs to Many join tables use the same idea, but add a _join suffix: - # - # Post.joins(:categories) - # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ... - # Post.joins(categories: :posts) - # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ... - # INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories - # Post.joins(categories: {posts: :categories}) - # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ... - # INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories - # INNER JOIN categories_posts categories_posts_join INNER JOIN categories categories_posts_2 - # - # If you wish to specify your own custom joins using ActiveRecord::QueryMethods#joins method, those table - # names will take precedence over the eager associations: - # - # Post.joins(:comments).joins("inner join comments ...") - # # => SELECT ... FROM posts INNER JOIN comments_posts ON ... INNER JOIN comments ... - # Post.joins(:comments, :special_comments).joins("inner join comments ...") - # # => SELECT ... FROM posts INNER JOIN comments comments_posts ON ... - # INNER JOIN comments special_comments_posts ... - # INNER JOIN comments ... - # - # Table aliases are automatically truncated according to the maximum length of table identifiers - # according to the specific database. - # - # == Modules - # - # By default, associations will look for objects within the current module scope. Consider: - # - # module MyApplication - # module Business - # class Firm < ActiveRecord::Base - # has_many :clients - # end - # - # class Client < ActiveRecord::Base; end - # end - # end - # - # When Firm#clients is called, it will in turn call - # MyApplication::Business::Client.find_all_by_firm_id(firm.id). - # If you want to associate with a class in another module scope, this can be done by - # specifying the complete class name. - # - # module MyApplication - # module Business - # class Firm < ActiveRecord::Base; end - # end - # - # module Billing - # class Account < ActiveRecord::Base - # belongs_to :firm, class_name: "MyApplication::Business::Firm" - # end - # end - # end - # - # == Bi-directional associations - # - # When you specify an association, there is usually an association on the associated model - # that specifies the same relationship in reverse. For example, with the following models: - # - # class Dungeon < ActiveRecord::Base - # has_many :traps - # has_one :evil_wizard - # end - # - # class Trap < ActiveRecord::Base - # belongs_to :dungeon - # end - # - # class EvilWizard < ActiveRecord::Base - # belongs_to :dungeon - # end - # - # The +traps+ association on +Dungeon+ and the +dungeon+ association on +Trap+ are - # the inverse of each other, and the inverse of the +dungeon+ association on +EvilWizard+ - # is the +evil_wizard+ association on +Dungeon+ (and vice-versa). By default, - # Active Record can guess the inverse of the association based on the name - # of the class. The result is the following: - # - # d = Dungeon.first - # t = d.traps.first - # d.object_id == t.dungeon.object_id # => true - # - # The +Dungeon+ instances +d+ and t.dungeon in the above example refer to - # the same in-memory instance since the association matches the name of the class. - # The result would be the same if we added +:inverse_of+ to our model definitions: - # - # class Dungeon < ActiveRecord::Base - # has_many :traps, inverse_of: :dungeon - # has_one :evil_wizard, inverse_of: :dungeon - # end - # - # class Trap < ActiveRecord::Base - # belongs_to :dungeon, inverse_of: :traps - # end - # - # class EvilWizard < ActiveRecord::Base - # belongs_to :dungeon, inverse_of: :evil_wizard - # end - # - # There are limitations to :inverse_of support: - # - # * does not work with :through associations. - # * does not work with :polymorphic associations. - # * inverse associations for #belongs_to associations #has_many are ignored. - # - # For more information, see the documentation for the +:inverse_of+ option. - # - # == Deleting from associations - # - # === Dependent associations - # - # #has_many, #has_one, and #belongs_to associations support the :dependent option. - # This allows you to specify that associated records should be deleted when the owner is - # deleted. - # - # For example: - # - # class Author - # has_many :posts, dependent: :destroy - # end - # Author.find(1).destroy # => Will destroy all of the author's posts, too - # - # The :dependent option can have different values which specify how the deletion - # is done. For more information, see the documentation for this option on the different - # specific association types. When no option is given, the behavior is to do nothing - # with the associated records when destroying a record. - # - # Note that :dependent is implemented using Rails' callback - # system, which works by processing callbacks in order. Therefore, other - # callbacks declared either before or after the :dependent option - # can affect what it does. - # - # Note that :dependent option is ignored for #has_one :through associations. - # - # === Delete or destroy? - # - # #has_many and #has_and_belongs_to_many associations have the methods destroy, - # delete, destroy_all and delete_all. - # - # For #has_and_belongs_to_many, delete and destroy are the same: they - # cause the records in the join table to be removed. - # - # For #has_many, destroy and destroy_all will always call the destroy method of the - # record(s) being removed so that callbacks are run. However delete and delete_all will either - # do the deletion according to the strategy specified by the :dependent option, or - # if no :dependent option is given, then it will follow the default strategy. - # The default strategy is to do nothing (leave the foreign keys with the parent ids set), except for - # #has_many :through, where the default strategy is delete_all (delete - # the join records, without running their callbacks). - # - # There is also a clear method which is the same as delete_all, except that - # it returns the association rather than the records which have been deleted. - # - # === What gets deleted? - # - # There is a potential pitfall here: #has_and_belongs_to_many and #has_many :through - # associations have records in join tables, as well as the associated records. So when we - # call one of these deletion methods, what exactly should be deleted? - # - # The answer is that it is assumed that deletion on an association is about removing the - # link between the owner and the associated object(s), rather than necessarily the - # associated objects themselves. So with #has_and_belongs_to_many and #has_many - # :through, the join records will be deleted, but the associated records won't. - # - # This makes sense if you think about it: if you were to call post.tags.delete(Tag.find_by(name: 'food')) - # you would want the 'food' tag to be unlinked from the post, rather than for the tag itself - # to be removed from the database. - # - # However, there are examples where this strategy doesn't make sense. For example, suppose - # a person has many projects, and each project has many tasks. If we deleted one of a person's - # tasks, we would probably not want the project to be deleted. In this scenario, the delete method - # won't actually work: it can only be used if the association on the join model is a - # #belongs_to. In other situations you are expected to perform operations directly on - # either the associated records or the :through association. - # - # With a regular #has_many there is no distinction between the "associated records" - # and the "link", so there is only one choice for what gets deleted. - # - # With #has_and_belongs_to_many and #has_many :through, if you want to delete the - # associated records themselves, you can always do something along the lines of - # person.tasks.each(&:destroy). - # - # == Type safety with ActiveRecord::AssociationTypeMismatch - # - # If you attempt to assign an object to an association that doesn't match the inferred - # or specified :class_name, you'll get an ActiveRecord::AssociationTypeMismatch. - # - # == Options - # - # All of the association macros can be specialized through options. This makes cases - # more complex than the simple and guessable ones possible. + # \Associations are a set of macro-like class methods for tying objects together through + # foreign keys. They express relationships like "Project has one Project Manager" + # or "Project belongs to a Portfolio". Each macro adds a number of methods to the + # class which are specialized according to the collection or association symbol and the + # options hash. It works much the same way as Ruby's own attr* + # methods. + # + # class Project < ActiveRecord::Base + # belongs_to :portfolio + # has_one :project_manager + # has_many :milestones + # has_and_belongs_to_many :categories + # end + # + # The project class now has the following methods (and more) to ease the traversal and + # manipulation of its relationships: + # * Project#portfolio, Project#portfolio=(portfolio), Project#portfolio.nil? + # * Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?, + # * Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone), + # Project#milestones.delete(milestone), Project#milestones.destroy(milestone), Project#milestones.find(milestone_id), + # Project#milestones.build, Project#milestones.create + # * Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1), + # Project#categories.delete(category1), Project#categories.destroy(category1) + # + # === A word of warning + # + # Don't create associations that have the same name as {instance methods}[rdoc-ref:ActiveRecord::Core] of + # ActiveRecord::Base. Since the association adds a method with that name to + # its model, using an association with the same name as one provided by ActiveRecord::Base will override the method inherited through ActiveRecord::Base and will break things. + # For instance, +attributes+ and +connection+ would be bad choices for association names, because those names already exist in the list of ActiveRecord::Base instance methods. + # + # == Auto-generated methods + # See also Instance Public methods below for more details. + # + # === Singular associations (one-to-one) + # | | belongs_to | + # generated methods | belongs_to | :polymorphic | has_one + # ----------------------------------+------------+--------------+--------- + # other(force_reload=false) | X | X | X + # other=(other) | X | X | X + # build_other(attributes={}) | X | | X + # create_other(attributes={}) | X | | X + # create_other!(attributes={}) | X | | X + # + # === Collection associations (one-to-many / many-to-many) + # | | | has_many + # generated methods | habtm | has_many | :through + # ----------------------------------+-------+----------+---------- + # others(force_reload=false) | X | X | X + # others=(other,other,...) | X | X | X + # other_ids | X | X | X + # other_ids=(id,id,...) | X | X | X + # others<< | X | X | X + # others.push | X | X | X + # others.concat | X | X | X + # others.build(attributes={}) | X | X | X + # others.create(attributes={}) | X | X | X + # others.create!(attributes={}) | X | X | X + # others.size | X | X | X + # others.length | X | X | X + # others.count | X | X | X + # others.sum(*args) | X | X | X + # others.empty? | X | X | X + # others.clear | X | X | X + # others.delete(other,other,...) | X | X | X + # others.delete_all | X | X | X + # others.destroy(other,other,...) | X | X | X + # others.destroy_all | X | X | X + # others.find(*args) | X | X | X + # others.exists? | X | X | X + # others.distinct | X | X | X + # others.reset | X | X | X + # + # === Overriding generated methods + # + # Association methods are generated in a module that is included into the model class, + # which allows you to easily override with your own methods and call the original + # generated method with +super+. For example: + # + # class Car < ActiveRecord::Base + # belongs_to :owner + # belongs_to :old_owner + # def owner=(new_owner) + # self.old_owner = self.owner + # super + # end + # end + # + # If your model class is Project, then the module is + # named Project::GeneratedAssociationMethods. The +GeneratedAssociationMethods+ module is + # included in the model class immediately after the (anonymous) generated attributes methods + # module, meaning an association will override the methods for an attribute with the same name. + # + # == Cardinality and associations + # + # Active Record associations can be used to describe one-to-one, one-to-many and many-to-many + # relationships between models. Each model uses an association to describe its role in + # the relation. The #belongs_to association is always used in the model that has + # the foreign key. + # + # === One-to-one + # + # Use #has_one in the base, and #belongs_to in the associated model. + # + # class Employee < ActiveRecord::Base + # has_one :office + # end + # class Office < ActiveRecord::Base + # belongs_to :employee # foreign key - employee_id + # end + # + # === One-to-many + # + # Use #has_many in the base, and #belongs_to in the associated model. + # + # class Manager < ActiveRecord::Base + # has_many :employees + # end + # class Employee < ActiveRecord::Base + # belongs_to :manager # foreign key - manager_id + # end + # + # === Many-to-many + # + # There are two ways to build a many-to-many relationship. + # + # The first way uses a #has_many association with the :through option and a join model, so + # there are two stages of associations. + # + # class Assignment < ActiveRecord::Base + # belongs_to :programmer # foreign key - programmer_id + # belongs_to :project # foreign key - project_id + # end + # class Programmer < ActiveRecord::Base + # has_many :assignments + # has_many :projects, through: :assignments + # end + # class Project < ActiveRecord::Base + # has_many :assignments + # has_many :programmers, through: :assignments + # end + # + # For the second way, use #has_and_belongs_to_many in both models. This requires a join table + # that has no corresponding model or primary key. + # + # class Programmer < ActiveRecord::Base + # has_and_belongs_to_many :projects # foreign keys in the join table + # end + # class Project < ActiveRecord::Base + # has_and_belongs_to_many :programmers # foreign keys in the join table + # end + # + # Choosing which way to build a many-to-many relationship is not always simple. + # If you need to work with the relationship model as its own entity, + # use #has_many :through. Use #has_and_belongs_to_many when working with legacy schemas or when + # you never work directly with the relationship itself. + # + # == Is it a #belongs_to or #has_one association? + # + # Both express a 1-1 relationship. The difference is mostly where to place the foreign + # key, which goes on the table for the class declaring the #belongs_to relationship. + # + # class User < ActiveRecord::Base + # # I reference an account. + # belongs_to :account + # end + # + # class Account < ActiveRecord::Base + # # One user references me. + # has_one :user + # end + # + # The tables for these classes could look something like: + # + # CREATE TABLE users ( + # id int NOT NULL auto_increment, + # account_id int default NULL, + # name varchar default NULL, + # PRIMARY KEY (id) + # ) + # + # CREATE TABLE accounts ( + # id int NOT NULL auto_increment, + # name varchar default NULL, + # PRIMARY KEY (id) + # ) + # + # == Unsaved objects and associations + # + # You can manipulate objects and associations before they are saved to the database, but + # there is some special behavior you should be aware of, mostly involving the saving of + # associated objects. + # + # You can set the :autosave option on a #has_one, #belongs_to, + # #has_many, or #has_and_belongs_to_many association. Setting it + # to +true+ will _always_ save the members, whereas setting it to +false+ will + # _never_ save the members. More details about :autosave option is available at + # AutosaveAssociation. + # + # === One-to-one associations + # + # * Assigning an object to a #has_one association automatically saves that object and + # the object being replaced (if there is one), in order to update their foreign + # keys - except if the parent object is unsaved (new_record? == true). + # * If either of these saves fail (due to one of the objects being invalid), an + # ActiveRecord::RecordNotSaved exception is raised and the assignment is + # cancelled. + # * If you wish to assign an object to a #has_one association without saving it, + # use the #build_association method (documented below). The object being + # replaced will still be saved to update its foreign key. + # * Assigning an object to a #belongs_to association does not save the object, since + # the foreign key field belongs on the parent. It does not save the parent either. + # + # === Collections + # + # * Adding an object to a collection (#has_many or #has_and_belongs_to_many) automatically + # saves that object, except if the parent object (the owner of the collection) is not yet + # stored in the database. + # * If saving any of the objects being added to a collection (via push or similar) + # fails, then push returns +false+. + # * If saving fails while replacing the collection (via association=), an + # ActiveRecord::RecordNotSaved exception is raised and the assignment is + # cancelled. + # * You can add an object to a collection without automatically saving it by using the + # collection.build method (documented below). + # * All unsaved (new_record? == true) members of the collection are automatically + # saved when the parent is saved. + # + # == Customizing the query + # + # \Associations are built from Relation objects, and you can use the Relation syntax + # to customize them. For example, to add a condition: + # + # class Blog < ActiveRecord::Base + # has_many :published_posts, -> { where(published: true) }, class_name: 'Post' + # end + # + # Inside the -> { ... } block you can use all of the usual Relation methods. + # + # === Accessing the owner object + # + # Sometimes it is useful to have access to the owner object when building the query. The owner + # is passed as a parameter to the block. For example, the following association would find all + # events that occur on the user's birthday: + # + # class User < ActiveRecord::Base + # has_many :birthday_events, ->(user) { where(starts_on: user.birthday) }, class_name: 'Event' + # end + # + # Note: Joining, eager loading and preloading of these associations is not fully possible. + # These operations happen before instance creation and the scope will be called with a +nil+ argument. + # This can lead to unexpected behavior and is deprecated. + # + # == Association callbacks + # + # Similar to the normal callbacks that hook into the life cycle of an Active Record object, + # you can also define callbacks that get triggered when you add an object to or remove an + # object from an association collection. + # + # class Project + # has_and_belongs_to_many :developers, after_add: :evaluate_velocity + # + # def evaluate_velocity(developer) + # ... + # end + # end + # + # It's possible to stack callbacks by passing them as an array. Example: + # + # class Project + # has_and_belongs_to_many :developers, + # after_add: [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}] + # end + # + # Possible callbacks are: +before_add+, +after_add+, +before_remove+ and +after_remove+. + # + # If any of the +before_add+ callbacks throw an exception, the object will not be + # added to the collection. + # + # Similarly, if any of the +before_remove+ callbacks throw an exception, the object + # will not be removed from the collection. + # + # == Association extensions + # + # The proxy objects that control the access to associations can be extended through anonymous + # modules. This is especially beneficial for adding new finders, creators, and other + # factory-type methods that are only used as part of this association. + # + # class Account < ActiveRecord::Base + # has_many :people do + # def find_or_create_by_name(name) + # first_name, last_name = name.split(" ", 2) + # find_or_create_by(first_name: first_name, last_name: last_name) + # end + # end + # end + # + # person = Account.first.people.find_or_create_by_name("David Heinemeier Hansson") + # person.first_name # => "David" + # person.last_name # => "Heinemeier Hansson" + # + # If you need to share the same extensions between many associations, you can use a named + # extension module. + # + # module FindOrCreateByNameExtension + # def find_or_create_by_name(name) + # first_name, last_name = name.split(" ", 2) + # find_or_create_by(first_name: first_name, last_name: last_name) + # end + # end + # + # class Account < ActiveRecord::Base + # has_many :people, -> { extending FindOrCreateByNameExtension } + # end + # + # class Company < ActiveRecord::Base + # has_many :people, -> { extending FindOrCreateByNameExtension } + # end + # + # Some extensions can only be made to work with knowledge of the association's internals. + # Extensions can access relevant state using the following methods (where +items+ is the + # name of the association): + # + # * record.association(:items).owner - Returns the object the association is part of. + # * record.association(:items).reflection - Returns the reflection object that describes the association. + # * record.association(:items).target - Returns the associated object for #belongs_to and #has_one, or + # the collection of associated objects for #has_many and #has_and_belongs_to_many. + # + # However, inside the actual extension code, you will not have access to the record as + # above. In this case, you can access proxy_association. For example, + # record.association(:items) and record.items.proxy_association will return + # the same object, allowing you to make calls like proxy_association.owner inside + # association extensions. + # + # == Association Join Models + # + # Has Many associations can be configured with the :through option to use an + # explicit join model to retrieve the data. This operates similarly to a + # #has_and_belongs_to_many association. The advantage is that you're able to add validations, + # callbacks, and extra attributes on the join model. Consider the following schema: + # + # class Author < ActiveRecord::Base + # has_many :authorships + # has_many :books, through: :authorships + # end + # + # class Authorship < ActiveRecord::Base + # belongs_to :author + # belongs_to :book + # end + # + # @author = Author.first + # @author.authorships.collect { |a| a.book } # selects all books that the author's authorships belong to + # @author.books # selects all books by using the Authorship join model + # + # You can also go through a #has_many association on the join model: + # + # class Firm < ActiveRecord::Base + # has_many :clients + # has_many :invoices, through: :clients + # end + # + # class Client < ActiveRecord::Base + # belongs_to :firm + # has_many :invoices + # end + # + # class Invoice < ActiveRecord::Base + # belongs_to :client + # end + # + # @firm = Firm.first + # @firm.clients.flat_map { |c| c.invoices } # select all invoices for all clients of the firm + # @firm.invoices # selects all invoices by going through the Client join model + # + # Similarly you can go through a #has_one association on the join model: + # + # class Group < ActiveRecord::Base + # has_many :users + # has_many :avatars, through: :users + # end + # + # class User < ActiveRecord::Base + # belongs_to :group + # has_one :avatar + # end + # + # class Avatar < ActiveRecord::Base + # belongs_to :user + # end + # + # @group = Group.first + # @group.users.collect { |u| u.avatar }.compact # select all avatars for all users in the group + # @group.avatars # selects all avatars by going through the User join model. + # + # An important caveat with going through #has_one or #has_many associations on the + # join model is that these associations are *read-only*. For example, the following + # would not work following the previous example: + # + # @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around + # @group.avatars.delete(@group.avatars.last) # so would this + # + # == Setting Inverses + # + # If you are using a #belongs_to on the join model, it is a good idea to set the + # :inverse_of option on the #belongs_to, which will mean that the following example + # works correctly (where tags is a #has_many :through association): + # + # @post = Post.first + # @tag = @post.tags.build name: "ruby" + # @tag.save + # + # The last line ought to save the through record (a Tagging). This will only work if the + # :inverse_of is set: + # + # class Tagging < ActiveRecord::Base + # belongs_to :post + # belongs_to :tag, inverse_of: :taggings + # end + # + # If you do not set the :inverse_of record, the association will + # do its best to match itself up with the correct inverse. Automatic + # inverse detection only works on #has_many, #has_one, and + # #belongs_to associations. + # + # Extra options on the associations, as defined in the + # AssociationReflection::INVALID_AUTOMATIC_INVERSE_OPTIONS constant, will + # also prevent the association's inverse from being found automatically. + # + # The automatic guessing of the inverse association uses a heuristic based + # on the name of the class, so it may not work for all associations, + # especially the ones with non-standard names. + # + # You can turn off the automatic detection of inverse associations by setting + # the :inverse_of option to false like so: + # + # class Tagging < ActiveRecord::Base + # belongs_to :tag, inverse_of: false + # end + # + # == Nested \Associations + # + # You can actually specify *any* association with the :through option, including an + # association which has a :through option itself. For example: + # + # class Author < ActiveRecord::Base + # has_many :posts + # has_many :comments, through: :posts + # has_many :commenters, through: :comments + # end + # + # class Post < ActiveRecord::Base + # has_many :comments + # end + # + # class Comment < ActiveRecord::Base + # belongs_to :commenter + # end + # + # @author = Author.first + # @author.commenters # => People who commented on posts written by the author + # + # An equivalent way of setting up this association this would be: + # + # class Author < ActiveRecord::Base + # has_many :posts + # has_many :commenters, through: :posts + # end + # + # class Post < ActiveRecord::Base + # has_many :comments + # has_many :commenters, through: :comments + # end + # + # class Comment < ActiveRecord::Base + # belongs_to :commenter + # end + # + # When using a nested association, you will not be able to modify the association because there + # is not enough information to know what modification to make. For example, if you tried to + # add a Commenter in the example above, there would be no way to tell how to set up the + # intermediate Post and Comment objects. + # + # == Polymorphic \Associations + # + # Polymorphic associations on models are not restricted on what types of models they + # can be associated with. Rather, they specify an interface that a #has_many association + # must adhere to. + # + # class Asset < ActiveRecord::Base + # belongs_to :attachable, polymorphic: true + # end + # + # class Post < ActiveRecord::Base + # has_many :assets, as: :attachable # The :as option specifies the polymorphic interface to use. + # end + # + # @asset.attachable = @post + # + # This works by using a type column in addition to a foreign key to specify the associated + # record. In the Asset example, you'd need an +attachable_id+ integer column and an + # +attachable_type+ string column. + # + # Using polymorphic associations in combination with single table inheritance (STI) is + # a little tricky. In order for the associations to work as expected, ensure that you + # store the base model for the STI models in the type column of the polymorphic + # association. To continue with the asset example above, suppose there are guest posts + # and member posts that use the posts table for STI. In this case, there must be a +type+ + # column in the posts table. + # + # Note: The attachable_type= method is being called when assigning an +attachable+. + # The +class_name+ of the +attachable+ is passed as a String. + # + # class Asset < ActiveRecord::Base + # belongs_to :attachable, polymorphic: true + # + # def attachable_type=(class_name) + # super(class_name.constantize.base_class.to_s) + # end + # end + # + # class Post < ActiveRecord::Base + # # because we store "Post" in attachable_type now dependent: :destroy will work + # has_many :assets, as: :attachable, dependent: :destroy + # end + # + # class GuestPost < Post + # end + # + # class MemberPost < Post + # end + # + # == Caching + # + # All of the methods are built on a simple caching principle that will keep the result + # of the last query around unless specifically instructed not to. The cache is even + # shared across methods to make it even cheaper to use the macro-added methods without + # worrying too much about performance at the first go. + # + # project.milestones # fetches milestones from the database + # project.milestones.size # uses the milestone cache + # project.milestones.empty? # uses the milestone cache + # project.milestones(true).size # fetches milestones from the database + # project.milestones # uses the milestone cache + # + # == Eager loading of associations + # + # Eager loading is a way to find objects of a certain class and a number of named associations. + # It is one of the easiest ways to prevent the dreaded N+1 problem in which fetching 100 + # posts that each need to display their author triggers 101 database queries. Through the + # use of eager loading, the number of queries will be reduced from 101 to 2. + # + # class Post < ActiveRecord::Base + # belongs_to :author + # has_many :comments + # end + # + # Consider the following loop using the class above: + # + # Post.all.each do |post| + # puts "Post: " + post.title + # puts "Written by: " + post.author.name + # puts "Last comment on: " + post.comments.first.created_on + # end + # + # To iterate over these one hundred posts, we'll generate 201 database queries. Let's + # first just optimize it for retrieving the author: + # + # Post.includes(:author).each do |post| + # + # This references the name of the #belongs_to association that also used the :author + # symbol. After loading the posts, +find+ will collect the +author_id+ from each one and load + # all of the referenced authors with one query. Doing so will cut down the number of queries + # from 201 to 102. + # + # We can improve upon the situation further by referencing both associations in the finder with: + # + # Post.includes(:author, :comments).each do |post| + # + # This will load all comments with a single query. This reduces the total number of queries + # to 3. In general, the number of queries will be 1 plus the number of associations + # named (except if some of the associations are polymorphic #belongs_to - see below). + # + # To include a deep hierarchy of associations, use a hash: + # + # Post.includes(:author, { comments: { author: :gravatar } }).each do |post| + # + # The above code will load all the comments and all of their associated + # authors and gravatars. You can mix and match any combination of symbols, + # arrays, and hashes to retrieve the associations you want to load. + # + # All of this power shouldn't fool you into thinking that you can pull out huge amounts + # of data with no performance penalty just because you've reduced the number of queries. + # The database still needs to send all the data to Active Record and it still needs to + # be processed. So it's no catch-all for performance problems, but it's a great way to + # cut down on the number of queries in a situation as the one described above. + # + # Since only one table is loaded at a time, conditions or orders cannot reference tables + # other than the main one. If this is the case, Active Record falls back to the previously + # used LEFT OUTER JOIN based strategy. For example: + # + # Post.includes([:author, :comments]).where(['comments.approved = ?', true]) + # + # This will result in a single SQL query with joins along the lines of: + # LEFT OUTER JOIN comments ON comments.post_id = posts.id and + # LEFT OUTER JOIN authors ON authors.id = posts.author_id. Note that using conditions + # like this can have unintended consequences. + # In the above example, posts with no approved comments are not returned at all because + # the conditions apply to the SQL statement as a whole and not just to the association. + # + # You must disambiguate column references for this fallback to happen, for example + # order: "author.name DESC" will work but order: "name DESC" will not. + # + # If you want to load all posts (including posts with no approved comments), then write + # your own LEFT OUTER JOIN query using ON: + # + # Post.joins("LEFT OUTER JOIN comments ON comments.post_id = posts.id AND comments.approved = '1'") + # + # In this case, it is usually more natural to include an association which has conditions defined on it: + # + # class Post < ActiveRecord::Base + # has_many :approved_comments, -> { where(approved: true) }, class_name: 'Comment' + # end + # + # Post.includes(:approved_comments) + # + # This will load posts and eager load the +approved_comments+ association, which contains + # only those comments that have been approved. + # + # If you eager load an association with a specified :limit option, it will be ignored, + # returning all the associated objects: + # + # class Picture < ActiveRecord::Base + # has_many :most_recent_comments, -> { order('id DESC').limit(10) }, class_name: 'Comment' + # end + # + # Picture.includes(:most_recent_comments).first.most_recent_comments # => returns all associated comments. + # + # Eager loading is supported with polymorphic associations. + # + # class Address < ActiveRecord::Base + # belongs_to :addressable, polymorphic: true + # end + # + # A call that tries to eager load the addressable model + # + # Address.includes(:addressable) + # + # This will execute one query to load the addresses and load the addressables with one + # query per addressable type. + # For example, if all the addressables are either of class Person or Company, then a total + # of 3 queries will be executed. The list of addressable types to load is determined on + # the back of the addresses loaded. This is not supported if Active Record has to fallback + # to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError. + # The reason is that the parent model's type is a column value so its corresponding table + # name cannot be put in the +FROM+/+JOIN+ clauses of that query. + # + # == Table Aliasing + # + # Active Record uses table aliasing in the case that a table is referenced multiple times + # in a join. If a table is referenced only once, the standard table name is used. The + # second time, the table is aliased as #{reflection_name}_#{parent_table_name}. + # Indexes are appended for any more successive uses of the table name. + # + # Post.joins(:comments) + # # => SELECT ... FROM posts INNER JOIN comments ON ... + # Post.joins(:special_comments) # STI + # # => SELECT ... FROM posts INNER JOIN comments ON ... AND comments.type = 'SpecialComment' + # Post.joins(:comments, :special_comments) # special_comments is the reflection name, posts is the parent table name + # # => SELECT ... FROM posts INNER JOIN comments ON ... INNER JOIN comments special_comments_posts + # + # Acts as tree example: + # + # TreeMixin.joins(:children) + # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ... + # TreeMixin.joins(children: :parent) + # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ... + # INNER JOIN parents_mixins ... + # TreeMixin.joins(children: {parent: :children}) + # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ... + # INNER JOIN parents_mixins ... + # INNER JOIN mixins childrens_mixins_2 + # + # Has and Belongs to Many join tables use the same idea, but add a _join suffix: + # + # Post.joins(:categories) + # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ... + # Post.joins(categories: :posts) + # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ... + # INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories + # Post.joins(categories: {posts: :categories}) + # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ... + # INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories + # INNER JOIN categories_posts categories_posts_join INNER JOIN categories categories_posts_2 + # + # If you wish to specify your own custom joins using ActiveRecord::QueryMethods#joins method, those table + # names will take precedence over the eager associations: + # + # Post.joins(:comments).joins("inner join comments ...") + # # => SELECT ... FROM posts INNER JOIN comments_posts ON ... INNER JOIN comments ... + # Post.joins(:comments, :special_comments).joins("inner join comments ...") + # # => SELECT ... FROM posts INNER JOIN comments comments_posts ON ... + # INNER JOIN comments special_comments_posts ... + # INNER JOIN comments ... + # + # Table aliases are automatically truncated according to the maximum length of table identifiers + # according to the specific database. + # + # == Modules + # + # By default, associations will look for objects within the current module scope. Consider: + # + # module MyApplication + # module Business + # class Firm < ActiveRecord::Base + # has_many :clients + # end + # + # class Client < ActiveRecord::Base; end + # end + # end + # + # When Firm#clients is called, it will in turn call + # MyApplication::Business::Client.find_all_by_firm_id(firm.id). + # If you want to associate with a class in another module scope, this can be done by + # specifying the complete class name. + # + # module MyApplication + # module Business + # class Firm < ActiveRecord::Base; end + # end + # + # module Billing + # class Account < ActiveRecord::Base + # belongs_to :firm, class_name: "MyApplication::Business::Firm" + # end + # end + # end + # + # == Bi-directional associations + # + # When you specify an association, there is usually an association on the associated model + # that specifies the same relationship in reverse. For example, with the following models: + # + # class Dungeon < ActiveRecord::Base + # has_many :traps + # has_one :evil_wizard + # end + # + # class Trap < ActiveRecord::Base + # belongs_to :dungeon + # end + # + # class EvilWizard < ActiveRecord::Base + # belongs_to :dungeon + # end + # + # The +traps+ association on +Dungeon+ and the +dungeon+ association on +Trap+ are + # the inverse of each other, and the inverse of the +dungeon+ association on +EvilWizard+ + # is the +evil_wizard+ association on +Dungeon+ (and vice-versa). By default, + # Active Record can guess the inverse of the association based on the name + # of the class. The result is the following: + # + # d = Dungeon.first + # t = d.traps.first + # d.object_id == t.dungeon.object_id # => true + # + # The +Dungeon+ instances +d+ and t.dungeon in the above example refer to + # the same in-memory instance since the association matches the name of the class. + # The result would be the same if we added +:inverse_of+ to our model definitions: + # + # class Dungeon < ActiveRecord::Base + # has_many :traps, inverse_of: :dungeon + # has_one :evil_wizard, inverse_of: :dungeon + # end + # + # class Trap < ActiveRecord::Base + # belongs_to :dungeon, inverse_of: :traps + # end + # + # class EvilWizard < ActiveRecord::Base + # belongs_to :dungeon, inverse_of: :evil_wizard + # end + # + # There are limitations to :inverse_of support: + # + # * does not work with :through associations. + # * does not work with :polymorphic associations. + # * inverse associations for #belongs_to associations #has_many are ignored. + # + # For more information, see the documentation for the +:inverse_of+ option. + # + # == Deleting from associations + # + # === Dependent associations + # + # #has_many, #has_one, and #belongs_to associations support the :dependent option. + # This allows you to specify that associated records should be deleted when the owner is + # deleted. + # + # For example: + # + # class Author + # has_many :posts, dependent: :destroy + # end + # Author.find(1).destroy # => Will destroy all of the author's posts, too + # + # The :dependent option can have different values which specify how the deletion + # is done. For more information, see the documentation for this option on the different + # specific association types. When no option is given, the behavior is to do nothing + # with the associated records when destroying a record. + # + # Note that :dependent is implemented using Rails' callback + # system, which works by processing callbacks in order. Therefore, other + # callbacks declared either before or after the :dependent option + # can affect what it does. + # + # Note that :dependent option is ignored for #has_one :through associations. + # + # === Delete or destroy? + # + # #has_many and #has_and_belongs_to_many associations have the methods destroy, + # delete, destroy_all and delete_all. + # + # For #has_and_belongs_to_many, delete and destroy are the same: they + # cause the records in the join table to be removed. + # + # For #has_many, destroy and destroy_all will always call the destroy method of the + # record(s) being removed so that callbacks are run. However delete and delete_all will either + # do the deletion according to the strategy specified by the :dependent option, or + # if no :dependent option is given, then it will follow the default strategy. + # The default strategy is to do nothing (leave the foreign keys with the parent ids set), except for + # #has_many :through, where the default strategy is delete_all (delete + # the join records, without running their callbacks). + # + # There is also a clear method which is the same as delete_all, except that + # it returns the association rather than the records which have been deleted. + # + # === What gets deleted? + # + # There is a potential pitfall here: #has_and_belongs_to_many and #has_many :through + # associations have records in join tables, as well as the associated records. So when we + # call one of these deletion methods, what exactly should be deleted? + # + # The answer is that it is assumed that deletion on an association is about removing the + # link between the owner and the associated object(s), rather than necessarily the + # associated objects themselves. So with #has_and_belongs_to_many and #has_many + # :through, the join records will be deleted, but the associated records won't. + # + # This makes sense if you think about it: if you were to call post.tags.delete(Tag.find_by(name: 'food')) + # you would want the 'food' tag to be unlinked from the post, rather than for the tag itself + # to be removed from the database. + # + # However, there are examples where this strategy doesn't make sense. For example, suppose + # a person has many projects, and each project has many tasks. If we deleted one of a person's + # tasks, we would probably not want the project to be deleted. In this scenario, the delete method + # won't actually work: it can only be used if the association on the join model is a + # #belongs_to. In other situations you are expected to perform operations directly on + # either the associated records or the :through association. + # + # With a regular #has_many there is no distinction between the "associated records" + # and the "link", so there is only one choice for what gets deleted. + # + # With #has_and_belongs_to_many and #has_many :through, if you want to delete the + # associated records themselves, you can always do something along the lines of + # person.tasks.each(&:destroy). + # + # == Type safety with ActiveRecord::AssociationTypeMismatch + # + # If you attempt to assign an object to an association that doesn't match the inferred + # or specified :class_name, you'll get an ActiveRecord::AssociationTypeMismatch. + # + # == Options + # + # All of the association macros can be specialized through options. This makes cases + # more complex than the simple and guessable ones possible. module ClassMethods # Specifies a one-to-many association. The following methods for retrieval and query of # collections of associated objects will be added: diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb index a81860e40f..9f77f38b35 100644 --- a/activerecord/lib/active_record/associations/preloader.rb +++ b/activerecord/lib/active_record/associations/preloader.rb @@ -106,7 +106,7 @@ module ActiveRecord 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 @@ module ActiveRecord } 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 @@ module ActiveRecord 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 diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb index f3ce52fdfe..9843e0ca66 100644 --- a/activerecord/lib/active_record/attribute_assignment.rb +++ b/activerecord/lib/active_record/attribute_assignment.rb @@ -29,17 +29,17 @@ module ActiveRecord 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) diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index ba26a11b39..1ed1deec55 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -416,8 +416,8 @@ module ActiveRecord 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 @@ module ActiveRecord 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? diff --git a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb index 92f124078c..115eb1ef3f 100644 --- a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb +++ b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb @@ -63,7 +63,7 @@ module ActiveRecord 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 diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index 131ed8740b..30f7750884 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -6,24 +6,24 @@ module ActiveRecord 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}" diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb index e9d044ef13..f65c297e01 100644 --- a/activerecord/lib/active_record/attribute_methods/write.rb +++ b/activerecord/lib/active_record/attribute_methods/write.rb @@ -37,7 +37,7 @@ module ActiveRecord end private - # Handle *= for method_missing. + # Handle *= for method_missing. def attribute=(attribute_name, value) write_attribute(attribute_name, value) end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index d0c5bbe17d..2d62fd8d50 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -158,33 +158,33 @@ module ActiveRecord @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 @@ module ActiveRecord 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 @@ module ActiveRecord 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 @@ module ActiveRecord 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 @@ module ActiveRecord 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 @@ module ActiveRecord 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 @@ module ActiveRecord 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 diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb index 10c60080d5..6ca53c72ce 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb @@ -85,8 +85,8 @@ module ActiveRecord 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 diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index d0ea1ce0cf..be8511f119 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -818,8 +818,8 @@ module ActiveRecord 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] diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb index be6b55e53c..849130ba43 100644 --- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb @@ -63,15 +63,15 @@ module ActiveRecord @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 @@ module ActiveRecord 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 @@ module ActiveRecord 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 @@ module ActiveRecord 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 @@ module ActiveRecord 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 @@ module ActiveRecord 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 diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 03ee0eec5b..8001c0dd53 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -527,7 +527,7 @@ module ActiveRecord 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 @@ module ActiveRecord # 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 diff --git a/activerecord/lib/active_record/dynamic_matchers.rb b/activerecord/lib/active_record/dynamic_matchers.rb index bbd8ca2377..9a7a8d25bb 100644 --- a/activerecord/lib/active_record/dynamic_matchers.rb +++ b/activerecord/lib/active_record/dynamic_matchers.rb @@ -75,14 +75,14 @@ module ActiveRecord "#{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 diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb index 4adcd7e65c..a1d4f47372 100644 --- a/activerecord/lib/active_record/inheritance.rb +++ b/activerecord/lib/active_record/inheritance.rb @@ -132,8 +132,8 @@ module ActiveRecord 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 @@ module ActiveRecord 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 @@ module ActiveRecord 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 @@ module ActiveRecord 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 Reply[Reply.inheritance_column] = "Reply" 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 Reply[Reply.inheritance_column] = "Reply" 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? diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index 1b6cda3861..8e8a97990a 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -168,10 +168,10 @@ module ActiveRecord 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 } diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 063366bc60..05568039d8 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -1163,7 +1163,7 @@ module ActiveRecord 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 @@ module ActiveRecord 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 @@ module ActiveRecord 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 @@ module ActiveRecord 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 @@ module ActiveRecord @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 } diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb index 44ea756028..03103bba98 100644 --- a/activerecord/lib/active_record/migration/command_recorder.rb +++ b/activerecord/lib/active_record/migration/command_recorder.rb @@ -225,7 +225,7 @@ module ActiveRecord [: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) diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb index f49f8da2ed..76b3169411 100644 --- a/activerecord/lib/active_record/model_schema.rb +++ b/activerecord/lib/active_record/model_schema.rb @@ -397,13 +397,13 @@ module ActiveRecord 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 diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index f0f88b120a..e983026961 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -341,17 +341,17 @@ module ActiveRecord 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 @@ module ActiveRecord 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 :id that matches the existing record's - # id, then the existing record will be modified. If no :id 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 :id is provided. - # - # If the given attributes include a matching :id attribute, or - # update_only is true, and a :_destroy 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 :id that matches the existing record's + # id, then the existing record will be modified. If no :id 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 :id is provided. + # + # If the given attributes include a matching :id attribute, or + # update_only is true, and a :_destroy 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 @@ module ActiveRecord end end - # Assigns the given attributes to the collection association. - # - # Hashes with an :id value matching an existing associated record - # will update that record. Hashes without an :id value will build - # a new record for the association. Hashes with a matching :id - # value and a :_destroy 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 :id value matching an existing associated record + # will update that record. Hashes without an :id value will build + # a new record for the association. Hashes with a matching :id + # value and a :_destroy 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 @@ module ActiveRecord 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 @@ module ActiveRecord 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 :reject_if 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 :reject_if 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 @@ module ActiveRecord end end - # Only take into account the destroy flag if :allow_destroy is true + # Only take into account the destroy flag if :allow_destroy is true def will_be_destroyed?(association_name, attributes) allow_destroy?(association_name) && has_destroy_flag?(attributes) end diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index a796e35261..09ca30e434 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -325,13 +325,13 @@ module ActiveRecord 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}" diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 78570140e5..9fbbe32e7f 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -1150,22 +1150,22 @@ module ActiveRecord 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." diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb index 7f596120eb..e7c0936984 100644 --- a/activerecord/lib/active_record/sanitization.rb +++ b/activerecord/lib/active_record/sanitization.rb @@ -7,20 +7,20 @@ module ActiveRecord 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 @@ module ActiveRecord 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 @@ module ActiveRecord 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 @@ module ActiveRecord 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 @@ module ActiveRecord 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 @@ module ActiveRecord 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) diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb index 7409706851..9d8253faa3 100644 --- a/activerecord/lib/active_record/scoping/default.rb +++ b/activerecord/lib/active_record/scoping/default.rb @@ -46,47 +46,47 @@ module ActiveRecord 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 @@ module ActiveRecord 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? diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index b19ae5c46e..af3fc88282 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -409,7 +409,7 @@ module ActiveRecord 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 @@ module ActiveRecord @_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 @@ module ActiveRecord 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 @@ module ActiveRecord !_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 diff --git a/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb index bc9037c476..76ed25ea75 100644 --- a/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb +++ b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb @@ -17,9 +17,9 @@ module ActiveRecord 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 diff --git a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb index 7736113a19..776549eb7a 100644 --- a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb +++ b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb @@ -130,19 +130,19 @@ class Mysql2ReservedWordTest < ActiveRecord::Mysql2TestCase #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} )") diff --git a/activerecord/test/cases/hot_compatibility_test.rb b/activerecord/test/cases/hot_compatibility_test.rb index e0946544ef..e107ff2362 100644 --- a/activerecord/test/cases/hot_compatibility_test.rb +++ b/activerecord/test/cases/hot_compatibility_test.rb @@ -119,11 +119,11 @@ class HotCompatibilityTest < ActiveRecord::TestCase .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)) diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb index 8f0b2bd313..4f80b3ac9b 100644 --- a/activerecord/test/cases/transactions_test.rb +++ b/activerecord/test/cases/transactions_test.rb @@ -511,9 +511,10 @@ class TransactionTest < ActiveRecord::TestCase 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" diff --git a/activerecord/test/cases/validations/i18n_validation_test.rb b/activerecord/test/cases/validations/i18n_validation_test.rb index bdae806f7c..fd88a3ea67 100644 --- a/activerecord/test/cases/validations/i18n_validation_test.rb +++ b/activerecord/test/cases/validations/i18n_validation_test.rb @@ -36,7 +36,7 @@ class I18nValidationTest < ActiveRecord::TestCase # 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 } }, {}], diff --git a/activerecord/test/cases/view_test.rb b/activerecord/test/cases/view_test.rb index 3cbfbc22c6..0e38cee334 100644 --- a/activerecord/test/cases/view_test.rb +++ b/activerecord/test/cases/view_test.rb @@ -149,7 +149,7 @@ if ActiveRecord::Base.connection.supports_views? 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 diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index ecf0c0eec7..3a2c7b0e74 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -102,9 +102,9 @@ module ActiveSupport 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 @@ module ActiveSupport 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 before_foo 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 before_foo 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 @@ module ActiveSupport 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 @@ module ActiveSupport end end - # An Array with a compile method. + # An Array with a compile method. class CallbackChain #:nodoc:# include Enumerable diff --git a/activesupport/lib/active_support/concurrency/share_lock.rb b/activesupport/lib/active_support/concurrency/share_lock.rb index 8969ebb080..4318523b07 100644 --- a/activesupport/lib/active_support/concurrency/share_lock.rb +++ b/activesupport/lib/active_support/concurrency/share_lock.rb @@ -199,7 +199,7 @@ module ActiveSupport 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) diff --git a/activesupport/lib/active_support/duration/iso8601_parser.rb b/activesupport/lib/active_support/duration/iso8601_parser.rb index df56f13b8d..e96cb8e883 100644 --- a/activesupport/lib/active_support/duration/iso8601_parser.rb +++ b/activesupport/lib/active_support/duration/iso8601_parser.rb @@ -84,7 +84,7 @@ module ActiveSupport 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 @@ module ActiveSupport 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? diff --git a/activesupport/lib/active_support/file_update_checker.rb b/activesupport/lib/active_support/file_update_checker.rb index 0b7d67e37a..98aceabe21 100644 --- a/activesupport/lib/active_support/file_update_checker.rb +++ b/activesupport/lib/active_support/file_update_checker.rb @@ -105,13 +105,13 @@ module ActiveSupport @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 diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb index 79cede2a0c..c80243c40a 100644 --- a/activesupport/lib/active_support/inflector/methods.rb +++ b/activesupport/lib/active_support/inflector/methods.rb @@ -356,11 +356,11 @@ module ActiveSupport 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 @@ module ActiveSupport 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 diff --git a/activesupport/lib/active_support/key_generator.rb b/activesupport/lib/active_support/key_generator.rb index 156cfc32f8..1064398d8c 100644 --- a/activesupport/lib/active_support/key_generator.rb +++ b/activesupport/lib/active_support/key_generator.rb @@ -51,8 +51,8 @@ module ActiveSupport 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 " \ diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb index 70864038e8..46b91806f6 100644 --- a/activesupport/lib/active_support/xml_mini.rb +++ b/activesupport/lib/active_support/xml_mini.rb @@ -157,7 +157,7 @@ module ActiveSupport "#{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" diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb index c698780da8..10498c9be0 100644 --- a/activesupport/lib/active_support/xml_mini/jdom.rb +++ b/activesupport/lib/active_support/xml_mini/jdom.rb @@ -52,12 +52,12 @@ module ActiveSupport 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 @@ module ActiveSupport 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 @@ module ActiveSupport 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 @@ module ActiveSupport 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 @@ module ActiveSupport 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 @@ module ActiveSupport 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 @@ module ActiveSupport 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 diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb index 39e8c611e5..1c5a4f378c 100644 --- a/activesupport/test/inflector_test.rb +++ b/activesupport/test/inflector_test.rb @@ -245,12 +245,12 @@ class InflectorTest < ActiveSupport::TestCase 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| -- cgit v1.2.3