🕌

Rubyの良い感じなメソッド名 ActiveSupport::Cache 編

2020/12/02に公開

ActiveSupportから、良い感じだと思ったメソッド名を抜粋してみました。
この記事では特に、ActiveSupport::Cacheモジュールの中からメソッド名を抜粋しています。

設計というのは名前が決まればだいたい終わったようなもの

タイトル引用元1
タイトル引用元2

掲題の通り、名前付けはプログラミングにおいてとても大事な工程です。しかし、名前を決めるだけなのに、その名前が決まらないという状況は多いものです。

この記事で紹介しているメソッド名が、そのような悩みを持つ人の一助になれば幸いです。

質問や分からない点は、@ts_3156 までお気軽にご連絡ください。

def read(key)

キーを指定しキャッシュから値を読むメソッド。

cache = Cache.new
cache.read('key')
# => 'value'

メソッド名、特に外部に公開しているAPIのメソッド名は、シンプルすぎても長すぎても分かりづらいものです。少なくともキャッシュの分野では、readというメソッド名に対する共通認識がありますが、readというメソッド名を他の分野で使う場合は注意が必要でしょう。

※ これ以降、参考コードはActiveSupportの利用例であったり、説明に必要な部分だけ改変して抜粋したものです。

def read_multi(*names)

複数のキーを指定しキャッシュから値を読むメソッド。readを複数回呼ぶよりも効率が良い。

cache.read_multi('key1', 'key2')

日本人が出来るだけ英語っぽく名前付けしようとするとread_multiple(もしくはmultiple_read)という名前になってしまいそうですが、read_multiの方がすっきりしてよさそうです。

def read_entry(key)

readが公開されているAPIのとき、内部的に値を取得するメソッドが別でほしいことがある。そういう用途のメソッド。

entry = read_entry('key')
unless entry.expired?
  entry.value
end

プログラミング言語を問わず、内部で利用するメソッドは公開されているメソッドと比べて長い名前を付けると読みやすくなります。

def read_multi_entries(*names)

read_multiの内部で使われるメソッド。

entries = read_multi_entries('key1', 'key2')
vaules = entries.select { |e| !e.expired? }.map(&:value)

def write(key, value)

  • def write_multi(entries)
  • def write_entry(key, entry)
  • def write_multi_entries(entries)

read* に対応する書き込みのためのメソッド。

cache.write('key')

def fetch(key, &block)

キーを指定しキャッシュから値を読む、ただし値がない場合は新しく作成し書き込みまで行うというメソッド。

cache.fetch('key') { 'new value' }

Rails(の中のActiveSupport)がこういう動作になっているため、fetchというメソッドを作るときは、この実装に合わせると複数人開発でメンバー間の戸惑いが減るかもしれません。

ただし、fetchという単語自体は抽象的なニュアンスになるので、どこでも使うと混乱を招く可能性もあります。

def increment(name, amount = 1)

  • def decrement(name, amount = 1)

incrementは値を1増やす、decrementは値を1減らす、というメソッド。

cache.increment('key')

メソッド名というよりも、引数名がvalueではなくamountになっているのが分かりやすさのポイントに思えたので紹介しました。

def modify_value(name, amount)

incrementとdecrementの内部で使われているメソッド。

def increment(name, amount = 1)
  modify_value(name, amount)
end

def decrement(name, amount = 1)
  modify_value(name, -amount)
end

値を少し変更する、というようなニュアンス。changeではなくmodifyになっているところが分かりやすさのポイントに思えたので紹介しました。

def delete(name)

キーを指定しキャッシュから値を削除するメソッド。

cache.delete('key')

似たような名前にremoveがあります。少なくとも、Rubyのコードかつキャッシュの分野では、removeよりもdeleteが使われることが多い気がします。

deleteも、readやwriteと同様に内部で使うメソッド名はdelete_entryになっています。

def clear

キャッシュから全てのエントリーを削除するメソッド。

cache.clear

後述するcleanupと似ていますが少し違います。clearは完全に削除するイメージのようです。

def cleanup

キャッシュから有効期限が切れたエントリーのみを削除するメソッド。

cache.cleanup

前述したclearと違い、全てを削除するわけではありません。

def prune

大きくなりすぎたキャッシュをあらかじめ指定されたサイズまで小さくするメソッド。

cache.prune

cleanupと似ていますが、ニュアンスが少し異なります。pruneを実行すると内部的にcleanupが呼ばれる、みたいなイメージです。

def serialize_entry

  • def deserialize_entry

readやwriteが読み書きするEntryデータを、シリアライズ(= 保存に適した形式への変換)するためのメソッド。

