diff options
author | Ryuta Kamizono <kamipo@gmail.com> | 2016-09-14 17:57:52 +0900 |
---|---|---|
committer | Ryuta Kamizono <kamipo@gmail.com> | 2016-09-14 18:26:32 +0900 |
commit | 3464cd5c288323ca115a4929d1e6b435c4afc8d4 (patch) | |
tree | ad2e1380ede0a1789c7c1a7bffb1f43b465c4105 /activerecord | |
parent | 92703a9ea5d8b96f30e0b706b801c9185ef14f0e (diff) | |
download | rails-3464cd5c288323ca115a4929d1e6b435c4afc8d4.tar.gz rails-3464cd5c288323ca115a4929d1e6b435c4afc8d4.tar.bz2 rails-3464cd5c288323ca115a4929d1e6b435c4afc8d4.zip |
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.
Diffstat (limited to 'activerecord')
31 files changed, 1462 insertions, 1461 deletions
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: - # * <tt>Customer#balance, Customer#balance=(money)</tt> - # * <tt>Customer#address, Customer#address=(address)</tt> - # - # These methods will operate with value objects like the ones described below: - # - # class Money - # include Comparable - # attr_reader :amount, :currency - # EXCHANGE_RATES = { "USD_TO_DKK" => 6 } - # - # def initialize(amount, currency = "USD") - # @amount, @currency = amount, currency - # end - # - # def exchange_to(other_currency) - # exchanged_amount = (amount * EXCHANGE_RATES["#{currency}_TO_#{other_currency}"]).floor - # Money.new(exchanged_amount, other_currency) - # end - # - # def ==(other_money) - # amount == other_money.amount && currency == other_money.currency - # end - # - # def <=>(other_money) - # if currency == other_money.currency - # amount <=> other_money.amount - # else - # amount <=> other_money.exchange_to(currency).amount - # end - # end - # end - # - # class Address - # attr_reader :street, :city - # def initialize(street, city) - # @street, @city = street, city - # end - # - # def close_to?(other_address) - # city == other_address.city - # end - # - # def ==(other_address) - # city == other_address.city && street == other_address.street - # end - # end - # - # Now it's possible to access attributes from the database through the value objects instead. If - # you choose to name the composition the same as the attribute's name, it will be the only way to - # access that attribute. That's the case with our +balance+ attribute. You interact with the value - # objects just like you would with any other attribute: - # - # customer.balance = Money.new(20) # sets the Money value object and the attribute - # customer.balance # => Money value object - # customer.balance.exchange_to("DKK") # => Money.new(120, "DKK") - # customer.balance > Money.new(10) # => true - # customer.balance == Money.new(20) # => true - # customer.balance < Money.new(5) # => false - # - # Value objects can also be composed of multiple attributes, such as the case of Address. The order - # of the mappings will determine the order of the parameters. - # - # customer.address_street = "Hyancintvej" - # customer.address_city = "Copenhagen" - # customer.address # => Address.new("Hyancintvej", "Copenhagen") - # - # customer.address = Address.new("May Street", "Chicago") - # customer.address_street # => "May Street" - # customer.address_city # => "Chicago" - # - # == Writing value objects - # - # Value objects are immutable and interchangeable objects that represent a given value, such as - # a Money object representing $5. Two Money objects both representing $5 should be equal (through - # methods such as <tt>==</tt> and <tt><=></tt> from Comparable if ranking makes sense). This is - # unlike entity objects where equality is determined by identity. An entity class such as Customer can - # easily have two different objects that both have an address on Hyancintvej. Entity identity is - # determined by object or relational unique identifiers (such as primary keys). Normal - # ActiveRecord::Base classes are entity objects. - # - # It's also important to treat the value objects as immutable. Don't allow the Money object to have - # its amount changed after creation. Create a new Money object with the new value instead. The - # <tt>Money#exchange_to</tt> method is an example of this. It returns a new value object instead of changing - # its own values. Active Record won't persist value objects that have been changed through means - # other than the writer method. - # - # The immutable requirement is enforced by Active Record by freezing any object assigned as a value - # object. Attempting to change it afterwards will result in a +RuntimeError+. - # - # Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not - # keeping value objects immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable - # - # == Custom constructors and converters - # - # By default value objects are initialized by calling the <tt>new</tt> constructor of the value - # class passing each of the mapped attributes, in the order specified by the <tt>:mapping</tt> - # option, as arguments. If the value class doesn't support this convention then #composed_of allows - # a custom constructor to be specified. - # - # When a new value is assigned to the value object, the default assumption is that the new value - # is an instance of the value class. Specifying a custom converter allows the new value to be automatically - # converted to an instance of value class if necessary. - # - # For example, the +NetworkResource+ model has +network_address+ and +cidr_range+ attributes that should be - # aggregated using the +NetAddr::CIDR+ value class (http://www.rubydoc.info/gems/netaddr/1.5.0/NetAddr/CIDR). - # The constructor for the value class is called +create+ and it expects a CIDR address string as a parameter. - # New values can be assigned to the value object using either another +NetAddr::CIDR+ object, a string - # or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to meet - # these requirements: - # - # class NetworkResource < ActiveRecord::Base - # composed_of :cidr, - # class_name: 'NetAddr::CIDR', - # mapping: [ %w(network_address network), %w(cidr_range bits) ], - # allow_nil: true, - # constructor: Proc.new { |network_address, cidr_range| NetAddr::CIDR.create("#{network_address}/#{cidr_range}") }, - # converter: Proc.new { |value| NetAddr::CIDR.create(value.is_a?(Array) ? value.join('/') : value) } - # end - # - # # This calls the :constructor - # network_resource = NetworkResource.new(network_address: '192.168.0.1', cidr_range: 24) - # - # # These assignments will both use the :converter - # network_resource.cidr = [ '192.168.2.1', 8 ] - # network_resource.cidr = '192.168.0.1/24' - # - # # This assignment won't use the :converter as the value is already an instance of the value class - # network_resource.cidr = NetAddr::CIDR.create('192.168.2.1/8') - # - # # Saving and then reloading will use the :constructor on reload - # network_resource.save - # network_resource.reload - # - # == Finding records by a value object - # - # Once a #composed_of relationship is specified for a model, records can be loaded from the database - # by specifying an instance of the value object in the conditions hash. The following example - # finds all customers with +balance_amount+ equal to 20 and +balance_currency+ equal to "USD": - # - # Customer.where(balance: Money.new(20, "USD")) - # + # Active Record implements aggregation through a macro-like class method called #composed_of + # for representing attributes as value objects. It expresses relationships like "Account [is] + # composed of Money [among other things]" or "Person [is] composed of [an] address". Each call + # to the macro adds a description of how the value objects are created from the attributes of + # the entity object (when the entity is initialized either as a new object or from finding an + # existing object) and how it can be turned back into attributes (when the entity is saved to + # the database). + # + # class Customer < ActiveRecord::Base + # composed_of :balance, class_name: "Money", mapping: %w(amount currency) + # composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ] + # end + # + # The customer class now has the following methods to manipulate the value objects: + # * <tt>Customer#balance, Customer#balance=(money)</tt> + # * <tt>Customer#address, Customer#address=(address)</tt> + # + # These methods will operate with value objects like the ones described below: + # + # class Money + # include Comparable + # attr_reader :amount, :currency + # EXCHANGE_RATES = { "USD_TO_DKK" => 6 } + # + # def initialize(amount, currency = "USD") + # @amount, @currency = amount, currency + # end + # + # def exchange_to(other_currency) + # exchanged_amount = (amount * EXCHANGE_RATES["#{currency}_TO_#{other_currency}"]).floor + # Money.new(exchanged_amount, other_currency) + # end + # + # def ==(other_money) + # amount == other_money.amount && currency == other_money.currency + # end + # + # def <=>(other_money) + # if currency == other_money.currency + # amount <=> other_money.amount + # else + # amount <=> other_money.exchange_to(currency).amount + # end + # end + # end + # + # class Address + # attr_reader :street, :city + # def initialize(street, city) + # @street, @city = street, city + # end + # + # def close_to?(other_address) + # city == other_address.city + # end + # + # def ==(other_address) + # city == other_address.city && street == other_address.street + # end + # end + # + # Now it's possible to access attributes from the database through the value objects instead. If + # you choose to name the composition the same as the attribute's name, it will be the only way to + # access that attribute. That's the case with our +balance+ attribute. You interact with the value + # objects just like you would with any other attribute: + # + # customer.balance = Money.new(20) # sets the Money value object and the attribute + # customer.balance # => Money value object + # customer.balance.exchange_to("DKK") # => Money.new(120, "DKK") + # customer.balance > Money.new(10) # => true + # customer.balance == Money.new(20) # => true + # customer.balance < Money.new(5) # => false + # + # Value objects can also be composed of multiple attributes, such as the case of Address. The order + # of the mappings will determine the order of the parameters. + # + # customer.address_street = "Hyancintvej" + # customer.address_city = "Copenhagen" + # customer.address # => Address.new("Hyancintvej", "Copenhagen") + # + # customer.address = Address.new("May Street", "Chicago") + # customer.address_street # => "May Street" + # customer.address_city # => "Chicago" + # + # == Writing value objects + # + # Value objects are immutable and interchangeable objects that represent a given value, such as + # a Money object representing $5. Two Money objects both representing $5 should be equal (through + # methods such as <tt>==</tt> and <tt><=></tt> from Comparable if ranking makes sense). This is + # unlike entity objects where equality is determined by identity. An entity class such as Customer can + # easily have two different objects that both have an address on Hyancintvej. Entity identity is + # determined by object or relational unique identifiers (such as primary keys). Normal + # ActiveRecord::Base classes are entity objects. + # + # It's also important to treat the value objects as immutable. Don't allow the Money object to have + # its amount changed after creation. Create a new Money object with the new value instead. The + # <tt>Money#exchange_to</tt> method is an example of this. It returns a new value object instead of changing + # its own values. Active Record won't persist value objects that have been changed through means + # other than the writer method. + # + # The immutable requirement is enforced by Active Record by freezing any object assigned as a value + # object. Attempting to change it afterwards will result in a +RuntimeError+. + # + # Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not + # keeping value objects immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable + # + # == Custom constructors and converters + # + # By default value objects are initialized by calling the <tt>new</tt> constructor of the value + # class passing each of the mapped attributes, in the order specified by the <tt>:mapping</tt> + # option, as arguments. If the value class doesn't support this convention then #composed_of allows + # a custom constructor to be specified. + # + # When a new value is assigned to the value object, the default assumption is that the new value + # is an instance of the value class. Specifying a custom converter allows the new value to be automatically + # converted to an instance of value class if necessary. + # + # For example, the +NetworkResource+ model has +network_address+ and +cidr_range+ attributes that should be + # aggregated using the +NetAddr::CIDR+ value class (http://www.rubydoc.info/gems/netaddr/1.5.0/NetAddr/CIDR). + # The constructor for the value class is called +create+ and it expects a CIDR address string as a parameter. + # New values can be assigned to the value object using either another +NetAddr::CIDR+ object, a string + # or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to meet + # these requirements: + # + # class NetworkResource < ActiveRecord::Base + # composed_of :cidr, + # class_name: 'NetAddr::CIDR', + # mapping: [ %w(network_address network), %w(cidr_range bits) ], + # allow_nil: true, + # constructor: Proc.new { |network_address, cidr_range| NetAddr::CIDR.create("#{network_address}/#{cidr_range}") }, + # converter: Proc.new { |value| NetAddr::CIDR.create(value.is_a?(Array) ? value.join('/') : value) } + # end + # + # # This calls the :constructor + # network_resource = NetworkResource.new(network_address: '192.168.0.1', cidr_range: 24) + # + # # These assignments will both use the :converter + # network_resource.cidr = [ '192.168.2.1', 8 ] + # network_resource.cidr = '192.168.0.1/24' + # + # # This assignment won't use the :converter as the value is already an instance of the value class + # network_resource.cidr = NetAddr::CIDR.create('192.168.2.1/8') + # + # # Saving and then reloading will use the :constructor on reload + # network_resource.save + # network_resource.reload + # + # == Finding records by a value object + # + # Once a #composed_of relationship is specified for a model, records can be loaded from the database + # by specifying an instance of the value object in the conditions hash. The following example + # finds all customers with +balance_amount+ equal to 20 and +balance_currency+ equal to "USD": + # + # Customer.where(balance: Money.new(20, "USD")) + # module ClassMethods # Adds reader and writer methods for manipulating a value object: # <tt>composed_of :address</tt> adds <tt>address</tt> and <tt>address=(new_address)</tt> methods. 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 <tt>attr*</tt> - # 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: - # * <tt>Project#portfolio, Project#portfolio=(portfolio), Project#portfolio.nil?</tt> - # * <tt>Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?,</tt> - # * <tt>Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone),</tt> - # <tt>Project#milestones.delete(milestone), Project#milestones.destroy(milestone), Project#milestones.find(milestone_id),</tt> - # <tt>Project#milestones.build, Project#milestones.create</tt> - # * <tt>Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1),</tt> - # <tt>Project#categories.delete(category1), Project#categories.destroy(category1)</tt> - # - # === A word of warning - # - # Don't create associations that have the same name as {instance methods}[rdoc-ref:ActiveRecord::Core] of - # <tt>ActiveRecord::Base</tt>. Since the association adds a method with that name to - # its model, using an association with the same name as one provided by <tt>ActiveRecord::Base</tt> will override the method inherited through <tt>ActiveRecord::Base</tt> 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 <tt>ActiveRecord::Base</tt> 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 <tt>Project</tt>, then the module is - # named <tt>Project::GeneratedAssociationMethods</tt>. 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 <tt>:through</tt> 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 <tt>:through</tt>. 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 <tt>:autosave</tt> 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 <tt>:autosave</tt> 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 (<tt>new_record? == true</tt>). - # * 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 <tt>#build_association</tt> 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 <tt>push</tt> or similar) - # fails, then <tt>push</tt> returns +false+. - # * If saving fails while replacing the collection (via <tt>association=</tt>), 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 - # <tt>collection.build</tt> method (documented below). - # * All unsaved (<tt>new_record? == true</tt>) members of the collection are automatically - # saved when the parent is saved. - # - # == Customizing the query - # - # \Associations are built from <tt>Relation</tt> 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 <tt>-> { ... }</tt> 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): - # - # * <tt>record.association(:items).owner</tt> - Returns the object the association is part of. - # * <tt>record.association(:items).reflection</tt> - Returns the reflection object that describes the association. - # * <tt>record.association(:items).target</tt> - 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 <tt>record</tt> as - # above. In this case, you can access <tt>proxy_association</tt>. For example, - # <tt>record.association(:items)</tt> and <tt>record.items.proxy_association</tt> will return - # the same object, allowing you to make calls like <tt>proxy_association.owner</tt> inside - # association extensions. - # - # == Association Join Models - # - # Has Many associations can be configured with the <tt>:through</tt> 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 - # <tt>:inverse_of</tt> option on the #belongs_to, which will mean that the following example - # works correctly (where <tt>tags</tt> is a #has_many <tt>:through</tt> association): - # - # @post = Post.first - # @tag = @post.tags.build name: "ruby" - # @tag.save - # - # The last line ought to save the through record (a <tt>Tagging</tt>). This will only work if the - # <tt>:inverse_of</tt> is set: - # - # class Tagging < ActiveRecord::Base - # belongs_to :post - # belongs_to :tag, inverse_of: :taggings - # end - # - # If you do not set the <tt>:inverse_of</tt> 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 - # <tt>AssociationReflection::INVALID_AUTOMATIC_INVERSE_OPTIONS</tt> 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 <tt>:inverse_of</tt> option to <tt>false</tt> like so: - # - # class Tagging < ActiveRecord::Base - # belongs_to :tag, inverse_of: false - # end - # - # == Nested \Associations - # - # You can actually specify *any* association with the <tt>:through</tt> option, including an - # association which has a <tt>:through</tt> 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 <tt>Commenter</tt> in the example above, there would be no way to tell how to set up the - # intermediate <tt>Post</tt> and <tt>Comment</tt> 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 <tt>attachable_type=</tt> 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 <tt>:author</tt> - # 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 <tt>LEFT OUTER JOIN</tt> 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: - # <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and - # <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. 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 - # <tt>order: "author.name DESC"</tt> will work but <tt>order: "name DESC"</tt> will not. - # - # If you want to load all posts (including posts with no approved comments), then write - # your own <tt>LEFT OUTER JOIN</tt> query using <tt>ON</tt>: - # - # 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 <tt>:limit</tt> 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 <tt>#{reflection_name}_#{parent_table_name}</tt>. - # 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 <tt>_join</tt> 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 <tt>Firm#clients</tt> is called, it will in turn call - # <tt>MyApplication::Business::Client.find_all_by_firm_id(firm.id)</tt>. - # 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 <tt>t.dungeon</tt> 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 <tt>:inverse_of</tt> support: - # - # * does not work with <tt>:through</tt> associations. - # * does not work with <tt>:polymorphic</tt> 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 <tt>:dependent</tt> 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 <tt>:dependent</tt> 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 <tt>:dependent</tt> is implemented using Rails' callback - # system, which works by processing callbacks in order. Therefore, other - # callbacks declared either before or after the <tt>:dependent</tt> option - # can affect what it does. - # - # Note that <tt>:dependent</tt> option is ignored for #has_one <tt>:through</tt> associations. - # - # === Delete or destroy? - # - # #has_many and #has_and_belongs_to_many associations have the methods <tt>destroy</tt>, - # <tt>delete</tt>, <tt>destroy_all</tt> and <tt>delete_all</tt>. - # - # For #has_and_belongs_to_many, <tt>delete</tt> and <tt>destroy</tt> are the same: they - # cause the records in the join table to be removed. - # - # For #has_many, <tt>destroy</tt> and <tt>destroy_all</tt> will always call the <tt>destroy</tt> method of the - # record(s) being removed so that callbacks are run. However <tt>delete</tt> and <tt>delete_all</tt> will either - # do the deletion according to the strategy specified by the <tt>:dependent</tt> option, or - # if no <tt>:dependent</tt> 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 <tt>:through</tt>, where the default strategy is <tt>delete_all</tt> (delete - # the join records, without running their callbacks). - # - # There is also a <tt>clear</tt> method which is the same as <tt>delete_all</tt>, 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 <tt>:through</tt> - # 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 - # <i>link</i> 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 - # <tt>:through</tt>, 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 <tt>post.tags.delete(Tag.find_by(name: 'food'))</tt> - # 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 <tt>:through</tt> 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 <tt>:through</tt>, if you want to delete the - # associated records themselves, you can always do something along the lines of - # <tt>person.tasks.each(&:destroy)</tt>. - # - # == Type safety with ActiveRecord::AssociationTypeMismatch - # - # If you attempt to assign an object to an association that doesn't match the inferred - # or specified <tt>:class_name</tt>, 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 <tt>attr*</tt> + # 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: + # * <tt>Project#portfolio, Project#portfolio=(portfolio), Project#portfolio.nil?</tt> + # * <tt>Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?,</tt> + # * <tt>Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone),</tt> + # <tt>Project#milestones.delete(milestone), Project#milestones.destroy(milestone), Project#milestones.find(milestone_id),</tt> + # <tt>Project#milestones.build, Project#milestones.create</tt> + # * <tt>Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1),</tt> + # <tt>Project#categories.delete(category1), Project#categories.destroy(category1)</tt> + # + # === A word of warning + # + # Don't create associations that have the same name as {instance methods}[rdoc-ref:ActiveRecord::Core] of + # <tt>ActiveRecord::Base</tt>. Since the association adds a method with that name to + # its model, using an association with the same name as one provided by <tt>ActiveRecord::Base</tt> will override the method inherited through <tt>ActiveRecord::Base</tt> 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 <tt>ActiveRecord::Base</tt> 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 <tt>Project</tt>, then the module is + # named <tt>Project::GeneratedAssociationMethods</tt>. 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 <tt>:through</tt> 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 <tt>:through</tt>. 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 <tt>:autosave</tt> 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 <tt>:autosave</tt> 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 (<tt>new_record? == true</tt>). + # * 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 <tt>#build_association</tt> 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 <tt>push</tt> or similar) + # fails, then <tt>push</tt> returns +false+. + # * If saving fails while replacing the collection (via <tt>association=</tt>), 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 + # <tt>collection.build</tt> method (documented below). + # * All unsaved (<tt>new_record? == true</tt>) members of the collection are automatically + # saved when the parent is saved. + # + # == Customizing the query + # + # \Associations are built from <tt>Relation</tt> 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 <tt>-> { ... }</tt> 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): + # + # * <tt>record.association(:items).owner</tt> - Returns the object the association is part of. + # * <tt>record.association(:items).reflection</tt> - Returns the reflection object that describes the association. + # * <tt>record.association(:items).target</tt> - 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 <tt>record</tt> as + # above. In this case, you can access <tt>proxy_association</tt>. For example, + # <tt>record.association(:items)</tt> and <tt>record.items.proxy_association</tt> will return + # the same object, allowing you to make calls like <tt>proxy_association.owner</tt> inside + # association extensions. + # + # == Association Join Models + # + # Has Many associations can be configured with the <tt>:through</tt> 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 + # <tt>:inverse_of</tt> option on the #belongs_to, which will mean that the following example + # works correctly (where <tt>tags</tt> is a #has_many <tt>:through</tt> association): + # + # @post = Post.first + # @tag = @post.tags.build name: "ruby" + # @tag.save + # + # The last line ought to save the through record (a <tt>Tagging</tt>). This will only work if the + # <tt>:inverse_of</tt> is set: + # + # class Tagging < ActiveRecord::Base + # belongs_to :post + # belongs_to :tag, inverse_of: :taggings + # end + # + # If you do not set the <tt>:inverse_of</tt> 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 + # <tt>AssociationReflection::INVALID_AUTOMATIC_INVERSE_OPTIONS</tt> 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 <tt>:inverse_of</tt> option to <tt>false</tt> like so: + # + # class Tagging < ActiveRecord::Base + # belongs_to :tag, inverse_of: false + # end + # + # == Nested \Associations + # + # You can actually specify *any* association with the <tt>:through</tt> option, including an + # association which has a <tt>:through</tt> 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 <tt>Commenter</tt> in the example above, there would be no way to tell how to set up the + # intermediate <tt>Post</tt> and <tt>Comment</tt> 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 <tt>attachable_type=</tt> 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 <tt>:author</tt> + # 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 <tt>LEFT OUTER JOIN</tt> 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: + # <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and + # <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. 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 + # <tt>order: "author.name DESC"</tt> will work but <tt>order: "name DESC"</tt> will not. + # + # If you want to load all posts (including posts with no approved comments), then write + # your own <tt>LEFT OUTER JOIN</tt> query using <tt>ON</tt>: + # + # 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 <tt>:limit</tt> 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 <tt>#{reflection_name}_#{parent_table_name}</tt>. + # 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 <tt>_join</tt> 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 <tt>Firm#clients</tt> is called, it will in turn call + # <tt>MyApplication::Business::Client.find_all_by_firm_id(firm.id)</tt>. + # 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 <tt>t.dungeon</tt> 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 <tt>:inverse_of</tt> support: + # + # * does not work with <tt>:through</tt> associations. + # * does not work with <tt>:polymorphic</tt> 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 <tt>:dependent</tt> 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 <tt>:dependent</tt> 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 <tt>:dependent</tt> is implemented using Rails' callback + # system, which works by processing callbacks in order. Therefore, other + # callbacks declared either before or after the <tt>:dependent</tt> option + # can affect what it does. + # + # Note that <tt>:dependent</tt> option is ignored for #has_one <tt>:through</tt> associations. + # + # === Delete or destroy? + # + # #has_many and #has_and_belongs_to_many associations have the methods <tt>destroy</tt>, + # <tt>delete</tt>, <tt>destroy_all</tt> and <tt>delete_all</tt>. + # + # For #has_and_belongs_to_many, <tt>delete</tt> and <tt>destroy</tt> are the same: they + # cause the records in the join table to be removed. + # + # For #has_many, <tt>destroy</tt> and <tt>destroy_all</tt> will always call the <tt>destroy</tt> method of the + # record(s) being removed so that callbacks are run. However <tt>delete</tt> and <tt>delete_all</tt> will either + # do the deletion according to the strategy specified by the <tt>:dependent</tt> option, or + # if no <tt>:dependent</tt> 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 <tt>:through</tt>, where the default strategy is <tt>delete_all</tt> (delete + # the join records, without running their callbacks). + # + # There is also a <tt>clear</tt> method which is the same as <tt>delete_all</tt>, 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 <tt>:through</tt> + # 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 + # <i>link</i> 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 + # <tt>:through</tt>, 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 <tt>post.tags.delete(Tag.find_by(name: 'food'))</tt> + # 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 <tt>:through</tt> 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 <tt>:through</tt>, if you want to delete the + # associated records themselves, you can always do something along the lines of + # <tt>person.tasks.each(&:destroy)</tt>. + # + # == Type safety with ActiveRecord::AssociationTypeMismatch + # + # If you attempt to assign an object to an association that doesn't match the inferred + # or specified <tt>:class_name</tt>, 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 <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself. - # No such attribute would be set for objects of the Message class in that example. + # Sets the attribute used for single table inheritance to this class name if this is not the + # ActiveRecord::Base descendant. + # Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to + # do Reply.new without having to set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself. + # No such attribute would be set for objects of the Message class in that example. def ensure_proper_type klass = self.class if klass.finder_needs_type_condition? 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 <tt>:id</tt> that matches the existing record's - # id, then the existing record will be modified. If no <tt>:id</tt> is provided - # it will be replaced with a new record. If update_only is +true+ the existing - # record will be modified regardless of whether an <tt>:id</tt> is provided. - # - # If the given attributes include a matching <tt>:id</tt> attribute, or - # update_only is true, and a <tt>:_destroy</tt> key set to a truthy value, - # then the existing record will be marked for destruction. + # Assigns the given attributes to the association. + # + # If an associated record does not yet exist, one will be instantiated. If + # an associated record already exists, the method's behavior depends on + # the value of the update_only option. If update_only is +false+ and the + # given attributes include an <tt>:id</tt> that matches the existing record's + # id, then the existing record will be modified. If no <tt>:id</tt> is provided + # it will be replaced with a new record. If update_only is +true+ the existing + # record will be modified regardless of whether an <tt>:id</tt> is provided. + # + # If the given attributes include a matching <tt>:id</tt> attribute, or + # update_only is true, and a <tt>:_destroy</tt> key set to a truthy value, + # then the existing record will be marked for destruction. def assign_nested_attributes_for_one_to_one_association(association_name, attributes) options = self.nested_attributes_options[association_name] if attributes.respond_to?(:permitted?) @@ -424,33 +424,33 @@ module ActiveRecord end end - # Assigns the given attributes to the collection association. - # - # Hashes with an <tt>:id</tt> value matching an existing associated record - # will update that record. Hashes without an <tt>:id</tt> value will build - # a new record for the association. Hashes with a matching <tt>:id</tt> - # value and a <tt>:_destroy</tt> key set to a truthy value will mark the - # matched record for destruction. - # - # For example: - # - # assign_nested_attributes_for_collection_association(:people, { - # '1' => { id: '1', name: 'Peter' }, - # '2' => { name: 'John' }, - # '3' => { id: '2', _destroy: true } - # }) - # - # Will update the name of the Person with ID 1, build a new associated - # person with the name 'John', and mark the associated Person with ID 2 - # for destruction. - # - # Also accepts an Array of attribute hashes: - # - # assign_nested_attributes_for_collection_association(:people, [ - # { id: '1', name: 'Peter' }, - # { name: 'John' }, - # { id: '2', _destroy: true } - # ]) + # Assigns the given attributes to the collection association. + # + # Hashes with an <tt>:id</tt> value matching an existing associated record + # will update that record. Hashes without an <tt>:id</tt> value will build + # a new record for the association. Hashes with a matching <tt>:id</tt> + # value and a <tt>:_destroy</tt> key set to a truthy value will mark the + # matched record for destruction. + # + # For example: + # + # assign_nested_attributes_for_collection_association(:people, { + # '1' => { id: '1', name: 'Peter' }, + # '2' => { name: 'John' }, + # '3' => { id: '2', _destroy: true } + # }) + # + # Will update the name of the Person with ID 1, build a new associated + # person with the name 'John', and mark the associated Person with ID 2 + # for destruction. + # + # Also accepts an Array of attribute hashes: + # + # assign_nested_attributes_for_collection_association(:people, [ + # { id: '1', name: 'Peter' }, + # { name: 'John' }, + # { id: '2', _destroy: true } + # ]) def assign_nested_attributes_for_collection_association(association_name, attributes_collection) options = self.nested_attributes_options[association_name] if attributes_collection.respond_to?(:permitted?) @@ -511,12 +511,12 @@ 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 <tt>:reject_if</tt> proc exists for this - # association and evaluates to +true+. + # Determines if a new record should be rejected by checking + # has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this + # association and evaluates to +true+. def reject_new_record?(association_name, attributes) will_be_destroyed?(association_name, attributes) || call_reject_if(association_name, attributes) end - # Determines if a record with the particular +attributes+ should be - # rejected by calling the reject_if Symbol or Proc (if defined). - # The reject_if option is defined by +accepts_nested_attributes_for+. - # - # Returns false if there is a +destroy_flag+ on the attributes. + # Determines if a record with the particular +attributes+ should be + # rejected by calling the reject_if Symbol or Proc (if defined). + # The reject_if option is defined by +accepts_nested_attributes_for+. + # + # Returns false if there is a +destroy_flag+ on the attributes. def call_reject_if(association_name, attributes) return false if will_be_destroyed?(association_name, attributes) @@ -570,7 +570,7 @@ module ActiveRecord end end - # Only take into account the destroy flag if <tt>:allow_destroy</tt> is true + # Only take into account the destroy flag if <tt>:allow_destroy</tt> is true def will_be_destroyed?(association_name, attributes) allow_destroy?(association_name) && has_destroy_flag?(attributes) end 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 |