Module: ActiveRecord::AttributeMethods::ClassMethods

Defined in:
lib/active_record/attribute_methods.rb

Instance Method Summary collapse

Instance Method Details

#_has_attribute?(attr_name) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


255
256
257
# File 'lib/active_record/attribute_methods.rb', line 255

def _has_attribute?(attr_name) # :nodoc:
  attribute_types.key?(attr_name)
end

#alias_attribute(new_name, old_name) ⇒ Object



53
54
55
56
57
58
59
60
61
# File 'lib/active_record/attribute_methods.rb', line 53

def alias_attribute(new_name, old_name)
  super

  if @alias_attributes_mass_generated
    ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |code_generator|
      generate_alias_attribute_methods(code_generator, new_name, old_name)
    end
  end
end

#alias_attribute_method_definition(code_generator, pattern, new_name, old_name) ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/active_record/attribute_methods.rb', line 85

def alias_attribute_method_definition(code_generator, pattern, new_name, old_name)
  method_name = pattern.method_name(new_name).to_s
  target_name = pattern.method_name(old_name).to_s
  parameters = pattern.parameters
  old_name = old_name.to_s

  method_defined = method_defined?(target_name) || private_method_defined?(target_name)
  manually_defined = method_defined &&
    !self.instance_method(target_name).owner.is_a?(GeneratedAttributeMethods)
  reserved_method_name = ::ActiveRecord::AttributeMethods.dangerous_attribute_methods.include?(target_name)

  if !abstract_class? && !has_attribute?(old_name)
    # We only need to issue this deprecation warning once, so we issue it when defining the original reader method.
    should_warn = target_name == old_name
    if should_warn
      ActiveRecord.deprecator.warn(
        "#{self} model aliases `#{old_name}`, but `#{old_name}` is not an attribute. " \
        "Starting in Rails 7.2, alias_attribute with non-attribute targets will raise. " \
        "Use `alias_method :#{new_name}, :#{old_name}` or define the method manually."
      )
    end
    super
  elsif manually_defined && !reserved_method_name
    aliased_method_redefined_as_well = method_defined_within?(method_name, self)
    return if aliased_method_redefined_as_well

    ActiveRecord.deprecator.warn(
      "#{self} model aliases `#{old_name}` and has a method called `#{target_name}` defined. " \
      "Starting in Rails 7.2 `#{method_name}` will not be calling `#{target_name}` anymore. " \
      "You may want to additionally define `#{method_name}` to preserve the current behavior."
    )
    super
  else
    define_proxy_call(code_generator, method_name, pattern.proxy_target, parameters, old_name,
      namespace: :proxy_alias_attribute
    )
  end
end

#attribute_method?(attribute) ⇒ Boolean

Returns true if attribute is an attribute method and table exists, false otherwise.

class Person < ActiveRecord::Base
end

Person.attribute_method?('name')   # => true
Person.attribute_method?(:age=)    # => true
Person.attribute_method?(:nothing) # => false

Returns:

  • (Boolean)


219
220
221
# File 'lib/active_record/attribute_methods.rb', line 219

def attribute_method?(attribute)
  super || (table_exists? && column_names.include?(attribute.to_s.delete_suffix("=")))
end

#attribute_namesObject

Returns an array of column names as strings if it’s not an abstract class and table exists. Otherwise it returns an empty array.

class Person < ActiveRecord::Base
end

Person.attribute_names
# => ["id", "created_at", "updated_at", "name", "age"]


231
232
233
234
235
236
237
# File 'lib/active_record/attribute_methods.rb', line 231

def attribute_names
  @attribute_names ||= if !abstract_class? && table_exists?
    attribute_types.keys
  else
    []
  end.freeze
end

#dangerous_attribute_method?(name) ⇒ Boolean

A method name is ‘dangerous’ if it is already (re)defined by Active Record, but not by any ancestors. (So ‘puts’ is not dangerous but ‘save’ is.)

Returns:

  • (Boolean)


178
179
180
# File 'lib/active_record/attribute_methods.rb', line 178

def dangerous_attribute_method?(name) # :nodoc:
  ::ActiveRecord::AttributeMethods.dangerous_attribute_methods.include?(name.to_s)
end

#dangerous_class_method?(method_name) ⇒ Boolean

A class method is ‘dangerous’ if it is already (re)defined by Active Record, but not by any ancestors. (So ‘puts’ is not dangerous but ‘new’ is.)

Returns:

  • (Boolean)


196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/active_record/attribute_methods.rb', line 196

def dangerous_class_method?(method_name)
  return true if RESTRICTED_CLASS_METHODS.include?(method_name.to_s)

  if Base.respond_to?(method_name, true)
    if Object.respond_to?(method_name, true)
      Base.method(method_name).owner != Object.method(method_name).owner
    else
      true
    end
  else
    false
  end