def read_entry(key)
  payload = @data.get(key)
  deserialize_entry(payload)
end

def write_entry(key, entry)
  payload = serialize_entry(entry)
  @data.set(key, payload)
  true
end

readはread_entryを呼び、さらに中ではdeserialize_entryを呼ぶイメージです。

ある程度作り込まれたライブラリでは、readは公開API、readの内部では処理A、処理Bを行う、という実装になることがあります。そういうときに、処理Aと処理Bの名前に困ることが(私は)よくあります。少なくともキャッシュの分野では、これからこういう悩みを持つことがなくなりそうです。

def namespace_key

「ユーザーが与えたキーに接頭語を付けたキー」を生成するメソッド。

namespace_key('key', namespace: 'myapp')
# => 'myapp:key'

作り込まれたライブラリでは、ユーザーが与えた「キー」は、実際に「キャッシュのキー」として使われるまでに複数回の変換があるものです。そういうときに、こういう名前付けのレパートリーを持っておくと、名前に延々と悩むことが少なくなります。

def expand_cache_key

「ユーザーが与えた複数の値を1つの値に展開したキー」を生成するメソッド。

expand_cache_key([:xxx, :yyy])
# => 'xxx/yyy'

前述のnamespace_keyと似ていますが、微妙に用途が違います。こういうときに名前付けに困るのはありがちです。

def retrieve_cache_key

前述のexpand_cache_keyの中でさらに使われているメソッド。

expand_cache_keyとの違いを言葉で説明するのが難しいです。詳細は実装をご覧になってください。ActiveSupportでは下記のような実装になっています。

def retrieve_cache_key(key)
  case
  when key.respond_to?(:cache_key_with_version) then key.cache_key_with_version
  when key.respond_to?(:cache_key)              then key.cache_key
  when key.is_a?(Array)                         then key.map { |element| retrieve_cache_key(element) }.to_param
  when key.respond_to?(:to_a)                   then retrieve_cache_key(key.to_a)
  else                                               key.to_param
  end.to_s
end

retrieveはfetchと似た意味の単語で、エンジニアリング分野ではよく見かける単語です。

def normalize_key

キャッシュをファイルとして保存する際、ユーザーが与えたキーをファイルパスに変換するためのメソッド。

normalize_key('key')
# => /aaa/bbb/ccc

normalizeは「正規化する」という意味の単語です。このメソッドの場合はファイルパスへの変換をnormalizeと呼んでいますが、この単語には他の用途もたくさんあるため注意が必要です。

def file_path_key

normalize_keyとは逆に、ファイルパスをキーに変換するメソッド。

file_path_key('/aaa/bbb/ccc')
# => 'key'

逆変換なのでreverse* のような名前が真っ先に浮かんでしまいますが、この名前の方がスマートに感じます。

def bypass_local_cache

local_cacheを一時的に使わないためのメソッド。

def bypass_local_cache
  use_temporary_local_cache(nil) { yield }
end

bypassは「迂回する」という意味の単語です。何かを迂回するためのメソッドにはbypass* という名前が使えそうです。

bypassがあまり思い浮かばない単語、かつ用途がそれなりにありそうなので今回紹介しました。

def ensure_cache_path

入力として与えたパスのディレクトリを作成するメソッド。

def ensure_cache_path(path)
  FileUtils.makedirs(path) unless File.exist?(path)
end

「ファイルに値を書き込む。ただし、ディレクトリがない場合は作る」という動作を1つのメソッドでやると、ほんの少しですがメソッドがごちゃごちゃします。そんなときに使えそうなメソッドです。

ensureがあまり思い浮かばない単語、かつ用途がそれなりにありそうなので今回紹介しました。

def failsafe

「内部で起きた例外を内部でrescueして、外部には例外が起きたことを伝えない」という用途に使うメソッド。

簡易化した実装は下記の通りです。

def write(key, value)
  failsafe do
    write_entry(...)
  end
end

def failsafe(returning: nil)
  yield
rescue => e
  handle_exception e
  returning
end

failsafeという単語自体はよく聞きますが、メソッド名にするのはなるほどと思いました。発想が新鮮だったので今回紹介しました。

def rescue_error_with

前述のfailsafeと同じようなメソッド。

簡易化した実装は下記の通りです。

def write(key, value)
  rescue_error_with(false) do
    write_entry(...)
  end
end

def rescue_error_with(fallback)
  yield
rescue => e
  fallback
end

メソッド名が少し説明調で長いですが、外部に公開するメソッドとして考えるなら、こちらの方がfailsafeよりも用途が分かりやすくよいネーミングかもしれません。


質問や分からない点は、@ts_3156 までお気軽にご連絡ください。

Discussion