File: subclasses.rb

package info (click to toggle)
ruby-sequel 5.63.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 10,408 kB
  • sloc: ruby: 113,747; makefile: 3
file content (96 lines) | stat: -rw-r--r-- 3,152 bytes parent folder | download | duplicates (2)
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
# frozen-string-literal: true

module Sequel
  module Plugins
    # The subclasses plugin keeps track of all subclasses of the
    # current model class.  Direct subclasses are available via the
    # subclasses method, and all descendent classes are available via the
    # descendants method:
    #
    #   c = Class.new(Sequel::Model)
    #   c.plugin :subclasses
    #   sc1 = Class.new(c)
    #   sc2 = Class.new(c)
    #   ssc1 = Class.new(sc1)
    #   c.subclasses    # [sc1, sc2]
    #   sc1.subclasses  # [ssc1]
    #   sc2.subclasses  # []
    #   ssc1.subclasses # []
    #   c.descendants   # [sc1, ssc1, sc2]
    #
    # You can also finalize the associations and then freeze the classes
    # in all descendent classes.  Doing so is a recommended practice after
    # all models have been defined in production and testing, and this makes
    # it easier than keeping track of the classes to finalize and freeze
    # manually:
    #
    #   c.freeze_descendants
    #
    # You can provide a block when loading the plugin, and it will be called
    # with each subclass created:
    #
    #   a = []
    #   Sequel::Model.plugin(:subclasses){|sc| a << sc}
    #   class A < Sequel::Model; end
    #   class B < Sequel::Model; end
    #   a # => [A, B]
    module Subclasses
      NEED_SUBCLASSES = !Object.respond_to?(:subclasses) || Object.method(:subclasses).source_location
      private_constant :NEED_SUBCLASSES

      # Initialize the subclasses instance variable for the model.
      def self.apply(model, &block)
        # :nocov:
        model.instance_variable_set(:@subclasses, [])  if NEED_SUBCLASSES
        # :nocov:
        model.instance_variable_set(:@on_subclass, block)
      end

      module ClassMethods
        # Callable object that should be called with every descendent
        # class created.
        attr_reader :on_subclass

        # :nocov:
        if NEED_SUBCLASSES
          # All subclasses for the current model.  Does not
          # include the model itself.
          attr_reader :subclasses
        end
        # :nocov:

        # All descendent classes of this model.
        def descendants
          Sequel.synchronize{subclasses.dup}.map{|x| [x] + x.send(:descendants)}.flatten
        end

        # SEQUEL6: Remove
        alias descendents descendants

        # Freeze all descendent classes.  This also finalizes the associations for those
        # classes before freezing.
        def freeze_descendants
          descendants.each(&:finalize_associations).each(&:freeze)
        end

        # SEQUEL6: Remove
        alias freeze_descendents freeze_descendants

        Plugins.inherited_instance_variables(self, :@subclasses=>lambda{|v| []}, :@on_subclass=>nil)

        private

        # Add the subclass to this model's current subclasses,
        # and initialize a new subclasses instance variable
        # in the subclass.
        def inherited(subclass)
          super
          # :nocov:
          Sequel.synchronize{subclasses << subclass} if NEED_SUBCLASSES
          # :nocov:
          on_subclass.call(subclass) if on_subclass
        end
      end
    end
  end
end