# frozen_string_literal: true module Gitlab module Utils module StrongMemoize # Instead of writing patterns like this: # # def trigger_from_token # return @trigger if defined?(@trigger) # # @trigger = Ci::Trigger.find_by_token(params[:token].to_s) # end # # We could write it like: # # include Gitlab::Utils::StrongMemoize # # def trigger_from_token # strong_memoize(:trigger) do # Ci::Trigger.find_by_token(params[:token].to_s) # end # end # # Or like: # # include Gitlab::Utils::StrongMemoize # # def trigger_from_token # Ci::Trigger.find_by_token(params[:token].to_s) # end # strong_memoize_attr :trigger_from_token # # strong_memoize_attr :enabled?, :enabled # def enabled? # Feature.enabled?(:some_feature) # end # def strong_memoize(name) key = ivar(name) if instance_variable_defined?(key) instance_variable_get(key) else instance_variable_set(key, yield) end end def strong_memoized?(name) instance_variable_defined?(ivar(name)) end def clear_memoization(name) key = ivar(name) remove_instance_variable(key) if instance_variable_defined?(key) end module StrongMemoizeClassMethods def strong_memoize_attr(method_name, member_name = nil) member_name ||= method_name if method_defined?(method_name) || private_method_defined?(method_name) StrongMemoize.send( # rubocop:disable GitlabSecurity/PublicSend :do_strong_memoize, self, method_name, member_name) else StrongMemoize.send( # rubocop:disable GitlabSecurity/PublicSend :queue_strong_memoize, self, method_name, member_name) end end def method_added(method_name) super if member_name = StrongMemoize .send(:strong_memoize_queue, self).delete(method_name) # rubocop:disable GitlabSecurity/PublicSend StrongMemoize.send( # rubocop:disable GitlabSecurity/PublicSend :do_strong_memoize, self, method_name, member_name) end end end def self.included(base) base.singleton_class.prepend(StrongMemoizeClassMethods) end private # Convert `"name"`/`:name` into `:@name` # # Depending on a type ensure that there's a single memory allocation def ivar(name) if name.is_a?(Symbol) name.to_s.prepend("@").to_sym elsif name.is_a?(String) :"@#{name}" else raise ArgumentError, "Invalid type of '#{name}'" end end class <