YOSHINO日記

プログラミングに関すること

Delegate: 特定のメソッドを別のインスタンスから利用する

使いたいシチュエーション

継承も利用しているので、少し複雑ですが。

Mirror: only hit all sites for upload and delete · rails/activestorage@a8e849b · GitHub

The mirror service exists for the purpose of migration, where all blobs exist in the primary subservice and a subset of blobs exist in the secondary subservice. Since the primary subservice is the source of truth until a migration is completed, operations like existence checks need not be performed against the secondary subservices.

意訳。

全てのblobは常にプライマリーサービスとセカンダリーサービスをセットで持っているんだ。

だけど、プライマリーサービスはmigrationするまで必要で、それ以降はいらない。

だから、存在を確認するメソッドとかはセカンダリーサービスには必要ない。

delegate :download, :exist?, :url, :byte_size, :checksum, to: :primary_service

例えば、以下のようにサービスは配列で作成される。

(下のコードだけではわからないと思う。大事なのは2つのインスタンスの配列からSITEが作成されるということ。)

PRIMARY_DISK_SITE        =  Yactivestorage::Service.configure(:Disk, root: File.join(Dir.tmpdir, "yactivestorage"))
SECONDARY_DISK_SITE  =  Yactivestorage::Service.configure(:Disk, root: File.join(Dir.tmpdir, "yactivestorage_mirror"))

SITE = Yactivestorage::Service.configure :Mirror, services: [ PRIMARY_DISK_SITE, SECONDARY_DISK_SITE ]

ポイントは、SITEのインスタンスメソッドの内、:download, :exist?, :url, :byte_size, :checksumは、PRIMARY_DISK_SITEのものが 実行されるということです。

もっと簡単な例。

require "active_support/core_ext/module/delegation"

class Site
  def initialize(name)
    @name = name
  end

  def common_method
    puts "このメソッドは共通メソッドです。"
  end

  def special_method
    puts "配列(hoges)で渡された初めのインスタンスのメソッドです"
  end
end

class Mirror < Site
  attr_reader :sites
  delegate :special_method, to: :first_site

  def initialize(sites:)
    @sites = sites
  end

  private
    def perform_across_services(method, *args)
      public_send method, *args
    end

    def first_site
      @sites.first
    end
end

prime  = Site.new("prime")
second = Site.new("second")

site = Foo.new(sites: [prime, second])
site.common_method
site.special_method

fooによって呼び出されるspecial-methodはprimeのもので、common_methodはsecondのものとなっています。