graphql 1.8.4 → 1.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/templates/graphql_controller.erb +10 -0
  3. data/lib/graphql/compatibility/schema_parser_specification.rb +398 -0
  4. data/lib/graphql/execution/execute.rb +24 -18
  5. data/lib/graphql/execution/lazy.rb +14 -1
  6. data/lib/graphql/introspection/type_type.rb +1 -1
  7. data/lib/graphql/language/lexer.rb +68 -42
  8. data/lib/graphql/language/lexer.rl +2 -0
  9. data/lib/graphql/language/nodes.rb +98 -0
  10. data/lib/graphql/language/parser.rb +1050 -770
  11. data/lib/graphql/language/parser.y +50 -2
  12. data/lib/graphql/object_type.rb +4 -0
  13. data/lib/graphql/query.rb +3 -1
  14. data/lib/graphql/query/variables.rb +1 -1
  15. data/lib/graphql/schema.rb +10 -2
  16. data/lib/graphql/schema/input_object.rb +1 -1
  17. data/lib/graphql/schema/interface.rb +6 -0
  18. data/lib/graphql/schema/member/has_arguments.rb +1 -0
  19. data/lib/graphql/schema/member/instrumentation.rb +21 -16
  20. data/lib/graphql/schema/mutation.rb +1 -1
  21. data/lib/graphql/schema/object.rb +7 -2
  22. data/lib/graphql/schema/possible_types.rb +1 -1
  23. data/lib/graphql/schema/resolver.rb +210 -1
  24. data/lib/graphql/schema/validation.rb +1 -1
  25. data/lib/graphql/static_validation/message.rb +5 -1
  26. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +9 -0
  27. data/lib/graphql/type_kinds.rb +9 -6
  28. data/lib/graphql/types.rb +1 -0
  29. data/lib/graphql/types/iso_8601_date_time.rb +1 -5
  30. data/lib/graphql/unauthorized_error.rb +7 -2
  31. data/lib/graphql/version.rb +1 -1
  32. data/spec/generators/graphql/install_generator_spec.rb +12 -2
  33. data/spec/graphql/authorization_spec.rb +80 -1
  34. data/spec/graphql/directive_spec.rb +42 -0
  35. data/spec/graphql/execution_error_spec.rb +1 -1
  36. data/spec/graphql/query_spec.rb +14 -0
  37. data/spec/graphql/schema/interface_spec.rb +14 -0
  38. data/spec/graphql/schema/object_spec.rb +41 -1
  39. data/spec/graphql/schema/relay_classic_mutation_spec.rb +47 -0
  40. data/spec/graphql/schema/resolver_spec.rb +182 -8
  41. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +6 -5
  42. data/spec/graphql/static_validation/rules/no_definitions_are_present_spec.rb +34 -0
  43. data/spec/graphql/static_validation/validator_spec.rb +15 -0
  44. data/spec/support/jazz.rb +36 -1
  45. metadata +2 -2
@@ -9,9 +9,13 @@ rule
9
9
  | definitions_list definition { val[0] << val[1] }
10
10
 
11
11
  definition:
12
+ executable_definition
13
+ | type_system_definition
14
+ | type_system_extension
15
+
16
+ executable_definition:
12
17
  operation_definition
13
18
  | fragment_definition
14
- | type_system_definition
15
19
 
16
20
  operation_definition:
17
21
  operation_type operation_name_opt variable_definitions_opt directives_list_opt selection_set {
@@ -293,6 +297,47 @@ rule
293
297
  | enum_type_definition
294
298
  | input_object_type_definition
295
299
 
300
+ type_system_extension:
301
+ schema_extension
302
+ | type_extension
303
+
304
+ schema_extension:
305
+ EXTEND SCHEMA directives_list_opt LCURLY operation_type_definition_list RCURLY { return make_node(:SchemaExtension, position_source: val[0], directives: val[2], **val[4]) }
306
+ | EXTEND SCHEMA directives_list { return make_node(:SchemaExtension, position_source: val[0], directives: val[2]) }
307
+
308
+ type_extension:
309
+ scalar_type_extension
310
+ | object_type_extension
311
+ | interface_type_extension
312
+ | union_type_extension
313
+ | enum_type_extension
314
+ | input_object_type_extension
315
+
316
+ scalar_type_extension: EXTEND SCALAR name directives_list { return make_node(:ScalarTypeExtension, name: val[2], directives: val[3], position_source: val[0]) }
317
+
318
+ object_type_extension:
319
+ /* TODO - This first one shouldn't be necessary but parser is getting confused */
320
+ EXTEND TYPE name implements LCURLY field_definition_list RCURLY { return make_node(:ObjectTypeExtension, name: val[2], interfaces: val[3], directives: [], fields: val[5], position_source: val[0]) }
321
+ | EXTEND TYPE name implements_opt directives_list_opt LCURLY field_definition_list RCURLY { return make_node(:ObjectTypeExtension, name: val[2], interfaces: val[3], directives: val[4], fields: val[6], position_source: val[0]) }
322
+ | EXTEND TYPE name implements_opt directives_list { return make_node(:ObjectTypeExtension, name: val[2], interfaces: val[3], directives: val[4], fields: [], position_source: val[0]) }
323
+ | EXTEND TYPE name implements { return make_node(:ObjectTypeExtension, name: val[2], interfaces: val[3], directives: [], fields: [], position_source: val[0]) }
324
+
325
+ interface_type_extension:
326
+ EXTEND INTERFACE name directives_list_opt LCURLY field_definition_list RCURLY { return make_node(:InterfaceTypeExtension, name: val[2], directives: val[3], fields: val[5], position_source: val[0]) }
327
+ | EXTEND INTERFACE name directives_list { return make_node(:InterfaceTypeExtension, name: val[2], directives: val[3], fields: [], position_source: val[0]) }
328
+
329
+ union_type_extension:
330
+ EXTEND UNION name directives_list_opt EQUALS union_members { return make_node(:UnionTypeExtension, name: val[2], directives: val[3], types: val[5], position_source: val[0]) }
331
+ | EXTEND UNION name directives_list { return make_node(:UnionTypeExtension, name: val[2], directives: val[3], types: [], position_source: val[0]) }
332
+
333
+ enum_type_extension:
334
+ EXTEND ENUM name directives_list_opt LCURLY enum_value_definitions RCURLY { return make_node(:EnumTypeExtension, name: val[2], directives: val[3], values: val[5], position_source: val[0]) }
335
+ | EXTEND ENUM name directives_list { return make_node(:EnumTypeExtension, name: val[2], directives: val[3], values: [], position_source: val[0]) }
336
+
337
+ input_object_type_extension:
338
+ EXTEND INPUT name directives_list_opt LCURLY input_value_definition_list RCURLY { return make_node(:InputObjectTypeExtension, name: val[2], directives: val[3], fields: val[5], position_source: val[0]) }
339
+ | EXTEND INPUT name directives_list { return make_node(:InputObjectTypeExtension, name: val[2], directives: val[3], fields: [], position_source: val[0]) }
340
+
296
341
  scalar_type_definition: SCALAR name directives_list_opt { return make_node(:ScalarTypeDefinition, name: val[1], directives: val[2], description: get_description(val[0]), position_source: val[0]) }
297
342
 
298
343
  object_type_definition:
@@ -302,7 +347,10 @@ rule
302
347
 
303
348
  implements_opt:
304
349
  /* none */ { return [] }
305
- | IMPLEMENTS AMP interfaces_list { return val[2] }
350
+ | implements
351
+
352
+ implements:
353
+ IMPLEMENTS AMP interfaces_list { return val[2] }
306
354
  | IMPLEMENTS interfaces_list { return val[1] }
307
355
  | IMPLEMENTS legacy_interfaces_list { return val[1] }
308
356
 
@@ -102,6 +102,10 @@ module GraphQL
102
102
  dirty_ifaces.concat(interfaces)
103
103
  end
104
104
 
105
+ def resolve_type_proc
106
+ nil
107
+ end
108
+
105
109
  protected
106
110
 
107
111
  attr_reader :dirty_interfaces, :dirty_inherited_interfaces
@@ -76,7 +76,9 @@ module GraphQL
76
76
  # @param max_complexity [Numeric] the maximum field complexity for this query (falls back to schema-level value)
77
77
  # @param except [<#call(schema_member, context)>] If provided, objects will be hidden from the schema when `.call(schema_member, context)` returns truthy
78
78
  # @param only [<#call(schema_member, context)>] If provided, objects will be hidden from the schema when `.call(schema_member, context)` returns false
79
- def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: {}, validate: true, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: nil, max_complexity: nil, except: nil, only: nil)
79
+ def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: nil, max_complexity: nil, except: nil, only: nil)
80
+ # Even if `variables: nil` is passed, use an empty hash for simpler logic
81
+ variables ||= {}
80
82
  @schema = schema
