Module: ActiveModel::AttributeMethods::AttrNames

Defined in:
activemodel/lib/active_model/attribute_methods.rb

Overview

:nodoc:

Constant Summary collapse

DEF_SAFE_NAME =
/\A[a-zA-Z_]\w*\z/

Class Method Summary collapse

Class Method Details

.define_attribute_accessor_method(mod, attr_name, writer: false) ⇒ Object

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.


493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
# File 'activemodel/lib/active_model/attribute_methods.rb', line 493

def self.define_attribute_accessor_method(mod, attr_name, writer: false)
  method_name = "#{attr_name}#{'=' if writer}"
  if attr_name.ascii_only? && DEF_SAFE_NAME.match?(attr_name)
    yield method_name, "'#{attr_name}'.freeze"
  else
    safe_name = attr_name.unpack1("h*")
    const_name = "ATTR_#{safe_name}"
    const_set(const_name, attr_name) unless const_defined?(const_name)
    temp_method_name = "__temp__#{safe_name}#{'=' if writer}"
    attr_name_expr = "::ActiveModel::AttributeMethods::AttrNames::#{const_name}"
    yield temp_method_name, attr_name_expr
    mod.alias_method method_name, temp_method_name
    mod.undef_method temp_method_name
  end
end