end

#define_attribute_methodsObject

Generates all the attribute related methods for columns in the database accessors, mutators and query methods.



126
127
128
129
130
131
132
133
134
135
136
# File 'lib/active_record/attribute_methods.rb', line 126

def define_attribute_methods # :nodoc:
  return false if @attribute_methods_generated
  # Use a mutex; we don't want two threads simultaneously trying to define
  # attribute methods.
  generated_attribute_methods.synchronize do
    return false if @attribute_methods_generated
    superclass.define_attribute_methods unless base_class?
    super(attribute_names)
    @attribute_methods_generated = true
  end
end

#eagerly_generate_alias_attribute_methods(_new_name, _old_name) ⇒ Object

:nodoc:



63
64
65
# File 'lib/active_record/attribute_methods.rb', line 63

def eagerly_generate_alias_attribute_methods(_new_name, _old_name) # :nodoc:
  # alias attributes in Active Record are lazily generated
end

#generate_alias_attributesObject

:nodoc:



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/active_record/attribute_methods.rb', line 67

def generate_alias_attributes # :nodoc:
  superclass.generate_alias_attributes unless superclass == Base
  return if @alias_attributes_mass_generated

  generated_attribute_methods.synchronize do
    return if @alias_attributes_mass_generated
    ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |code_generator|
      aliases_by_attribute_name.each do |old_name, new_names|
        new_names.each do |new_name|
          generate_alias_attribute_methods(code_generator, new_name, old_name)
        end
      end
    end

    @alias_attributes_mass_generated = true
  end
end

#has_attribute?(attr_name) ⇒ Boolean

Returns true if the given attribute exists, otherwise false.

class Person < ActiveRecord::Base
  alias_attribute :new_name, :name
end

Person.has_attribute?('name')     # => true
Person.has_attribute?('new_name') # => true
Person.has_attribute?(:age)       # => true
Person.has_attribute?(:nothing)   # => false

Returns:

  • (Boolean)


249
250
251
252
253
# File 'lib/active_record/attribute_methods.rb', line 249

def has_attribute?(attr_name)
  attr_name = attr_name.to_s
  attr_name = attribute_aliases[attr_name] || attr_name
  attribute_types.key?(attr_name)
end

#initialize_generated_modulesObject

:nodoc:



43
44
45
46
47
48
49
50
51
# File 'lib/active_record/attribute_methods.rb', line 43

def initialize_generated_modules # :nodoc:
  @generated_attribute_methods = const_set(:GeneratedAttributeMethods, GeneratedAttributeMethods.new)
  private_constant :GeneratedAttributeMethods
  @attribute_methods_generated = false
  @alias_attributes_mass_generated = false
  include @generated_attribute_methods

  super
end

#instance_method_already_implemented?(method_name) ⇒ Boolean

Raises an ActiveRecord::DangerousAttributeError exception when an Active Record method is defined in the model, otherwise false.

class Person < ActiveRecord::Base
  def save
    'already defined by Active Record'
  end
end

Person.instance_method_already_implemented?(:save)
# => ActiveRecord::DangerousAttributeError: save is defined by Active Record. Check to make sure that you don't have an attribute or method with the same name.

Person.instance_method_already_implemented?(:name)
# => false

Returns:

  • (Boolean)


160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/active_record/attribute_methods.rb', line 160

def instance_method_already_implemented?(method_name)
  if dangerous_attribute_method?(method_name)
    raise DangerousAttributeError, "#{method_name} is defined by Active Record. Check to make sure that you don't have an attribute or method with the same name."
  end

  if superclass == Base
    super
  else
    # If ThisClass < ... < SomeSuperClass < ... < Base and SomeSuperClass
    # defines its own attribute method, then we don't want to override that.
    defined = method_defined_within?(method_name, superclass, Base) &&
      ! superclass.instance_method(method_name).owner.is_a?(GeneratedAttributeMethods)
    defined || super
  end
end

#method_defined_within?(name, klass, superklass = klass.superclass) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


182
183
184
185
186
187
188
189
190
191
192
# File 'lib/active_record/attribute_methods.rb', line 182

def method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc:
  if klass.method_defined?(name) || klass.private_method_defined?(name)
    if superklass.method_defined?(name) || superklass.private_method_defined?(name)
      klass.instance_method(name).owner != superklass.instance_method(name).owner
    else
      true
    end
  else
    false
  end
end

#undefine_attribute_methodsObject

:nodoc:



138
139
140
141
142
143
144
# File 'lib/active_record/attribute_methods.rb', line 138

def undefine_attribute_methods # :nodoc:
  generated_attribute_methods.synchronize do
    super if defined?(@attribute_methods_generated) && @attribute_methods_generated
    @attribute_methods_generated = false
    @alias_attributes_mass_generated = false
  end
end