読者です 読者をやめる 読者になる 読者になる

遅延初期化する attr_reader を追加するモジュール

Ruby
module LazyInitializationAttribute
  def LazyInitializationAttribute.append_features(mod)
    super
    mod.const_set("LAZY_INITIALIZE_BLOCK_TABLE", {})

    def mod.lazy_attr_accesser name, hook=nil, &block
      lazy_attr_reader name, hook, &block
      attr_writer name
    end

    def mod.lazy_attr_reader name, hook=nil, &block
      name = name.to_sym
      hook = hook.to_sym unless hook.nil?
      self::LAZY_INITIALIZE_BLOCK_TABLE[name] = block
      hook_snippet = hook.nil? ? "" : %Q{
        result = self.__send__(:#{hook}) if self.respond_to? :#{hook}
      }.lstrip

      class_eval %Q{
        def #{name}
          if @#{name}.nil?
            lazy_init_proc = self.class::LAZY_INITIALIZE_BLOCK_TABLE[:#{name}]
            result = nil
            result = instance_eval &lazy_init_proc if lazy_init_proc
            #{hook_snippet}
            @#{name} = result if @#{name}.nil? and not result.nil?
          end
          @#{name}
        end
      }
    end
  end
end

使い方

適当すぎるけど。。

class A
  include LazyInitializationAttribute

  def initialize(init=0)
    @init = init
  end

  def update!
    @init += 1
    @a = @init
  end
  def hoge
    3
  end
  lazy_attr_reader :a, :update!
  lazy_attr_reader(:aa) {|a| 2 }
  lazy_attr_reader(:aai) {|a| @init }
  lazy_attr_reader(:bb, :hoge)
  lazy_attr_accesser(:aas) {|a| 2 }
  lazy_attr_accesser(:bbs, :hoge) 
end

a =A.new(0)
a.a # => 1
a.aa    # => 2
a.aai   # => 1
a.bb    # => 3

b =A.new(0)
b.aai # => 0
b.a # => 1
b.aai # => 0