From 49472d2a1036741668238676fcde44b6426afb3b Mon Sep 17 00:00:00 2001 From: Mattia Giuffrida Date: Thu, 15 Dec 2022 18:16:00 +0000 Subject: [PATCH 1/3] Add Tapioca compiler for GraphQL::Client --- lib/tapioca/dsl/compilers/graphql_client.rb | 78 +++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 lib/tapioca/dsl/compilers/graphql_client.rb diff --git a/lib/tapioca/dsl/compilers/graphql_client.rb b/lib/tapioca/dsl/compilers/graphql_client.rb new file mode 100644 index 00000000..02d975b1 --- /dev/null +++ b/lib/tapioca/dsl/compilers/graphql_client.rb @@ -0,0 +1,78 @@ +# typed: true +# frozen_string_literal: true + +require "graphql/client" +require "graphql/client/http" +require "tapioca/dsl/helpers/graphql_type_helper" + +module Tapioca + module Dsl + module Compilers + class GraphqlClient < Compiler + extend T::Sig + + # This is a hack to make Sorbet happy. + # It's necessary because there's no static class that implements this interface. + # See `GraphQL::Client::Schema::ObjectType.new` + class TypeClass + def self.fields; end + end + + ConstantType = type_member { { fixed: T.class_of(TypeClass) } } + + sig { override.returns(T::Enumerable[Module]) } + def self.gather_constants + all_modules + .select { |mod| mod.singleton_class < GraphQL::Client::Schema::ClassMethods } + .flat_map do |mod| + mod_name = qualified_name_of(mod) + next unless mod_name # Ignore anonymous modules + + mod.constants.map { |const| "#{mod_name}::#{const}".constantize } + end + .select { |c| c.is_a?(Class) } + end + + sig { override.void } + def decorate + root.create_path(constant) do |klass| + constant.fields.each do |name, definition| + define_field(klass, name, definition) + end + end + end + + private + + def define_field(klass, name, definition) + type = type_for(definition) + klass.create_method(name.to_s.underscore, return_type: type) if type + end + + def type_for(definition, nilable: true) + type = case definition + when GraphQL::Client::Schema::NonNullType + nilable = false + type_for(definition.of_klass, nilable: false) + when GraphQL::Client::Schema::ListType + sub_type = type_for(definition.of_klass, nilable: nilable) + "T::Array[#{sub_type}]" if sub_type + when GraphQL::Client::Schema::ScalarType, GraphQL::Client::Schema::EnumType + Helpers::GraphqlTypeHelper + .type_for(GraphQL::Schema::NonNull.new(definition.type)) + when GraphQL::Client::Schema::UnionType, GraphQL::Client::Schema::InterfaceType + nil + when Class + definition.name + else + raise "Unrecognised definition: #{definition}" + end + return unless type + + type = "T.nilable(#{type})" if nilable && type != "T.untyped" + type + end + end + end + end +end From 493c3481cbe8ac727c6e2d38f654cccfa763e2c0 Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 14 Dec 2023 13:10:40 +0000 Subject: [PATCH 2/3] Update graphql_client.rb to support latest tapioca version --- lib/tapioca/dsl/compilers/graphql_client.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/tapioca/dsl/compilers/graphql_client.rb b/lib/tapioca/dsl/compilers/graphql_client.rb index 02d975b1..58a9db1c 100644 --- a/lib/tapioca/dsl/compilers/graphql_client.rb +++ b/lib/tapioca/dsl/compilers/graphql_client.rb @@ -58,8 +58,10 @@ def type_for(definition, nilable: true) sub_type = type_for(definition.of_klass, nilable: nilable) "T::Array[#{sub_type}]" if sub_type when GraphQL::Client::Schema::ScalarType, GraphQL::Client::Schema::EnumType - Helpers::GraphqlTypeHelper - .type_for(GraphQL::Schema::NonNull.new(definition.type)) + argument = GraphQL::Schema::Argument.new(owner: nil) + argument.type = GraphQL::Schema::NonNull.new(definition.type) + Tapioca::Dsl::Helpers::GraphqlTypeHelper + .type_for(argument) when GraphQL::Client::Schema::UnionType, GraphQL::Client::Schema::InterfaceType nil when Class From 9e873d53719edfca8193b78c0d65b4aa57e486af Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 29 Jan 2024 10:13:51 +0000 Subject: [PATCH 3/3] Compatibility with tapioca 0.12.0+ --- lib/tapioca/dsl/compilers/graphql_client.rb | 32 ++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/tapioca/dsl/compilers/graphql_client.rb b/lib/tapioca/dsl/compilers/graphql_client.rb index 58a9db1c..5cf17dd8 100644 --- a/lib/tapioca/dsl/compilers/graphql_client.rb +++ b/lib/tapioca/dsl/compilers/graphql_client.rb @@ -22,46 +22,46 @@ def self.fields; end sig { override.returns(T::Enumerable[Module]) } def self.gather_constants - all_modules - .select { |mod| mod.singleton_class < GraphQL::Client::Schema::ClassMethods } - .flat_map do |mod| - mod_name = qualified_name_of(mod) - next unless mod_name # Ignore anonymous modules + graphql_type_modules = all_modules + .select { |mod| mod.singleton_class < GraphQL::Client::Schema::ClassMethods } + graphql_types = graphql_type_modules.flat_map do |mod| + mod_name = qualified_name_of(mod) + next unless mod_name # Ignore anonymous modules - mod.constants.map { |const| "#{mod_name}::#{const}".constantize } - end - .select { |c| c.is_a?(Class) } + mod.constants.map { |const| "#{mod_name}::#{const}".constantize } + end + graphql_types.select { |c| c.is_a?(Class) } end sig { override.void } def decorate root.create_path(constant) do |klass| constant.fields.each do |name, definition| - define_field(klass, name, definition) + define_field(klass, name, definition, constant) end end end private - def define_field(klass, name, definition) - type = type_for(definition) + def define_field(klass, name, definition, constant) + type = type_for(definition, constant) klass.create_method(name.to_s.underscore, return_type: type) if type end - def type_for(definition, nilable: true) + def type_for(definition, constant, nilable: true) type = case definition when GraphQL::Client::Schema::NonNullType nilable = false - type_for(definition.of_klass, nilable: false) + type_for(definition.of_klass, constant, nilable: false) when GraphQL::Client::Schema::ListType - sub_type = type_for(definition.of_klass, nilable: nilable) + sub_type = type_for(definition.of_klass, constant, nilable: nilable) "T::Array[#{sub_type}]" if sub_type when GraphQL::Client::Schema::ScalarType, GraphQL::Client::Schema::EnumType argument = GraphQL::Schema::Argument.new(owner: nil) argument.type = GraphQL::Schema::NonNull.new(definition.type) Tapioca::Dsl::Helpers::GraphqlTypeHelper - .type_for(argument) + .type_for_argument(argument, constant) when GraphQL::Client::Schema::UnionType, GraphQL::Client::Schema::InterfaceType nil when Class @@ -71,7 +71,7 @@ def type_for(definition, nilable: true) end return unless type - type = "T.nilable(#{type})" if nilable && type != "T.untyped" + type = "T.nilable(#{type})" if nilable && type != 'T.untyped' type end end