Class: Rails::GraphQL::Directive

Inherits:
Object
  • Object
show all
Extended by:
ActiveSupport::Autoload, Helpers::Registerable, Helpers::WithArguments, Helpers::WithCallbacks, Helpers::WithEvents, Helpers::WithGlobalID
Defined in:
lib/rails/graphql/directive.rb

Overview

GraphQL Directive

This is the base object for directives definition. See: spec.graphql.org/June2018/#DirectiveDefinition

Whenever you want to use a directive, you can use the ClassName(…) shortcut (which is the same as ClassName.new(…)).

Directives works as event listener and trigger, which means that some actions will trigger directives events, and the directive can listen to these events and perform an action

Examples

argument :test, :boolean, directives: FlagDirective()

# On defining an enum value
add :old_value, directives: DeprecatedDirective(reason: 'not used anymore')

Defined Under Namespace

Classes: CachedDirective, DeprecatedDirective, IncludeDirective, SkipDirective, SpecifiedByDirective

Constant Summary collapse

EXECUTION_LOCATIONS =
%i[
  query mutation subscription field fragment_definition fragment_spread inline_fragment
].to_set.freeze
DEFINITION_LOCATIONS =
%i[
  schema scalar object field_definition argument_definition interface union
  enum enum_value input_object input_field_definition
].to_set.freeze
VALID_LOCATIONS =
(EXECUTION_LOCATIONS + DEFINITION_LOCATIONS).freeze

Constants included from Helpers::WithCallbacks

Helpers::WithCallbacks::DEFAULT_EVENT_TYPES

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Helpers::WithEvents

extended, included, on

Methods included from Helpers::WithCallbacks

extended, included, on

Methods included from Helpers::WithArguments

=~, argument, extended, has_argument?, has_arguments?, id_argument, included, initialize_copy, ref_argument

Methods included from Helpers::WithGlobalID

to_gid_param, to_global_id

Methods included from Helpers::Registerable

aliases, extended, inherited, register!, registered?

Constructor Details

#initialize(args = nil, **xargs) ⇒ Directive

Returns a new instance of Directive.



178
179
180
181
# File 'lib/rails/graphql/directive.rb', line 178

def initialize(args = nil, **xargs)
  @args = args || OpenStruct.new(xargs.transform_keys { |key| key.to_s.underscore })
  @args.freeze
end

Instance Attribute Details

#argsObject (readonly)

Returns the value of attribute args.



176
177
178
# File 'lib/rails/graphql/directive.rb', line 176

def args
  @args
end

#eventObject (readonly)

Returns the value of attribute event.



176
177
178
# File 'lib/rails/graphql/directive.rb', line 176

def event
  @event
end

Class Method Details

.base_typeObject Also known as: gid_base_class

Ensure to return the directive class



48
49
50
# File 'lib/rails/graphql/directive.rb', line 48

def base_type
  GraphQL::Directive
end

.build(**xargs) ⇒ Object

A helper method that allows directives to be initialized while correctly parsing the arguments



83
84
85
86
87
88
89
90
# File 'lib/rails/graphql/directive.rb', line 83

def build(**xargs)
  xargs = xargs.stringify_keys
  result = all_arguments&.each&.each_with_object({}) do |(name, argument), hash|
    hash[name] = argument.deserialize(xargs[argument.gql_name] || xargs[name.to_s])
  end

  new(**result)
end

.find_by_gid(gid) ⇒ Object

Return the directive, instantiate if it has params



93
94
95
96
97
# File 'lib/rails/graphql/directive.rb', line 93

def find_by_gid(gid)
  options = { namespaces: gid.namespace, base_class: :Directive }
  klass = GraphQL.type_map.fetch!(gid.name, **options)
  gid.instantiate? ? klass.build(**gid.params) : klass
end

.gql_nameObject

Return the name of the object as a GraphQL name, ensure to use the first letter as lower case when being auto generated



56
57
58
59
# File 'lib/rails/graphql/directive.rb', line 56

def gql_name
  return @gql_name if defined?(@gql_name)
  @gql_name = super.camelize(:lower)
end

.inspectObject



99
100
101
102
103
104
105
106
107
# File 'lib/rails/graphql/directive.rb', line 99

