File: freezer.rb

package info (click to toggle)
ruby-adamantium 0.2.0-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, forky, sid, trixie
  • size: 284 kB
  • sloc: ruby: 741; makefile: 4
file content (138 lines) | stat: -rw-r--r-- 2,953 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# encoding: utf-8

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 = ->(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

    @freezers = {
      noop: Noop,
      deep: Deep,
      flat: Flat,
    }.freeze

    # Return freezer for name
    #
    # @param [Symbol] name
    #   a freezer name
    #
    # @return [#call]
    #
    # @api private
    def self.get(name)
      @freezers.fetch(name) do
        fail 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?
        fail OptionError, "Unknown option key(s) for memoizer #{keys.inspect}"
      end
      get(options.fetch(:freezer)) if options.key?(:freezer)
    end
  end
end