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
のドキュメントをお読みください。