module Scrooge class AttributesProxy attr_reader :callsite_signature def initialize(record, scrooge_columns, klass, callsite_signature) @attributes = record @scrooge_columns = scrooge_columns.dup @fully_fetched = false @klass = klass @callsite_signature = callsite_signature end # Delegate Hash keys to all defined columns # def keys @klass.column_names end # Let #has_key? consider defined columns # def has_key?(attr_name) keys.include?(attr_name.to_s) end alias_method :include?, :has_key? alias_method :key?, :has_key? # Lazily augment and load missing attributes # def [](attr_name) attr_s = attr_name.to_s if interesting_for_scrooge?( attr_s ) augment_callsite!( attr_s ) fetch_remaining @scrooge_columns << attr_s end @attributes[attr_s] end def fetch(*args, &block) self[args[0]] @attributes.fetch(*args, &block) end def []=(attr_name, value) attr_s = attr_name.to_s @attributes[attr_s] = value @scrooge_columns << attr_s end def dup super.dup_self end def to_a fetch_remaining @attributes.to_a end def delete(attr_name) self[attr_name] @attributes.delete(attr_name) end def update(hash) hash.to_hash.each do |k, v| self[k] = v end end def to_hash fetch_remaining @attributes end def freeze @attributes.freeze end def frozen? @attributes.frozen? end def fetch_remaining unless @fully_fetched columns_to_fetch = @klass.column_names - @scrooge_columns.to_a unless columns_to_fetch.empty? begin new_object = fetch_record_with_remaining_columns( columns_to_fetch ) rescue ActiveRecord::RecordNotFound raise ActiveRecord::MissingAttributeError, "scrooge cannot fetch missing attribute(s) because record went away" end @attributes = new_object.instance_variable_get(:@attributes).merge(@attributes) end @fully_fetched = true end end protected def fetch_record_with_remaining_columns( columns_to_fetch ) @klass.send(:with_exclusive_scope) do @klass.find(@attributes[@klass.primary_key], :select=>@klass.scrooge_sql(columns_to_fetch)) end end def interesting_for_scrooge?( attr_s ) has_key?(attr_s) && !@scrooge_columns.include?(attr_s) end def augment_callsite!( attr_s ) @klass.augment_scrooge_callsite!(callsite_signature, attr_s) end def dup_self @attributes = @attributes.dup @scrooge_columns = @scrooge_columns.dup self end end end