81
83
  @filter = schema.default_filter.merge(except: except, only: only)
82
84
  @context = schema.context_class.new(query: self, object: root_value, values: context)
@@ -43,7 +43,7 @@ module GraphQL
43
43
  elsif value_was_provided
44
44
  # Add the variable if a value was provided
45
45
  memo[variable_name] = variable_type.coerce_input(provided_value, ctx)
46
- elsif default_value
46
+ elsif default_value != nil
47
47
  # Add the variable if it wasn't provided but it has a default value (including `null`)
48
48
  memo[variable_name] = GraphQL::Query::LiteralInput.coerce(variable_type, default_value, self)
49
49
  end
@@ -462,6 +462,10 @@ module GraphQL
462
462
  object = object.object
463
463
  end
464
464
 
465
+ if type.respond_to?(:graphql_definition)
466
+ type = type.graphql_definition
467
+ end
468
+
465
469
  # Prefer a type-local function; fall back to the schema-level function
466
470
  type_proc = type && type.resolve_type_proc
467
471
  type_result = if type_proc
@@ -867,6 +871,10 @@ module GraphQL
867
871
  #
868
872
  # By default, this hook just replaces the unauthorized object with `nil`.
869
873
  #
874
+ # Whatever value is returned from this method will be used instead of the
875
+ # unauthorized object (accessible ass `unauthorized_error.object`). If an
876
+ # error is raised, then `nil` will be used.
877
+ #
870
878
  # If you want to add an error to the `"errors"` key, raise a {GraphQL::ExecutionError}
871
879
  # in this hook.
872
880
  #
@@ -997,11 +1005,11 @@ module GraphQL
997
1005
  result = value.public_send(lazy_method)
998
1006
  # The returned result might also be lazy, so check it, too
999
1007
  after_lazy(result) do |final_result|
1000
- yield(final_result)
1008
+ yield(final_result) if block_given?
1001
1009
  end
1002
1010
  end
1003
1011
  else
1004
- yield(value)
1012
+ yield(value) if block_given?
1005
1013
  end
1006
1014
  end
1007
1015
 
@@ -27,7 +27,7 @@ module GraphQL
27
27
  attr_reader :arguments
28
28
 
29
29
  # Ruby-like hash behaviors, read-only
30
- def_delegators :@ruby_style_hash, :keys, :values, :each, :any?
30
+ def_delegators :@ruby_style_hash, :keys, :values, :each, :any?, :empty?
31
31
 
32
32
  def to_h
33
33
  @ruby_style_hash.inject({}) do |h, (key, value)|
@@ -61,6 +61,12 @@ module GraphQL
61
61
  child_class.extend(interface_defn::DefinitionMethods)
62
62
  end
63
63
  elsif child_class < GraphQL::Schema::Object
64
+ # Add all definition methods of this interface and the interfaces it
65
+ # includes onto the child class.
66
+ (interfaces + [self] - [GraphQL::Schema::Interface]).each do |interface_defn|
67
+ child_class.extend(interface_defn::DefinitionMethods)
68
+ end
69
+
64
70
  # This is being included into an object type, make sure it's using `implements(...)`
65
71
  backtrace_line = caller(0, 10).find { |line| line.include?("schema/object.rb") && line.include?("in `implements'")}
66
72
  if !backtrace_line
@@ -17,6 +17,7 @@ module GraphQL
17
17
  kwargs[:owner] = self
18
18
  arg_defn = self.argument_class.new(*args, **kwargs, &block)
19
19
  own_arguments[arg_defn.name] = arg_defn
20
+ arg_defn
20
21
  end
21
22
 
22
23
  # @return [Hash<String => GraphQL::Schema::Argument] Arguments defined on this thing, keyed by name. Includes inherited definitions
@@ -78,8 +78,6 @@ module GraphQL
78
78
  ctx.wrapped_object = true
79
79
  proxy_to_depth(result, @list_depth, ctx)
80
80
  end
81
- rescue GraphQL::UnauthorizedError => err
82
- ctx.schema.unauthorized_object(err)
83
81
  end
84
82
 
