Class: Rails::GraphQL::Source::ActiveRecordSource

Inherits:
Base show all
Extended by:
MySQL::SourceMethods, PG::SourceMethods, Rails::GraphQL::SQLite::SourceMethods, Builders
Includes:
ScopedArguments
Defined in:
lib/rails/graphql/source/active_record_source.rb

Overview

GraphQL Source Active Record

This source allows the translation of active record objects into a new source object, creating:

  1. 1 Object

  2. 1 Input

  3. 2 Query fields (singular and plural)

  4. 3 Mutation fields (create, update, destroy)

Defined Under Namespace

Modules: Builders

Constant Summary

Constants included from Helpers::WithSchemaFields

Helpers::WithSchemaFields::TYPE_FIELD_CLASS

Constants included from Helpers::InheritedCollection

Helpers::InheritedCollection::DEFAULT_TYPES

Constants included from Helpers::WithCallbacks

Helpers::WithCallbacks::DEFAULT_EVENT_TYPES

Instance Attribute Summary

Attributes included from Helpers::Instantiable

#event

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Builders

build_primary_key_arguments, each_attribute, each_reflection, id_columns

Methods included from ScopedArguments

included, #inject_scopes

Methods inherited from Base

input, object

Methods included from Helpers::WithSchemaFields

#[], #add_field, #add_proxy_field, #change_field, #configure_field, #configure_fields, #disable_fields, #enable_fields, #enabled_fields_from, extended, #field_names_for, #fields_for, #fields_for?, #find_by_gid, #find_field, #find_field!, #has_field?, #import_all, #import_all_into, #import_into, #safe_add_field, #type_name_for, #validate!

Methods included from Helpers::WithAssignment

#assigned?, #assigned_class, #assigned_to, #assigned_to=, extended, #register!, #safe_assigned_class, #valid_assignment?

Methods included from Helpers::Unregisterable

#unregister!

Methods inherited from Rails::GraphQL::Source

attach_fields!, base_name, base_type_class, find_for, find_for!, kind, schemas, step

Methods included from Helpers::InheritedCollection

#inherited_collection

Methods included from Helpers::WithNamespace

#namespace, #namespaces, #set_namespace

Methods included from Helpers::WithEvents

extended, included, #on

Methods included from Helpers::WithCallbacks

extended, included, #on

Methods included from Builder

#build_all, #build_all_sources, #built?, #method_missing, #respond_to_missing?

Class Method Details

.assigned_toObject

Set the assignment to a model with a similar name as the source



127
128
129
130
# File 'lib/rails/graphql/source/active_record_source.rb', line 127

def assigned_to
  return @assigned_to if defined?(@assigned_to)
  @assigned_to = base_name.gsub('_', '::')
end

.collection_fieldObject

Provides access to the default plural query field, for associations interconnection



156
157
158
# File 'lib/rails/graphql/source/active_record_source.rb', line 156

def collection_field
  find_field(:query, plural)
end

.enumsObject

Stores columns associated with enums so that the fields can have a correctly assigned type



134
135
136
# File 'lib/rails/graphql/source/active_record_source.rb', line 134

def enums
  @enums ||= model.defined_enums.dup
end

.interface?Boolean

Checks if the source is building an interface instead of an object

Returns:

  • (Boolean)


150
151
152
153
# File 'lib/rails/graphql/source/active_record_source.rb', line 150

def interface?
  defined?(@interface) || act_as_interface == true ||
    (act_as_interface != false && sti_interface?)
end

.unregister!Object

Hook into the unregister to clean enums



161
162
163
164
# File 'lib/rails/graphql/source/active_record_source.rb', line 161

def unregister!
  super
  @enums = nil
end

Instance Method Details

#build_association_scope(association) ⇒ Object

Collect a scope for filters applied to a given association



242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/rails/graphql/source/active_record_source.rb', line 242

def build_association_scope(association)
  scope = model._reflect_on_association(association).klass.default_scoped

  # Apply proxied injected scopes
  # TODO: Arguments comes with their proxy, so we might not need this
  # proxied = event.field.try(:proxied_owner)
  # scope = event.on_instance(proxied) do |instance|
  #   instance.inject_scopes(scope, :relation)
  # end if proxied.present? && proxied <= Source::ActiveRecordSource

  # Apply self defined injected scopes
  inject_scopes(scope, :relation)
end

#create_recordObject

The perform step for the create based mutation



213
214
215
216
217
218
# File 'lib/rails/graphql/source/active_record_source.rb', line 213

def create_record
  input_argument.resource.tap(&:save!)
rescue ::ActiveRecord::RecordInvalid => error
  errors_to_extensions(error.record.errors)
  raise
end

#destroy_recordObject

The perform step for the delete based mutation



229
230
231
232
233
234
# File 'lib/rails/graphql/source/active_record_source.rb', line 229

def destroy_record
  !!current_value.destroy!
rescue ::ActiveRecord::RecordInvalid => error
  errors_to_extensions(error.record.errors)
  raise
end

#errors_to_extensions(errors, path = nil, format = nil) ⇒ Object

Expose the errors to the extensions of the response



273
274
275
276
277
278
279
280
# File 'lib/rails/graphql/source/active_record_source.rb', line 273

def errors_to_extensions(errors, path = nil, format = nil)
  format ||= self.class.errors_to_extensions
  return unless format

  path ||= [operation.name, field.gql_name].compact
  hash = GraphQL.enumerate(path).reduce(request.extensions) { |h, k| h[k] ||= {} }
  hash.replace(format == :messages ? errors.as_json : errors.details)
end

#load_record(scope = nil, find_by: nil) ⇒ Object

Prepare to load a single record from the underlying model



206
207
208
209
210
# File 'lib/rails/graphql/source/active_record_source.rb', line 206

def load_record(scope = nil, find_by: nil)
  scope ||= event.last_result || model.default_scoped
  find_by ||= { primary_key => event.argument(primary_key) }
  inject_scopes(scope, :relation).find_by(find_by)
end

#load_records(scope = nil) ⇒ Object

Prepare to load multiple records from the underlying model



200
201
202
203
# File 'lib/rails/graphql/source/active_record_source.rb', line 200

def load_records(scope = nil)
  scope ||= event.last_result || model.default_scoped
  inject_scopes(scope, :relation)
end

#parent_owned_records(collection_result = false) ⇒ Object

Once the records are pre-loaded due to preload_association, use the parent value and the preloader result to get the records



258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/rails/graphql/source/active_record_source.rb', line 258

def parent_owned_records(collection_result = false)
  # The absence of the prepared data key means we got to a point that we
  # don't know the result of the association, so we simply call it
  unless event.data.key?(:prepared_data)
    return current_value.public_send(field.method_name)
  end

  data = event.data[:prepared_data]
  return collection_result ? [] : nil unless data

  result = data.records_by_owner[current_value] || []
  collection_result ? result : result.first
end

#preload_association(association, scope = nil) ⇒ Object

Get the chain result and preload the records with the resulting scope



237
238
239
# File 'lib/rails/graphql/source/active_record_source.rb', line 237

def preload_association(association, scope = nil)
  event.stop(preload(association, scope || event.last_result), layer: :object)
end

#update_recordObject

The perform step for the update based mutation



221
222
223
224
225
226
# File 'lib/rails/graphql/source/active_record_source.rb', line 221

def update_record
  current_value.tap { |record| record.update!(**input_argument.params) }
rescue ::ActiveRecord::RecordInvalid => error
  errors_to_extensions(error.record.errors)
  raise
end