module Adamantium # Abstract base class for freezers # # TODO: Use dkubb/abstract_class? # # Better pattern for singleton inheritance/shared code class Freezer private_class_method :new # Attempt to freeze an object # # @example using a value object # Adamantium.freeze_object(12345) # => noop # # @example using a normal object # Adamantium.freeze_object({}) # => duplicate & freeze object # # @param [Object] object # the object to freeze # # @return [Object] # if supported, the frozen object, otherwise the object directly # # @api public def self.call(object) case object when Numeric, TrueClass, FalseClass, NilClass, Symbol, Class, Module, UnboundMethod, Method object else freeze_value(object) end end private_class_method :call # Returns a frozen value # # @param [Object] value # a value to freeze # # @return [Object] # if frozen, the value directly, otherwise a frozen copy of the value # # @api private def self.freeze_value(value) value.frozen? ? value : freeze(value.dup) end private_class_method :freeze_value # Freezer that does not deep freeze class Flat < self # Freeze value # # @param [Object] value # # @return [undefined] # # @api private def self.freeze(value) value.freeze end public_class_method :call end # Freezer that does deep freeze class Deep < self # Deep freeze value # # @param [Object] value # # @return [undefined] # # @api private def self.freeze(value) IceNine.deep_freeze(value) end public_class_method :call end Noop = lambda { |object| object }.freeze # Error raised when freezer cannot be found class UnknownFreezerError < RuntimeError; end # Error raised when memoizer options contain unknown keys class OptionError < RuntimeError; end # Return freezer for name # # @param [Symbol] name # a freezer name # # @return [#call] # # @api private def self.get(name) case name when :noop Noop when :deep Deep when :flat Flat else raise UnknownFreezerError, "Freezer with name #{name.inspect} is unknown" end end # Parse freezer options # # @param [Hash] options # an options hash # # @return [#call] # if freezer option was present # # @return [nil] # otherwise # # @api private # def self.parse(options) keys = options.keys - [:freezer] unless keys.empty? raise OptionError, "Unknown option key(s) for memoizer #{keys.inspect}" end return unless options.key?(:freezer) get(options.fetch(:freezer)) end end end