85
83
  private
@@ -88,21 +86,28 @@ module GraphQL
88
86
  if depth > 0
89
87
  inner_obj.map { |i| proxy_to_depth(i, depth - 1, ctx) }
90
88
  else
91
- concrete_type = case @inner_return_type
92
- when GraphQL::UnionType, GraphQL::InterfaceType
93
- ctx.query.resolve_type(@inner_return_type, inner_obj)
94
- when GraphQL::ObjectType
95
- @inner_return_type
96
- else
97
- raise "unexpected proxying type #{@inner_return_type} for #{inner_obj} at #{ctx.owner_type}.#{ctx.field.name}"
98
- end
89
+ ctx.schema.after_lazy(inner_obj) do |inner_obj|
90
+ if inner_obj.nil?
91
+ # For lists with nil, we need another nil check here
92
+ nil
93
+ else
94
+ concrete_type = case @inner_return_type
95
+ when GraphQL::UnionType, GraphQL::InterfaceType
96
+ ctx.query.resolve_type(@inner_return_type, inner_obj)
97
+ when GraphQL::ObjectType
98
+ @inner_return_type
99
+ else
100
+ raise "unexpected proxying type #{@inner_return_type} for #{inner_obj} at #{ctx.owner_type}.#{ctx.field.name}"
101
+ end
99
102
 
100
- if concrete_type && (object_class = concrete_type.metadata[:type_class])
101
- # use the query-level context here, since it won't be field-specific anyways
102
- query_ctx = ctx.query.context
103
- object_class.authorized_new(inner_obj, query_ctx)
104
- else
105
- inner_obj
103
+ if concrete_type && (object_class = concrete_type.metadata[:type_class])
104
+ # use the query-level context here, since it won't be field-specific anyways
105
+ query_ctx = ctx.query.context
106
+ object_class.authorized_new(inner_obj, query_ctx)
107
+ else
108
+ inner_obj
109
+ end
110
+ end
106
111
  end
107
112
  end
108
113
  end
@@ -17,7 +17,7 @@ module GraphQL
17
17
  # argument :post_id, ID, required: true
18
18
  #
19
19
  # field :comment, Types::Comment, null: true
20
- # field :error_messages, [String], null: false
20
+ # field :errors, [String], null: false
21
21
  #
22
22
  # def resolve(body:, post_id:)
23
23
  # post = Post.find(post_id)
@@ -39,7 +39,13 @@ module GraphQL
39
39
  if is_authorized
40
40
  self.new(object, context)
41
41
  else
42
- raise GraphQL::UnauthorizedError.new(object: object, type: self, context: context)
42
+ # It failed the authorization check, so go to the schema's authorized object hook
43
+ err = GraphQL::UnauthorizedError.new(object: object, type: self, context: context)
44
+ # If a new value was returned, wrap that instead of the original value
45
+ new_obj = context.schema.unauthorized_object(err)
46
+ if new_obj
47
+ self.new(new_obj, context)
48
+ end
43
49
  end
44
50
  end
45
51
  end
@@ -94,7 +100,6 @@ module GraphQL
94
100
  obj_type.interfaces = interfaces
95
101
  obj_type.introspection = introspection
96
102
  obj_type.mutation = mutation
97
-
98
103
  fields.each do |field_name, field_inst|
99
104
  field_defn = field_inst.to_graphql
100
105
  obj_type.fields[field_defn.name] = field_defn
@@ -31,7 +31,7 @@ module GraphQL
31
31
  when GraphQL::BaseType
32
32
  [type_defn]
33
33
  else
34
- raise "Unexpected possible_types object: #{type_defn}"
34
+ raise "Unexpected possible_types object: #{type_defn.inspect}"
35
35
  end
36
36
  end
37
37
  end
@@ -29,6 +29,12 @@ module GraphQL
29
29
  def initialize(object:, context:)
30
30
  @object = object
31
31
  @context = context
32
+ # Since this hash is constantly rebuilt, cache it for this call
33
+ @arguments_by_keyword = {}
34
+ self.class.arguments.each do |name, arg|
35
+ @arguments_by_keyword[arg.keyword] = arg
36
+ end
37
+ @arguments_loads_as_type = self.class.arguments_loads_as_type
32
38
  end
33
39
 
34
40
  # @return [Object] The application object this field is being resolved on
