summaryrefslogtreecommitdiff
path: root/config/initializers/active_record_locking.rb
blob: 9266ff0f615a486170d1160ee3c99371d1797aa9 (plain)
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
# rubocop:disable Lint/RescueException

# This patch fixes https://github.com/rails/rails/issues/26024
# TODO: Remove it when it's no longer necessary

module ActiveRecord
  module Locking
    module Optimistic
      # We overwrite this method because we don't want to have default value
      # for newly created records
      def _create_record(attribute_names = self.attribute_names, *) # :nodoc:
        super
      end

      def _update_record(attribute_names = self.attribute_names) #:nodoc:
        return super unless locking_enabled?
        return 0 if attribute_names.empty?

        lock_col = self.class.locking_column

        previous_lock_value = send(lock_col).to_i

        # This line is added as a patch
        previous_lock_value = nil if previous_lock_value == '0' || previous_lock_value == 0

        increment_lock

        attribute_names += [lock_col]
        attribute_names.uniq!

        begin
          relation = self.class.unscoped

          affected_rows = relation.where(
            self.class.primary_key => id,
            lock_col => previous_lock_value
          ).update_all(
            attributes_for_update(attribute_names).map do |name|
              [name, _read_attribute(name)]
            end.to_h
          )

          unless affected_rows == 1
            raise ActiveRecord::StaleObjectError.new(self, "update")
          end

          affected_rows

        # If something went wrong, revert the version.
        rescue Exception
          send(lock_col + '=', previous_lock_value)
          raise
        end
      end

      # This is patched because we need it to query `lock_version IS NULL`
      # rather than `lock_version = 0` whenever lock_version is NULL.
      def relation_for_destroy
        return super unless locking_enabled?

        column_name = self.class.locking_column
        super.where(self.class.arel_table[column_name].eq(self[column_name]))
      end
    end

    # This is patched because we want `lock_version` default to `NULL`
    # rather than `0`
    class LockingType < SimpleDelegator
      def type_cast_from_database(value)
        super
      end
    end
  end
end