Module: ActiveRecord::AttributeMethods::Read

Extended by:
ActiveSupport::Concern
Defined in:
lib/active_record/attribute_methods/read.rb

Defined Under Namespace

Modules: ClassMethods

Constant Summary collapse

ReaderMethodCache =
Class.new(AttributeMethodCache) {
  private
  # 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_cache in read_attribute.
  def method_body(method_name, const_name)
    <<-EOMETHOD
    def #{method_name}
      name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{const_name}
      read_attribute(name) { |n| missing_attribute(n, caller) }
    end
    EOMETHOD
  end
}.new
ATTRIBUTE_TYPES_CACHED_BY_DEFAULT =
[:datetime, :timestamp, :time, :date]

Instance Method Summary collapse

Instance Method Details

#read_attribute(attr_name) ⇒ Object

Returns the value of the attribute identified by attr_name after it has been typecast (for example, “2004-12-12” in a date column is cast to a date object, like Date.new(2004, 12, 12)).



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/active_record/attribute_methods/read.rb', line 107

def read_attribute(attr_name)
  # If it's cached, just return it
  # We use #[] first as a perf optimization for non-nil values. See https://gist.github.com/jonleighton/3552829.
  name = attr_name.to_s
  @attributes_cache[name] || @attributes_cache.fetch(name) {
    column = @column_types_override[name] if @column_types_override
    column ||= @column_types[name]

    return @attributes.fetch(name) {
      if name == 'id' && self.class.primary_key != name
        read_attribute(self.class.primary_key)
      end
    } unless column

    value = @attributes.fetch(name) {
      return block_given? ? yield(name) : nil
    }

    if self.class.cache_attribute?(name)
      @attributes_cache[name] = column.type_cast(value)
    else
      column.type_cast value
    end
  }
end