File: deprecated_associations.rb

package info (click to toggle)
ruby-sequel 5.97.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 11,188 kB
  • sloc: ruby: 123,115; makefile: 3
file content (151 lines) | stat: -rw-r--r-- 5,690 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
# frozen-string-literal: true

module Sequel
  module Plugins
    # The deprecated_associations plugin adds support for
    # deprecating associations. Attempts to use association
    # methods and access association metadata for deprecated
    # associations results in a warning.
    #
    #   Album.plugin :deprecated_associations
    #   Album.many_to_one :artist, deprecated: true
    #   album = Album[1]
    #
    #   # Warnings for all of the following calls
    #   album.artist
    #   album.artist_dataset
    #   album.artist = Artist[2]
    #   Album.association_reflection(:artist)
    #   Album.eager(:artist)
    #   Album.eager_graph(:artist)
    #   Album.where(artist: Artist[1]).all
    #
    # By default, the plugin issues a single warning per
    # association method or association reflection. See
    # DeprecatedAssociations.configure for options to make
    # deprecated association issue warnings for every access,
    # to include backtraces in warnings, or to raise an
    # exception instead of warning.
    #
    # Note that Model.association_reflections and
    # Model.all_association_reflections will include deprecated
    # associations, and accessing the metadata for deprecated
    # associations through these interfaces not issue warnings.
    #
    # Usage:
    #
    #   # Make all models support deprecated associations
    #   # (called before loading subclasses)
    #   Sequel::Model.plugin :deprecated_associations
    #
    #   # Make Album class support deprecated associations
    #   Album.plugin :deprecated_associations
    module DeprecatedAssociations
      # Exception class used for deprecated association use when
      # raising exceptions instead of emitting warnings.
      class Access < Sequel::Error; end

      # Configure the deprecated associations plugin. Options:
      #
      # backtrace: Print backtrace with warning
      # deduplicate: Set to false to emit warnings for every
      #              deprecated association method call/access
      #              (when not caching associations, this is always false)
      # raise: Raise Sequel::Plugin::DeprecatedAssociations::Access
      #        instead of warning
      def self.configure(model, opts = OPTS)
        model.instance_exec do
          (@deprecated_associations_config ||= {}).merge!(opts)
        end
      end

      module ClassMethods
        # Issue a deprecation warning if the association is deprecated.
        def association_reflection(assoc)
          ref = super
          if ref && ref[:deprecated]
            emit_deprecated_association_warning(ref, nil) do
              "Access of association reflection for deprecated association: class:#{name} association:#{assoc}"
            end
          end
          ref
        end

        private

        # Issue a deprecation warning when the defined method is called if the
        # association is deprecated and the method name does not start with the
        # underscore (to avoid not warning twice, once for the public association
        # method and once for the private association method).
        def association_module_def(name, opts=OPTS, &block)
          super
          if opts[:deprecated] && name[0] != "_"
            deprecated_associations_module.module_exec do
              define_method(name) do |*a, &b|
                self.class.send(:emit_deprecated_association_warning, opts, name) do
                  "Calling deprecated association method: class:#{self.class.name} association:#{opts[:name]} method:#{name}"
                end
                super(*a, &b)
              end
              alias_method name, name
            end
          end
          nil
        end

        # Issue a deprecation warning when the defined method is called if the
        # association is deprecated.
        def association_module_delegate_def(name, opts, &block)
          super
          if opts[:deprecated]
            deprecated_associations_module.module_exec do
              define_method(name) do |*a, &b|
                self.class.send(:emit_deprecated_association_warning, opts, name) do
                  "Calling deprecated association method: class:#{self.class.name} association:#{opts[:name]} method:#{name}"
                end
                super(*a, &b)
              end
              # :nocov:
              ruby2_keywords(name) if respond_to?(:ruby2_keywords, true)
              # :nocov:
              alias_method(name, name)
            end
          end
          nil
        end

        # A module to add deprecated association methods to. These methods
        # handle issuing the deprecation warnings, and call super to get the
        # default behavior.
        def deprecated_associations_module
          return @deprecated_associations_module if defined?(@deprecated_associations_module)
          @deprecated_associations_module = Module.new
          include(@deprecated_associations_module)
          @deprecated_associations_module
        end

        # Emit a deprecation warning, or raise an exception if the :raise
        # plugin option was used.
        def emit_deprecated_association_warning(ref, method)
          config = @deprecated_associations_config

          raise Access, yield if config[:raise]

          unless config[:deduplicate] == false
            emit = false
            ref.send(:cached_fetch, [:deprecated_associations, method]) do
              emit = true
            end
            return unless emit
          end

          if config[:backtrace]
            warn yield, caller(2)
          else
            warn yield, :uplevel => 2
          end
        end
      end
    end
  end
end