ActiveRecord::Associations::ClassMethods (Module)

In: lib/active_record/associations.rb

Associations is a set of macro-like class methods for tying objects together through foreign keys. Each macro adds a range of methods to the class which are specialized according to the collection or association symbol and the options hash. It works much the same was as Ruby’s own attr* methods.

Simple example:

  class Project < ActiveRecord::Base
     belongs_to :portofolio
     has_one    :project_manager
     has_many   :milestones
  end

The project class now has the following methods:

  • Project#has_portofolio?, Project#portofolio?(portofolio), Project#portofolio
  • Project#has_project_manger?, Project#project_manager?(project_manager), Project#project_manager
  • Project#has_milestones?, Project#milestones_count, Project#milestones, Project.find_in_milestones(milestone_id)

Methods

Public Instance methods

Adds methods for retrival and query of collections of associated objects.

Example: A Firm class declares has_many :clients, which will add:

  • Firm#clients (similar to Clients.find_all "firm_id = #{id}")
  • Firm#has_clients? (similar to firm.clients.length > 0)
  • Firm#clients_count (similar to Client.count "firm_id = #{id}")
  • Firm.find_in_clients (similar to Client.find_with_constraint(id, "firm_id = #{id}")

The declaration can also include an options hash to specialize the generated methods.

Options are:

  • :foreign_key - specify the name of one or more output files. If multiple
  • :class_name - specify the name of one or more output files. If multiple
  • :constraint - specify the name of one or more output files. If multiple
  • :order - specify the name of one or more output files. If multiple
  • :finder_sql - specify the name of one or more output files. If multiple

[Source]

    # File lib/active_record/associations.rb, line 41
41:       def has_many(collection_id, options = {})
42:         validate_options([ :foreign_key, :class_name, :constraint, :order, :finder_sql ], options.keys)
43: 
44:         collection_name  = collection_id.id2name
45:         collection_class_name  = options[:class_name]  || class_name(collection_id.id2name)
46:         class_primary_key_name = options[:foreign_key] || name.downcase + "_id"
47: 
48:         if options[:finder_sql]
49:           collection_finder = "#{collection_class_name}.find_by_sql(\"#{options[:finder_sql]}\")"
50:           
51:           counter_sql = options[:finder_sql].gsub(/SELECT (.*) FROM/, "SELECT COUNT(*) FROM")
52:           collection_counter = "#{collection_class_name}.count_by_sql(\"#{counter_sql}\")"
53:         else
54:           collection_finder = "\#{collection_class_name}.find_all(\n\"\#{class_primary_key_name} = \\\#{id}\#{options[:constraint] ? \" AND \" + options[:constraint] : \"\"}\",\n\#{options[:order] ? \"\\\"\" + options[:order] + \"\\\"\" : \"nil\" }\n)\n"
55:           
56:           collection_counter = "#{collection_class_name}.count(\"#{class_primary_key_name} = \#{id}\")"
57:         end
58: 
59: 
60:         module_eval "\# Ex: clients\ndef \#{collection_name}\nif @\#{collection_name}.nil?\n@\#{collection_name} = \#{collection_finder}\nend\n\nreturn @\#{collection_name}\nend\n\n\# Ex: has_clients?\ndef has_\#{collection_name}?\n\#{collection_name}.length > 0\nend\n\n\# Ex: clients_count\ndef \#{collection_name}_count\nif @\#{collection_name}.nil?\n\#{collection_counter}\nelse\n@\#{collection_name}.length\nend\nend\n"
61: 
62:         # Can't use constrained finds with specialized finder SQL
63:         unless options[:finder_sql]
64:           module_eval "\# Ex: find_in_clients(id)\ndef find_in_\#{collection_name}(association_id)\n\#{collection_class_name}.find_with_constraint(\nassociation_id, \"\#{class_primary_key_name} = \\\#{id}\#{options[:constraint] ? \" AND \" + options[:constraint] : \"\"}\"\n)\nend\n"
65:         end
66:       end

[Source]

     # File lib/active_record/associations.rb, line 106
106:       def has_one(association_id, options = {})
107:         validate_options([ :class_name, :foreign_key, :remote ], options.keys)
108: 
109:         association_name           = association_id.id2name
110:         association_class_name       = options[:class_name]  || class_name(association_id.id2name)
111:         association_class_primary_key_name = options[:foreign_key] || association_class_name.downcase + "_id"
112:         class_primary_key_name       = options[:foreign_key] || name.downcase + "_id"
113:         
114:         if options[:remote]
115:           association_finder = "#{association_class_name}.find_first(\"#{class_primary_key_name} = \#{id}\")"
116:         else
117:           association_finder = "#{association_class_name}.find(#{association_class_primary_key_name})"
118:         end
119:         
120:         module_eval "\# Ex: credit_card\ndef \#{association_name}\nif @\#{association_name}.nil?\n@\#{association_name} = \#{association_finder}\nend\n\nreturn @\#{association_name}\nend\n\n\# Ex: has_credit_card?\ndef has_\#{association_name}?\nbegin\n!\#{association_name}.nil?\nrescue Exception\nfalse\nend\nend\n\n\# Ex: credit_card?(same_credit_card)\ndef \#{association_name}?(comparison_object)\nif comparison_object.kind_of?(\#{association_class_name})\nbegin\n\#{association_name}.id == comparison_object.id\nrescue Exception\nfalse\nend\nelse\nraise \"Comparison object is a \#{association_class_name}, should have been \\\#{comparison_object.class.name}\"\nend\nend\n"
121:       end

[Source]

     # File lib/active_record/associations.rb, line 155
155:       def belongs_to(association_id, options = {})
156:         validate_options([ :class_name, :foreign_key ], options.keys)
157: 
158:         association_name           = association_id.id2name
159:         association_class_name       = options[:class_name] || class_name(association_id.id2name)
160:         association_class_primary_key_name = options[:foreign_key] || association_class_name.downcase + "_id"
161:         
162:         module_eval "\# Ex: credit_card\ndef \#{association_name}\nif @\#{association_name}.nil?\n@\#{association_name} = \#{association_class_name}.find(\#{association_class_primary_key_name})\nend\n\nreturn @\#{association_name}\nend\n\n\# Ex: has_credit_card?\ndef has_\#{association_name}?\n!\#{association_name}.nil?\nend\n\n\# Ex: credit_card?(same_credit_card)\ndef \#{association_name}?(comparison_object)\nif comparison_object.kind_of?(\#{association_class_name})\n\#{association_name}.id == comparison_object.id\nelse\nraise \"Comparison object is a \\\#{comparison_object.class.name}, should have been \#{association_class_name}\"\nend\nend\n"
163:       end

[Validate]