Is your feature request related to a problem? Please describe.
I am restructuring the folder and aiming to reduce indirection.
The folder structure I want to achieve:
- graphql
- base_types
- base_argument.rb
- base_enum.rb
- base_field.rb
...
- view_models
- failure.rb
- success.rb
- error.rb
- mutations
schema.rb
In models/failure.rb
module ViewModels
class Failure
class Type < BaseTypes::BaseObject
field :accountable, String, null: false
field :errors, [ViewModels::Error], null: false
end
attr_reader :errors
def initialize(errors:)
@errors = errors
end
def accountable
'CLIENT'
end
end
end
In models/success.rb
module ViewModels
module Success
module Interface
include BaseTypes::BaseInterface
...
end
end
end
This is essentially a lazy way to have type ViewModels::Failure::Type and implementation ViewModels::Failure defined closely in the same file, meanwhile it's Rails 6 Zeitwerk autoloader compatible.
However, I cannot find a easy way to override the default graphql type name generator, because graphql_name and default_graphql_name are used in some inconsistent ways across different components. So far, I am only able to override GraphQL::Schema::Object.self.graphql_name. If I monkey patch GraphQL::Schema::Member::BaseDSLMethods.graphql_name, things like Directive will break. And for Interfaces, I couldn't get it to work at all.
So my first question, is there any existing way designed to achieve type name customization?
Describe the solution you'd like
If no, I'd love to have an easy way to properly configure type naming.
Some common pattern such as
config/initializers/grpahql_ruby.rbGraphQL.configure do |config|
config.graphql_name = lambda (provided_name)
name_parts = name.split('::')
selection = name_parts.size > 1 ? name_parts[-2..-1] : [name_parts.last]
selection.join.sub(/Type\Z/, '')
end
end
graphql/application_schema.rbclass ApplicationSchema < GraphQL::Schema
def self.graphql_name(provided_name)
...
end
end
Describe alternatives you've considered
If it's going to take huge efforts and delay to achieve what I mentioned, would you plz give me at least some examples/guidelines/direction on how customization is currently possible for interfaces?
Thank heaps. You guys have done a great job on designing and maintaining the library 馃檪
override
GraphQL::Schema::Object.self.graphql_name
You're on the right track there! The catch is that Interfaces are a bit wacky because they're modules -- it takes some tricks to get them to properly "inherit" class methods. The default_graphql_name override has to be in the definition_methods do ... end block:
https://graphql-ruby.org/type_definitions/interfaces.html#definition-methods
Altogether, here's an example of overriding default_graphql_name on every base type, using a shared module:
require "bundler/inline"
gemfile do
source "https://rubygems.org"
gem "graphql", "1.10.6"
end
module CustomGraphQLName
def default_graphql_name
super + "CustomName"
end
end
class BaseObject < GraphQL::Schema::Object
extend CustomGraphQLName
end
module BaseInterface
include GraphQL::Schema::Interface
definition_methods do
include CustomGraphQLName
end
end
class BaseScalar < GraphQL::Schema::Scalar
extend CustomGraphQLName
end
class BaseUnion < GraphQL::Schema::Union
extend CustomGraphQLName
end
class BaseEnum < GraphQL::Schema::Enum
extend CustomGraphQLName
end
class BaseInputObject < GraphQL::Schema::InputObject
extend CustomGraphQLName
end
class UUID < BaseScalar
end
module Node
include BaseInterface
field :id, UUID, null: false
end
class Thing < BaseObject; implements Node; end
class OtherThing < BaseObject; implements Node; end
class Stuff < BaseUnion
possible_types Thing, OtherThing
end
class Setting < BaseEnum
value "A"
value "B"
end
class Options < BaseInputObject
argument :opt1, Boolean, required: false
end
class Query < BaseObject
implements Node
field :stuff, Stuff, null: true do
argument :enum, Setting, required: false
argument :input_obj, Options, required: false
end
end
class Schema < GraphQL::Schema
query Query
end
puts Schema.to_definition
It outputs a schema were ever type name has been customized:
schema {
query: QueryCustomName
}
interface NodeCustomName {
id: UUIDCustomName!
}
input OptionsCustomName {
opt1: Boolean
}
type OtherThingCustomName implements NodeCustomName {
id: UUIDCustomName!
}
type QueryCustomName implements NodeCustomName {
id: UUIDCustomName!
stuff(enum: SettingCustomName, inputObj: OptionsCustomName): StuffCustomName
}
enum SettingCustomName {
A
B
}
union StuffCustomName = OtherThingCustomName | ThingCustomName
type ThingCustomName implements NodeCustomName {
id: UUIDCustomName!
}
scalar UUIDCustomName
I think a solution like that should work in your case too, feel free to follow up here if you run into any trouble with it!
This, is, so, elegant.
And thanks for teaching me puts Schema.to_definition馃憤
Most helpful comment
You're on the right track there! The catch is that Interfaces are a bit wacky because they're modules -- it takes some tricks to get them to properly "inherit" class methods. The
default_graphql_nameoverride has to be in thedefinition_methods do ... endblock:https://graphql-ruby.org/type_definitions/interfaces.html#definition-methods
Altogether, here's an example of overriding
default_graphql_nameon every base type, using a shared module:It outputs a schema were ever type name has been customized:
I think a solution like that should work in your case too, feel free to follow up here if you run into any trouble with it!