graphql 1.8.4 → 1.8.5

Sign up to get free protection for your applications and to get access to all the features.
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