def inspect
  return super if eql?(GraphQL::Directive)

  repeatable = ' [repeatable]' if repeatable?
  args = all_arguments&.each_value&.map(&:inspect)
  args = args.force if args.respond_to?(:force)
  args = args.presence && "(#{args.join(', ')})"
  +"#<GraphQL::Directive @#{gql_name}#{repeatable}#{args}>"
end

.kindObject



43
44
45
# File 'lib/rails/graphql/directive.rb', line 43

def kind
  :directive
end

.locationsObject

Get the list of locations of a the directive



62
63
64
# File 'lib/rails/graphql/directive.rb', line 62

def locations
  @locations ||= Set.new
end

.placed_on(*list) ⇒ Object

A secure way to specify the locations of a the directive



67
68
69
70
71
72
73
# File 'lib/rails/graphql/directive.rb', line 67

def placed_on(*list)
  validate_locations!(list)
  @locations = (superclass.try(:locations)&.dup || Set.new) \
    unless defined?(@locations)

  @locations.merge(list)
end

.placed_on!(*list) ⇒ Object

This method overrides the locations of a the directive



76
77
78
79
# File 'lib/rails/graphql/directive.rb', line 76

def placed_on!(*list)
  validate_locations!(list)
  @locations = list.to_set
end

Instance Method Details

#+(other) ⇒ Object Also known as: &

This allows combining directives



238
239
240
# File 'lib/rails/graphql/directive.rb', line 238

def +(other)
  [self, other].flatten
end

#all_eventsObject

When fetching all the events, embed the actual instance as the context of the callback TODO: Maybe add a soft cached, based on the total number of events



211
212
213
214
215
216
217
# File 'lib/rails/graphql/directive.rb', line 211

def all_events
  return unless self.class.events?

  self.class.all_events.transform_values do |events|
    events.map { |item| Callback.set_context(item, self) }
  end
end

#args_as_jsonObject

Correctly turn all the arguments into their as_json version and return a hash of them



194
195
196
197
198
# File 'lib/rails/graphql/directive.rb', line 194

def args_as_json
  all_arguments&.each&.with_object({}) do |(name, argument), hash|
    hash[argument.gql_name] = argument.as_json(@args[name])
  end
end

#args_to_jsonObject

Correctly turn all the arguments into their to_json version and return a hash of them



202
203
204
205
206
# File 'lib/rails/graphql/directive.rb', line 202

def args_to_json
  all_arguments&.each&.with_object({}) do |(name, argument), hash|
    hash[argument.gql_name] = argument.to_json(@args[name])
  end
end

#assign_owner!(owner) ⇒ Object

Once the directive is correctly prepared, we need to assign the owner

Raises:



184
185
186
187
188
189
190
# File 'lib/rails/graphql/directive.rb', line 184

def assign_owner!(owner)
  raise ArgumentError, (+<<~MSG).squish if defined?(@owner)
    Owner already assigned for @#{gql_name} directive.
  MSG

  @owner = owner
end

#inspectObject



244
245
246
247
248
249
250
251
252
253
# File 'lib/rails/graphql/directive.rb', line 244

def inspect
  args = all_arguments&.filter_map do |name, arg|
    +"#{arg.gql_name}: #{@args[name].inspect}" unless @args[name].nil?
  end

  args = args.presence && +"(#{args.join(', ')})"
  repeatable = ' [repeatable]' if repeatable?
  unbound = ' # unbound' unless defined?(@owner)
  +"@#{gql_name}#{repeatable}#{args}#{unbound}"
end

#validate!Object

Checks if all the arguments provided to the directive instance are valid

Raises:



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/rails/graphql/directive.rb', line 220

def validate!(*)
  raise ArgumentError, (+<<~MSG).squish unless defined?(@owner)
    The @#{gql_name} directive is unbounded.
  MSG

  invalid = all_arguments&.reject { |name, arg| arg.valid?(@args[name]) }
  return if invalid.blank?

  invalid = invalid.each_key.map { |name| (+<<~MSG).squish }
    invalid value "#{@args[name].inspect}" for #{name} argument
  MSG

  raise ArgumentError, (+<<~MSG).squish
    Invalid usage of @#{gql_name} directive: #{invalid.to_sentence}.
  MSG
end