Module: Rails::GraphQL::Helpers::WithFields

Included in:
Alternative::FieldSet, Type::Input, Type::Interface, Type::Object
Defined in:
lib/rails/graphql/helpers/with_fields.rb

Overview

Helper module that allows other objects to hold fields during the definition process. Works very similar to Arguments, but it’s more flexible, since the type of the fields can be dynamic defined by the class that extends this module.

Fields, different from arguments, has extended types, which is somewhat related to the base type, but it’s closer associated with the strategy used to handle them.

Defined Under Namespace

Modules: ClassMethods

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.extended(other) ⇒ Object



36
37
38
39
40
# File 'lib/rails/graphql/helpers/with_fields.rb', line 36

def self.extended(other)
  other.extend(WithFields::ClassMethods)
  other.class_attribute(:field_type, instance_accessor: false)
  other.class_attribute(:valid_field_types, instance_accessor: false, default: [])
end

Instance Method Details

#change_field(object, **xargs, &block) ⇒ Object Also known as: overwrite_field

Overwrite attributes of a given field named as name, it also allows a block which will then further configure the field



82
83
84
# File 'lib/rails/graphql/helpers/with_fields.rb', line 82

def change_field(object, **xargs, &block)
  find_field!(object).apply_changes(**xargs, &block)
end

#configure_field(object, &block) ⇒ Object

Perform extra configurations on a given field



89
90
91
# File 'lib/rails/graphql/helpers/with_fields.rb', line 89

def configure_field(object, &block)
  find_field!(object).configure(&block)
end

#disable_fields(*list) ⇒ Object

Disable a list of given fields



94
95
96
# File 'lib/rails/graphql/helpers/with_fields.rb', line 94

def disable_fields(*list)
  list.flatten.map { |item| self[item]&.disable! }
end

#enable_fields(*list) ⇒ Object

Enable a list of given fields



99
100
101
# File 'lib/rails/graphql/helpers/with_fields.rb', line 99

def enable_fields(*list)
  list.flatten.map { |item| self[item]&.enable! }
end

#enabled_fieldsObject

Return a lazy enumerator for enabled fields



132
133
134
# File 'lib/rails/graphql/helpers/with_fields.rb', line 132

def enabled_fields
  lazy_each_field&.select(&:enabled?)
end

#field(name, *args, **xargs, &block) ⇒ Object

See Field class.



51
52
53
54
55
56
57
58
59
60
61
# File 'lib/rails/graphql/helpers/with_fields.rb', line 51

def field(name, *args, **xargs, &block)
  object = field_type.new(name, *args, **xargs, owner: self, &block)

  raise DuplicatedError, (+<<~MSG).squish if has_field?(object.name)
    The #{name.inspect} field is already defined and can't be redefined.
  MSG

  fields(true)[object.name] = object
rescue DefinitionError => e
  raise e.class, +"#{e.message}\n  Defined at: #{caller(2)[0]}"
end

#field_names(enabled_only = true) ⇒ Object

Get the list of GraphQL names of all the fields defined



127
128
129
# File 'lib/rails/graphql/helpers/with_fields.rb', line 127

def field_names(enabled_only = true)
  (enabled_only ? enabled_fields : lazy_each_field)&.map(&:gql_name)&.eager
end

#find_by_gid(gid) ⇒ Object

Find a specific field using its id as gql_name.type



179
180
181
# File 'lib/rails/graphql/helpers/with_fields.rb', line 179

def find_by_gid(gid)
  find_field!(gid.name)
end

#find_field(object) ⇒ Object Also known as: []

Allow accessing fields using the hash notation



111
112
113
114
115
# File 'lib/rails/graphql/helpers/with_fields.rb', line 111

def find_field(object)
  return unless fields?
  object = object.name if object.is_a?(GraphQL::Field)
  fields[object.is_a?(String) ? object.underscore.to_sym : object]
end

#find_field!(object) ⇒ Object

If the field is not found it will raise an exception



120
121
122
123
124
# File 'lib/rails/graphql/helpers/with_fields.rb', line 120

def find_field!(object)
  find_field(object) || raise(NotFoundError, (+<<~MSG).squish)
    The #{object.inspect} field is not defined yet.
  MSG
end

#has_field?(object) ⇒ Boolean

Check whether a given field object is defined in the list of fields

Returns:

  • (Boolean)


104
105
106
107
108
# File 'lib/rails/graphql/helpers/with_fields.rb', line 104

def has_field?(object)
  return false unless fields?
  object = object.name if object.is_a?(GraphQL::Field)
  fields.key?(object.is_a?(String) ? object.underscore.to_sym : object)
end

#import(source) ⇒ Object

Import one or more field into the current list of fields



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/rails/graphql/helpers/with_fields.rb', line 137

def import(source)
  # Import an alternative declaration of a field
  if source.is_a?(Module) && source <= Alternative::Query
    return proxy_field(source.field)
  end

  case source
  when Array
    # Import a list of fields
    source.each { |field| proxy_field(field) }
  when Hash, Concurrent::Map
    # Import a keyed list of fields
    source.each_value { |field| proxy_field(field) }
  when Helpers::WithFields
    # Import a set of fields
    source.fields.each_value { |field| proxy_field(field) }
  else
    return if GraphQL.config.silence_import_warnings
    GraphQL.logger.warn(+"Unable to import #{source.inspect} into #{self.name}.")
  end
end

#import_all(mod, recursive: false, **xargs) ⇒ Object

Import a module containing several classes to be imported



160
161
162
163
164
165
166
167
# File 'lib/rails/graphql/helpers/with_fields.rb', line 160

def import_all(mod, recursive: false, **xargs)
  mod.constants.each do |const_name|
    object = mod.const_get(const_name, false)

    import(object, **xargs) if object.is_a?(Class)
    import_all(object, recursive: recursive, **xargs) if recursive && object.is_a?(Module)
  end
end

#proxy_field(field, *args, **xargs, &block) ⇒ Object

Add a new field to the list but use a proxy instead of a hard copy of a given field

Raises:



65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/rails/graphql/helpers/with_fields.rb', line 65

def proxy_field(field, *args, **xargs, &block)
  field = field.field if field.is_a?(Module) && field <= Alternative::Query
  raise ArgumentError, (+<<~MSG).squish unless field.is_a?(field_type)
    The #{field.class.name} is not a valid field.
  MSG

  xargs[:owner] = self
  object = field.to_proxy(*args, **xargs, &block)
  raise DuplicatedError, (+<<~MSG).squish if has_field?(object.name)
    The #{field.name.inspect} field is already defined and can't be replaced.
  MSG

  fields(true)[object.name] = object
end

#safe_field(*args, of_type: nil, **xargs, &block) ⇒ Object

Check if the field is already defined before actually creating it



43
44
45
46
47
48
# File 'lib/rails/graphql/helpers/with_fields.rb', line 43

def safe_field(*args, of_type: nil, **xargs, &block)
  method_name = of_type.nil? ? :field : "#{of_type}_field"
  public_send(method_name, *args, **xargs, &block)
rescue DuplicatedError
  # Do not do anything if it is duplicated
end

#validate!Object

Validate all the fields to make sure the definition is valid



170
171
172
173
174
175
176
# File 'lib/rails/graphql/helpers/with_fields.rb', line 170

def validate!(*)
  super if defined? super

  # TODO: Maybe find a way to freeze the fields, since after validation
  # the best thing to do is block changes
  fields&.each_value(&:validate!)
end