File: activerecord.rb

package info (click to toggle)
ruby-enumerize 2.8.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 432 kB
  • sloc: ruby: 3,712; makefile: 6
file content (154 lines) | stat: -rw-r--r-- 4,678 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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# frozen_string_literal: true

module Enumerize
  module ActiveRecordSupport
    def enumerize(name, options={})
      super

      _enumerize_module.dependent_eval do
        if self < ::ActiveRecord::Base
          include InstanceMethods

          const_get(:ActiveRecord_Relation).include(RelationMethods)
          const_get(:ActiveRecord_AssociationRelation).include(RelationMethods)
          const_get(:ActiveRecord_Associations_CollectionProxy).include(RelationMethods)

          # Since Rails use `allocate` method on models and initializes them with `init_with` method.
          # This way `initialize` method is not being called, but `after_initialize` callback always gets triggered.
          after_initialize :_set_default_value_for_enumerized_attributes

          # https://github.com/brainspec/enumerize/issues/111
          require 'enumerize/hooks/uniqueness'

          unless options[:multiple]
            if ::ActiveRecord.version >= ::Gem::Version.new("7.2.0.alpha")
              attribute(name)

              decorate_attributes([name]) do |_, subtype|
                Type.new(enumerized_attributes[name], subtype)
              end
            elsif ::ActiveRecord.version >= ::Gem::Version.new("7.0.0.alpha")
              attribute(name) do |subtype|
                Type.new(enumerized_attributes[name], subtype)
              end
            elsif ::ActiveRecord.version >= ::Gem::Version.new("6.1.0.alpha")
              decorate_attribute_type(name.to_s) do |subtype|
                Type.new(enumerized_attributes[name], subtype)
              end
            else
              decorate_attribute_type(name, :enumerize) do |subtype|
                Type.new(enumerized_attributes[name], subtype)
              end
            end
          end
        end
      end
    end

    module InstanceMethods
      # https://github.com/brainspec/enumerize/issues/74
      def write_attribute(attr_name, value, *options)
        if self.class.enumerized_attributes[attr_name]
          _enumerized_values_for_validation[attr_name.to_s] = value
        end

        super
      end

      # Support multiple enumerized attributes
      def becomes(klass)
        became = super
        klass.enumerized_attributes.each do |attr|
          # Rescue when column associated to the enum does not exist.
          begin
            became.send("#{attr.name}=", send(attr.name))
          rescue ActiveModel::MissingAttributeError
          end
        end

        became
      end

      def reload(options = nil)
        reloaded = super

        reloaded.class.enumerized_attributes.each do |attr|
          begin
            # Checks first if the enumerized attribute is in ActiveRecord::Store
            store_attr, _ = reloaded.class.stored_attributes.detect do |_store_attr, keys|
              keys.include?(attr.name)
            end

            if store_attr.present?
              unless reloaded.send(store_attr).nil?
                reloaded.send("#{attr.name}=", reloaded.send(store_attr).with_indifferent_access[attr.name])
              end
            else
              reloaded.send("#{attr.name}=", reloaded[attr.name])
            end
          rescue ActiveModel::MissingAttributeError
          end
        end

        reloaded
      end
    end

    module RelationMethods
      def update_all(updates)
        if updates.is_a?(Hash)
          enumerized_attributes.each do |attr|
            next if updates[attr.name].blank? || attr.kind_of?(Enumerize::Multiple)
            enumerize_value = attr.find_value(updates[attr.name])
            updates[attr.name] = enumerize_value && enumerize_value.value
          end
        end

        super(updates)
      end
    end

    class Type < ActiveRecord::Type::Value
      delegate :type, to: :@subtype

      def initialize(attr, subtype)
        @attr = attr
        @subtype = subtype
      end

      def serialize(value)
        v = @attr.find_value(value)
        return value unless v

        v.value
      end

      def cast(value)
        return value if @subtype.is_a?(Type)

        if value.is_a?(::Enumerize::Value)
          value
        else
          @attr.find_value(@subtype.cast(value))
        end
      end

      def as_json(options = nil)
        {attr: @attr.name}.as_json(options)
      end

      def encode_with(coder)
        coder[:class_name] = @attr.klass.name
        coder[:attr_name] = @attr.name
        coder[:subtype] = @subtype
      end

      def init_with(coder)
        initialize(
          coder[:class_name].constantize.enumerized_attributes[coder[:attr_name]],
          coder[:subtype]
        )
      end
    end
  end
end