Class: Parse::GraphQL::TypeGenerator
- Inherits:
-
Object
- Object
- Parse::GraphQL::TypeGenerator
- Defined in:
- lib/parse/graphql/type_generator.rb
Overview
Generates GraphQL::Schema::Object subclasses from Parse::Object
subclasses by reading the class-level property and association
registries: fields, field_map, references (belongs_to),
has_one_associations, has_many_associations, and relations.
v1 scope: type shape only. Default graphql-ruby field resolution
invokes the same-named method on the underlying Ruby object, and
Parse::Object subclasses already expose typed accessors
(song.album, band.fans, etc.) — so no resolvers are emitted.
Pagination arguments, Loaders, and Relay connections are deferred
until query/mutation passthrough lands (TODO §7).
Cross-class references (pointers, has_many) require all referenced
model types to be in the registry BEFORE field emission. Use
generate_all to codegen a list of models in one call (two-pass:
stub classes first, then add fields), or generate with an
explicit registry: hash if you want incremental control.
Constant Summary collapse
- SCALAR_TYPE_MAP =
Maps Parse property
:typesymbols to graphql-ruby built-ins. Cross-class references (:pointer,:relation) are NOT in this map — they need the target class and are handled per-field. Nil mappings fall through to the JSON scalar with a warning. { string: ::GraphQL::Types::String, integer: ::GraphQL::Types::Int, float: ::GraphQL::Types::Float, boolean: ::GraphQL::Types::Boolean, date: ::GraphQL::Types::ISO8601DateTime, timezone: ::GraphQL::Types::String, phone: ::GraphQL::Types::String, email: ::GraphQL::Types::String, # Parse Bytes is a `{__type: Bytes, base64: ...}` wrapper, not a # bare string — the accessor returns the wrapped object, so # falling through to the JSON scalar (with a warn) is safer than # ::String.to_s on the hash. Subscribers needing structured byte # access should declare a `:string` property containing the base64. bytes: nil, polygon: nil, # JSON fallback (GeoJSON-shaped, multi-ring) # Parse vector columns are bounded-length Float arrays # (embeddings). Emit as a typed list of Floats, not JSON. vector: [::GraphQL::Types::Float], array: nil, # JSON fallback (no element type known) object: nil, # JSON fallback }.freeze
- OMITTED_TYPES =
Property types that are deliberately omitted. ACL is internal authz metadata; exposing it via GraphQL would leak authorization shape into clients. The objectId is exposed under the canonical
id: IDfield separately. %i[acl].freeze
Class Method Summary collapse
- .build_stub(model_class) ⇒ Object
-
.detect_name_collisions!(registry) ⇒ Object
graphql-ruby requires unique
graphql_nameacross the schema. -
.generate(model_class, registry: nil) ⇒ Class
Generate a single type.
-
.generate_all(model_classes) ⇒ Hash{String => Class}
Generate types for a list of models in one call.
- .validate!(model_class) ⇒ Object
Instance Method Summary collapse
-
#initialize(model_class, registry:, _prebuilt:) ⇒ TypeGenerator
constructor
A new instance of TypeGenerator.
- #populate_fields ⇒ Object
Constructor Details
#initialize(model_class, registry:, _prebuilt:) ⇒ TypeGenerator
Returns a new instance of TypeGenerator.
128 129 130 131 132 |
# File 'lib/parse/graphql/type_generator.rb', line 128 def initialize(model_class, registry:, _prebuilt:) @model_class = model_class @registry = registry @klass = _prebuilt end |
Class Method Details
.build_stub(model_class) ⇒ Object
119 120 121 122 123 124 125 126 |
# File 'lib/parse/graphql/type_generator.rb', line 119 def self.build_stub(model_class) type_name = model_class.parse_class.tr("_", "") description_text = "Generated GraphQL type for Parse class #{model_class.parse_class}." Class.new(::GraphQL::Schema::Object) do graphql_name type_name description description_text end end |
.detect_name_collisions!(registry) ⇒ Object
graphql-ruby requires unique graphql_name across the schema.
build_stub strips underscores so _User and User collapse
to the same name. Raise a clear error rather than letting
graphql-ruby's DuplicateNamesError surface at schema-build
time, which doesn't say which Parse classes collided.
84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/parse/graphql/type_generator.rb', line 84 def self.detect_name_collisions!(registry) by_gql_name = registry.each_with_object({}) do |(parse_name, type), acc| (acc[type.graphql_name] ||= []) << parse_name end collisions = by_gql_name.select { |_, names| names.size > 1 } return if collisions.empty? details = collisions.map { |gql, parse| "#{gql} ← #{parse.join(', ')}" }.join('; ') raise "Parse::GraphQL::TypeGenerator: graphql_name collisions: #{details}. " \ "Parse class names that differ only by underscores collapse to the same " \ "GraphQL type name. Rename or generate the conflicting classes separately." end |
.generate(model_class, registry: nil) ⇒ Class
Generate a single type. If the model has belongs_to / has_many
references to other Parse classes, those targets must already be
in the registry — otherwise an error is raised at field-emit
time. Prefer generate_all when you have a graph of models.
104 105 106 107 108 109 110 |
# File 'lib/parse/graphql/type_generator.rb', line 104 def self.generate(model_class, registry: nil) validate!(model_class) registry ||= {} stub = registry[model_class.parse_class] ||= build_stub(model_class) new(model_class, registry: registry, _prebuilt: stub).populate_fields stub end |
.generate_all(model_classes) ⇒ Hash{String => Class}
Generate types for a list of models in one call. Two-pass: creates empty stub classes for every model first so cross-class references resolve regardless of declaration order.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/parse/graphql/type_generator.rb', line 64 def self.generate_all(model_classes) registry = {} # Pass 1: stub classes registered by parse_class name. model_classes.each do |model| validate!(model) registry[model.parse_class] = build_stub(model) end # Pass 2: populate fields. Cross-references now resolve. model_classes.each do |model| new(model, registry: registry, _prebuilt: registry[model.parse_class]).populate_fields end detect_name_collisions!(registry) registry end |
.validate!(model_class) ⇒ Object
112 113 114 115 116 117 |
# File 'lib/parse/graphql/type_generator.rb', line 112 def self.validate!(model_class) unless model_class.is_a?(Class) && model_class < Parse::Object raise ArgumentError, "Parse::GraphQL::TypeGenerator requires a Parse::Object subclass, got #{model_class.inspect}" end end |
Instance Method Details
#populate_fields ⇒ Object
134 135 136 137 138 139 140 |
# File 'lib/parse/graphql/type_generator.rb', line 134 def populate_fields emit_scalar_fields emit_belongs_to_fields emit_has_one_fields emit_has_many_fields @klass end |