@@ -37,12 +43,170 @@ module GraphQL
37
43
  # @return [GraphQL::Query::Context]
38
44
  attr_reader :context
39
45
 
46
+ # This method is _actually_ called by the runtime,
47
+ # it does some preparation and then eventually calls
48
+ # the user-defined `#resolve` method.
49
+ # @api private
50
+ def resolve_with_support(**args)
51
+ # First call the before_prepare hook which may raise
52
+ before_prepare_val = if args.any?
53
+ before_prepare(**args)
54
+ else
55
+ before_prepare
56
+ end
57
+ context.schema.after_lazy(before_prepare_val) do
58
+ # Then call each prepare hook, which may return a different value
59
+ # for that argument, or may return a lazy object
60
+ load_arguments_val = load_arguments(args)
61
+ context.schema.after_lazy(load_arguments_val) do |loaded_args|
62
+ # Then validate each argument, which may raise or may return a lazy object
63
+ validate_arguments_val = validate_arguments(loaded_args)
64
+ context.schema.after_lazy(validate_arguments_val) do |validated_args|
65
+ # Finally, all the hooks have passed, so resolve it
66
+ if validated_args.any?
67
+ public_send(self.class.resolve_method, **validated_args)
68
+ else
69
+ public_send(self.class.resolve_method)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+
40
76
  # Do the work. Everything happens here.
41
77
  # @return [Object] An object corresponding to the return type
42
78
  def resolve(**args)
43
79
  raise NotImplementedError, "#{self.class.name}#resolve should execute the field's logic"
44
80
  end
45
81
 
82
+ # Called before arguments are prepared.
83
+ # Implement this hook to make checks before doing any work.
84
+ #
85
+ # If it returns a lazy object (like a promise), it will be synced by GraphQL
86
+ # (but the resulting value won't be used).
87
+ #
88
+ # @param args [Hash] The input arguments, if there are any
89
+ # @raise [GraphQL::ExecutionError] To add an error to the response
90
+ # @raise [GraphQL::UnauthorizedError] To signal an authorization failure
91
+ def before_prepare(**args)
92
+ end
93
+
94
+ private
95
+
96
+ def load_arguments(args)
97
+ prepared_args = {}
98
+ prepare_lazies = []
99
+
100
+ args.each do |key, value|
101
+ arg_defn = @arguments_by_keyword[key]
102
+ if arg_defn
103
+ prepped_value = prepared_args[key] = load_argument(key, value)
104
+ if context.schema.lazy?(prepped_value)
105
+ prepare_lazies << context.schema.after_lazy(prepped_value) do |finished_prepped_value|
106
+ prepared_args[key] = finished_prepped_value
107
+ end
108
+ end
109
+ else
110
+ # These are `extras: [...]`
111
+ prepared_args[key] = value
112
+ end
113
+ end
114
+
115
+ # Avoid returning a lazy if none are needed
116
+ if prepare_lazies.any?
117
+ GraphQL::Execution::Lazy.all(prepare_lazies).then { prepared_args }
118
+ else
119
+ prepared_args
120
+ end
121
+ end
122
+
123
+ def load_argument(name, value)
124
+ public_send("load_#{name}", value)
125
+ end
126
+
127
+ # TODO dedup with load_arguments
128
+ def validate_arguments(args)
129
+ validate_lazies = []
130
+ args.each do |key, value|
131
+ arg_defn = @arguments_by_keyword[key]
132
+ if arg_defn
133
+ validate_return = validate_argument(key, value)
134
+ if context.schema.lazy?(validate_return)
135
+ validate_lazies << context.schema.after_lazy(validate_return).then { "no-op" }
136
+ end
137
+ end
138
+ end
139
+
140
+ # Avoid returning a lazy if none are needed
141
+ if validate_lazies.any?
142
+ GraphQL::Execution::Lazy.all(validate_lazies).then { args }
143
+ else
144
+ args
145
+ end
146
+ end
147
+
148
+ def validate_argument(name, value)
149
+ public_send("validate_#{name}", value)
150
+ end
151
+
152
+ class LoadApplicationObjectFailedError < GraphQL::ExecutionError
153
+ # @return [GraphQL::Schema::Argument] the argument definition for the argument that was looked up
154
+ attr_reader :argument
155
+ # @return [String] The ID provided by the client
156
+ attr_reader :id
157
+ # @return [Object] The value found with this ID
158
+ attr_reader :object
159
+ def initialize(argument:, id:, object:)
160
+ @id = id
161
+ @argument = argument
162
+ @object = object
163
+ super("No object found for `#{argument.graphql_name}: #{id.inspect}`")
164
+ end
165
+ end
166
+
167
+ def load_application_object(arg_kwarg, id)
168
+ argument = @arguments_by_keyword[arg_kwarg]
169
+ # See if any object can be found for this ID
170
+ application_object = context.schema.object_from_id(id, context)
171
+ if application_object.nil?
172
+ raise LoadApplicationObjectFailedError.new(argument: argument, id: id, object: application_object)
173
+ end
174
+ # Double-check that the located object is actually of this type
175
+ # (Don't want to allow arbitrary access to objects this way)
176
+ lookup_as_type = @arguments_loads_as_type[arg_kwarg]
177
+ application_object_type = context.schema.resolve_type(lookup_as_type, application_object, context)
178
+ possible_object_types = context.schema.possible_types(lookup_as_type)
179
+ if !possible_object_types.include?(application_object_type)
180
+ raise LoadApplicationObjectFailedError.new(argument: argument, id: id, object: application_object)
181
+ else
182
+ # This object was loaded successfully
183
+ # and resolved to the right type,
184
+ # now apply the `.authorized?` class method if there is one
185
+ if (class_based_type = application_object_type.metadata[:type_class])
186
+ context.schema.after_lazy(class_based_type.authorized?(application_object, context)) do |authed|
187
+ if authed
188
+ application_object
189
+ else
190
+ raise GraphQL::UnauthorizedError.new(
191
+ object: application_object,
192
+ type: class_based_type,
193
+ context: context,
194
+ )
195
+ end
196
+ end
197
+ else
198
+ application_object
199
+ end
200
+ end
201
+ rescue LoadApplicationObjectFailedError => err
202
+ # pass it to a handler
203
+ load_application_object_failed(err)
204
+ end
205
+
206
+ def load_application_object_failed(err)
207
+ raise err
208
+ end
209
+
46
210
  class << self
