GitLab ユーティリティ
GitLab では、開発を容易にするためのユーティリティを多数開発しています:
MergeHash
merge_hash.rb をご参照ください:
-
ハッシュの配列をディープマージします:
Gitlab::Utils::MergeHash.merge( [{ hello: ["world"] }, { hello: "Everyone" }, { hello: { greetings: ['Bonjour', 'Hello', 'Hallo', 'Dzien dobry'] } }, "Goodbye", "Hallo"] )を返します:
[ { hello: [ "world", "Everyone", { greetings: ['Bonjour', 'Hello', 'Hallo', 'Dzien dobry'] } ] }, "Goodbye" ] -
ハッシュからすべてのキーと値を配列に取り出します:
Gitlab::Utils::MergeHash.crush( { hello: "world", this: { crushes: ["an entire", "hash"] } } )を返します:
[:hello, "world", :this, :crushes, "an entire", "hash"]
Override
override.rb をご参照ください:
-
このユーティリティは、あるメソッドが別のメソッドをオーバーライドするかどうかをチェックするのに役立ちます。Java の
@Overrideアノテーションや Scala のoverrideキーワードと同じ概念です。ただし、実行時のオーバーヘッドを避けるため、ENV['STATIC_VERIFICATION']が設定されている場合にのみこのチェックを実行します。これはチェックに便利です:- オーバーライド・メソッドにタイプミスがある場合。
-
オーバーライドされたメソッドの名前を変更したために、元のオーバーライド・メソッドが無意味になった場合。
簡単な例を示します:
class Base def execute end end class Derived < Base extend ::Gitlab::Utils::Override override :execute # Override check happens here def execute end endこれはモジュールでも使えます:
module Extension extend ::Gitlab::Utils::Override override :execute # Modules do not check this immediately def execute end end class Derived < Base prepend Extension # Override check happens here, not in the module endチェックが行われるのは
- オーバーライド・メソッドがクラスで定義されている場合:
- オーバーライド・メソッドはモジュールで定義され、クラスまたはモジュールの前に付加されます。
実際にメソッドをオーバーライドできるのは、クラスまたはプリペンドされたモジュールだけだからです。モジュールを別のモジュールにインクルードしたり、拡張したりしても、オーバーライドすることはできません。
ActiveSupport::Concern 、prepend 、およびclass_methods
ActiveSupport::Concern クラスのメソッドをインクルードして ActiveSupport::Concern使う場合、通常のRubyモジュールのようには動作ActiveSupport::Concern しないので、期待した結果は得 ActiveSupport::Concernられません。
すでにprepend を有効にするためのActiveSupport::Concern 用のパッチとしてPrependable が用意されているので、override やclass_methods とどのように相互作用するかが問題になります。回避策として、extend ClassMethods をPrependable モジュールの定義に追加します。
これにより、override を使用して、上記のコンテキストで使用されているclass_methods を検証することができます。この回避策は、検証を実行するときにのみ適用され、アプリケーション自体を実行するときには適用されません。
以下は、この回避策の効果を示すコードブロックの例です:
module Base
extend ActiveSupport::Concern
class_methods do
def f
end
end
end
module Derived
include Base
end
# Without the workaround
Base.f # => NoMethodError
Derived.f # => nil
# With the workaround
Base.f # => nil
Derived.f # => nil
StrongMemoize
strong_memoize.rb をご参照ください:
-
nilまたはfalseの場合でも、値をメモしてください。私たちはよく
@value ||= computeとします。しかし、computeが最終的にnilを与える可能性があり、再度計算したくない場合、これはうまく機能しません。代わりに、defined?を使って値が設定されているかどうかをチェックすることができます。このようなパターンを書くのは面倒なので、StrongMemoize。このようなパターンを書く代わりに
class Find def result return @result if defined?(@result) @result = search end endのように書くことができます:
class Find include Gitlab::Utils::StrongMemoize def result search end strong_memoize_attr :result def enabled? Feature.enabled?(:some_feature) end strong_memoize_attr :enabled? endパラメータを持つメソッドでの
strong_memoize_attrの使用はサポートされていません。また、overrideと組み合わせても動作せず、誤った結果を残す可能性があります。代わりに
strong_memoize_withを使用してください。# bad def expensive_method(arg) # ... end strong_memoize_attr :expensive_method # good def expensive_method(arg) strong_memoize_with(:expensive_method, arg) # ... end end引数を取るメソッドをメモするための
strong_memoize_withもあります。これは、引数として取りうる値の数が少ないメソッドや、ループ内で一貫して引数を繰り返すメソッドに使用します。class Find include Gitlab::Utils::StrongMemoize def result(basic: true) strong_memoize_with(:result, basic) do search(basic) end end end -
明確なメモ化
class Find include Gitlab::Utils::StrongMemoize end Find.new.clear_memoization(:result)
RequestCache
request_cache.rb をご参照ください。
このモジュールは、RequestStore に値をキャッシュする簡単な方法を提供します。キャッシュキーは、クラス名、メソッド名、オプションでカスタマイズされたインスタンスレベルの値、オプションでカスタマイズされたメソッドレベルの値、およびオプションのメソッド引数に基づいています。
インスタンスレベルでカスタマイズされた値のみを使用する簡単な例を示します:
class UserAccess
extend Gitlab::Cache::RequestCache
request_cache_key do
[user&.id, project&.id]
end
request_cache def can_push_to_branch?(ref)
# ...
end
end
このようにすると、can_push_to_branch? の結果はキャッシュキーに基づいてRequestStore.store にキャッシュされます。RequestStore が現在アクティビティでない場合は、ハッシュに保存され、インスタンス変数に保存されるので、キャッシュロジックは同じになります。
メソッドごとに異なる戦略を設定することもできます:
class Commit
extend Gitlab::Cache::RequestCache
def author
User.find_by_any_email(author_email)
end
request_cache(:author) { author_email }
end
ReactiveCaching
ReactiveCaching のドキュメントをお読みください。