File: has_interfaces.rb

package info (click to toggle)
ruby-graphql 2.2.17-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 9,584 kB
  • sloc: ruby: 67,505; ansic: 1,753; yacc: 831; javascript: 331; makefile: 6
file content (143 lines) | stat: -rw-r--r-- 5,304 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
# frozen_string_literal: true

module GraphQL
  class Schema
    class Member
      module HasInterfaces
        def implements(*new_interfaces, **options)
          new_memberships = []
          new_interfaces.each do |int|
            if int.is_a?(Module)
              unless int.include?(GraphQL::Schema::Interface)
                raise "#{int} cannot be implemented since it's not a GraphQL Interface. Use `include` for plain Ruby modules."
              end

              new_memberships << int.type_membership_class.new(int, self, **options)

              # Include the methods here,
              # `.fields` will use the inheritance chain
              # to find inherited fields
              include(int)

              # If this interface has interfaces of its own, add those, too
              int.interfaces.each do |next_interface|
                implements(next_interface)
              end
            elsif int.is_a?(String) || int.is_a?(GraphQL::Schema::LateBoundType)
              if options.any?
                raise ArgumentError, "`implements(...)` doesn't support options with late-loaded types yet. Remove #{options} and open an issue to request this feature."
              end
              new_memberships << int
            else
              raise ArgumentError, "Unexpected interface definition (expected module): #{int} (#{int.class})"
            end
          end

          # Remove any String or late-bound interfaces which are being replaced
          own_interface_type_memberships.reject! { |old_i_m|
            if !(old_i_m.respond_to?(:abstract_type) && old_i_m.abstract_type.is_a?(Module))
              old_int_type = old_i_m.respond_to?(:abstract_type) ? old_i_m.abstract_type : old_i_m
              old_name = Schema::Member::BuildType.to_type_name(old_int_type)

              new_memberships.any? { |new_i_m|
                new_int_type = new_i_m.respond_to?(:abstract_type) ? new_i_m.abstract_type : new_i_m
                new_name = Schema::Member::BuildType.to_type_name(new_int_type)

                new_name == old_name
              }
            end
          }
          own_interface_type_memberships.concat(new_memberships)
        end

        def own_interface_type_memberships
          @own_interface_type_memberships ||= []
        end

        def interface_type_memberships
          own_interface_type_memberships
        end

        module ClassConfigured
          # This combination of extended -> inherited -> extended
          # means that the base class (`Schema::Object`) *won't*
          # have the superclass-related code in `InheritedInterfaces`,
          # but child classes of `Schema::Object` will have it.
          # That way, we don't need a `superclass.respond_to?(...)` check.
          def inherited(child_class)
            super
            child_class.extend(InheritedInterfaces)
          end

          module InheritedInterfaces
            def interfaces(context = GraphQL::Query::NullContext.instance)
              visible_interfaces = super
              inherited_interfaces = superclass.interfaces(context)
              if visible_interfaces.any?
                if inherited_interfaces.any?
                  visible_interfaces.concat(inherited_interfaces)
                  visible_interfaces.uniq!
                end
                visible_interfaces
              elsif inherited_interfaces.any?
                inherited_interfaces
              else
                EmptyObjects::EMPTY_ARRAY
              end
            end

            def interface_type_memberships
              own_tms = super
              inherited_tms = superclass.interface_type_memberships
              if inherited_tms.size > 0
                own_tms + inherited_tms
              else
                own_tms
              end
            end
          end
        end

        # param context [Query::Context] If omitted, skip filtering.
        def interfaces(context = GraphQL::Query::NullContext.instance)
          warden = Warden.from_context(context)
          visible_interfaces = nil
          own_interface_type_memberships.each do |type_membership|
            case type_membership
            when Schema::TypeMembership
              if warden.visible_type_membership?(type_membership, context)
                visible_interfaces ||= []
                visible_interfaces << type_membership.abstract_type
              end
            when String, Schema::LateBoundType
              # During initialization, `type_memberships` can hold late-bound types
              visible_interfaces ||= []
              visible_interfaces << type_membership
            else
              raise "Invariant: Unexpected type_membership #{type_membership.class}: #{type_membership.inspect}"
            end
          end
          if visible_interfaces
            visible_interfaces.uniq!
            visible_interfaces
          else
            EmptyObjects::EMPTY_ARRAY
          end
        end

        private

        def self.extended(child_class)
          child_class.extend(ClassConfigured)
        end

        def inherited(subclass)
          super
          subclass.class_exec do
            @own_interface_type_memberships ||= nil
          end
        end
      end
    end
  end
end