47
211
  # Default `:resolve` set below.
48
212
  # @return [Symbol] The method to call on instances of this object to resolve the field
@@ -113,7 +277,7 @@ module GraphQL
113
277
  type: type_expr,
114
278
  description: description,
115
279
  extras: extras,
116
- method: resolve_method,
280
+ method: :resolve_with_support,
117
281
  resolver_class: self,
118
282
  arguments: arguments,
119
283
  null: null,
@@ -125,6 +289,51 @@ module GraphQL
125
289
  def type_expr
126
290
  @type_expr || (superclass.respond_to?(:type_expr) ? superclass.type_expr : nil)
127
291
  end
292
+
293
+ # Add an argument to this field's signature, but
294
+ # also add some preparation hook methods which will be used for this argument
295
+ # @see {GraphQL::Schema::Argument#initialize} for the signature
296
+ def argument(name, type, *rest, loads: nil, **kwargs, &block)
297
+ if loads
298
+ arg_keyword = name.to_s.sub(/_id$/, "").to_sym
299
+ kwargs[:as] = arg_keyword
300
+ own_arguments_loads_as_type[arg_keyword] = loads
301
+ end
302
+ arg_defn = super(name, type, *rest, **kwargs, &block)
303
+
304
+ if loads
305
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
306
+ def load_#{arg_defn.keyword}(value)
307
+ load_application_object(:#{arg_defn.keyword}, value)
308
+ end
309
+ RUBY
310
+ else
311
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
312
+ def load_#{arg_defn.keyword}(value)
313
+ value
314
+ end
315
+ RUBY
316
+ end
317
+
318
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
319
+ def validate_#{arg_defn.keyword}(value)
320
+ # No-op
321
+ end
322
+ RUBY
323
+ arg_defn
324
+ end
325
+
326
+ # @api private
327
+ def arguments_loads_as_type
328
+ inherited_lookups = superclass.respond_to?(:arguments_loads_as_type) ? superclass.arguments_loads_as_type : {}
329
+ inherited_lookups.merge(own_arguments_loads_as_type)
330
+ end
331
+
332
+ private
333
+
334
+ def own_arguments_loads_as_type
335
+ @own_arguments_loads_as_type ||= {}
336
+ end
128
337
  end
129
338
  end
130
339
  end