graphql 1.8.0.pre3 → 1.8.0.pre4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2a150383d3c4ffcaf126e675d8e844f93c4d2244
4
- data.tar.gz: 76b98bf791afc9ca8cd0eb9caf9798005d058169
3
+ metadata.gz: ef7a7ef6e77dbf150ac668f99f293298e3764e9d
4
+ data.tar.gz: 702a3a337b3c53a813bd699637a93c985f7f5bf0
5
5
  SHA512:
6
- metadata.gz: 4850a2d2c7872936c2ef09e016e6da4d6afba9bc554f76f08cc7f747d38e336e183ce201f184053364c468241b25442483ac83eab4e9bb63da2a74f19a67a202
7
- data.tar.gz: 853775ca411cab71eae69b917071ebb0c03c61d1a4557d38ff8fb4410b05da3e34dad381098ba41417b03e39114d6e1ba36aab441fc3297fffb356d0222d1b73
6
+ metadata.gz: b9e377b6787dda4fe97be9687545adb6f0fd9625df8f3233ee069ff70d96e660b3768bb58ef5b7ddbb330df3322dbe1aba23b13af53cd661dc69fe8e5d61a9e4
7
+ data.tar.gz: 628e7b0c786292b2009ce08b799c8c9db340a289ca3d3ac59b1cc9f454639cd321a327cefb9e77954311a1bd5994ffdd4ed5bd5f0715bbd38b5118345b861dfa
@@ -1,11 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './upgrader/member'
4
- require_relative './upgrader/schema'
5
3
 
6
4
  module GraphQL
7
5
  class Railtie < Rails::Railtie
8
6
  rake_tasks do
7
+ # Defer this so that you only need the `parser` gem when you _run_ the upgrader
8
+ def load_upgraders
9
+ require_relative './upgrader/member'
10
+ require_relative './upgrader/schema'
11
+ end
12
+
9
13
  namespace :graphql do
10
14
  task :upgrade, [:dir] do |t, args|
11
15
  unless (dir = args[:dir])
@@ -82,7 +86,7 @@ module GraphQL
82
86
 
83
87
  task :schema, [:schema_file] do |t, args|
84
88
  schema_file = args.schema_file
85
-
89
+ load_upgraders
86
90
  upgrader = GraphQL::Upgrader::Schema.new File.read(schema_file)
87
91
 
88
92
  puts "- Transforming schema #{schema_file}"
@@ -91,7 +95,7 @@ module GraphQL
91
95
 
92
96
  task :member, [:member_file] do |t, args|
93
97
  member_file = args.member_file
94
-
98
+ load_upgraders
95
99
  upgrader = GraphQL::Upgrader::Member.new File.read(member_file)
96
100
  next unless upgrader.upgradeable?
97
101
 
@@ -33,20 +33,15 @@ module GraphQL
33
33
  def value(graphql_name, description = nil, value: nil, deprecation_reason: nil)
34
34
  graphql_name = graphql_name.to_s
35
35
  value ||= graphql_name
36
- own_values << Value.new(graphql_name, description, value, deprecation_reason)
36
+ own_values[graphql_name] = Value.new(graphql_name, description, value, deprecation_reason)
37
37
  nil
38
38
  end
39
39
 
40
- # @return [Array<GraphQL::Schema::Enum::Value>]
40
+ # @return [Hash<String => GraphQL::Schema::Enum::Value>] Possible values of this enum, keyed by name
41
41
  def values
42
- all_values = own_values
43
- inherited_values = superclass <= GraphQL::Schema::Enum ? superclass.values : []
44
- inherited_values.each do |inherited_v|
45
- if all_values.none? { |v| v.name == inherited_v.name }
46
- all_values << inherited_v
47
- end
48
- end
49
- all_values
42
+ inherited_values = superclass <= GraphQL::Schema::Enum ? superclass.values : {}
43
+ # Local values take precedence over inherited ones
44
+ inherited_values.merge(own_values)
50
45
  end
51
46
 
52
47
  # @return [GraphQL::EnumType]
@@ -55,7 +50,7 @@ module GraphQL
55
50
  enum_type.name = graphql_name
56
51
  enum_type.description = description
57
52
  enum_type.introspection = introspection
58
- values.each do |val|
53
+ values.each do |name, val|
59
54
  enum_value = GraphQL::EnumType::EnumValue.new
60
55
  enum_value.name = val.name
61
56
  enum_value.description = val.description
@@ -69,7 +64,7 @@ module GraphQL
69
64
  private
70
65
 
71
66
  def own_values
72
- @own_values ||= []
67
+ @own_values ||= {}
73
68
  end
74
69
  end
75
70
  end
@@ -11,9 +11,12 @@ module GraphQL
11
11
  attr_reader :name
12
12
 
13
13
  # @return [String]
14
- attr_reader :description
14
+ attr_accessor :description
15
15
 
16
- def initialize(name, return_type_expr = nil, desc = nil, null: nil, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, connection: nil, max_page_size: nil, resolve: nil, introspection: false, extras: [], &args_block)
16
+ # @return [Hash{String => GraphQL::Schema::Argument}]
17
+ attr_reader :arguments
18
+
19
+ def initialize(name, return_type_expr = nil, desc = nil, null: nil, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, connection: nil, max_page_size: nil, resolve: nil, introspection: false, extras: [], &definition_block)
17
20
  if !(field || function)
18
21
  if return_type_expr.nil?
19
22
  raise ArgumentError, "missing positional argument `type`"
@@ -37,11 +40,29 @@ module GraphQL
37
40
  @method = method
38
41
  @return_type_expr = return_type_expr
39
42
  @return_type_null = null
40
- @args_block = args_block
41
43
  @connection = connection
42
44
  @max_page_size = max_page_size
43
45
  @introspection = introspection
44
46
  @extras = extras
47
+ @arguments = {}
48
+
49
+ if definition_block
50
+ instance_eval(&definition_block)
51
+ end
52
+ end
53
+
54
+ # This is the `argument(...)` DSL for class-based field definitons
55
+ def argument(*args)
56
+ arg_defn = self.class.argument_class.new(*args)
57
+ arguments[arg_defn.name] = arg_defn
58
+ end
59
+
60
+ def description(text = nil)
61
+ if text
62
+ @description = text
63
+ else
64
+ @description
65
+ end
45
66
  end
46
67
 
47
68
  # @return [GraphQL::Field]
@@ -93,17 +114,20 @@ module GraphQL
93
114
  field_defn.connection_max_page_size = @max_page_size
94
115
  field_defn.introspection = @introspection
95
116
 
96
- field_proxy = FieldProxy.new(field_defn, argument_class: self.class.argument_class)
97
117
  # apply this first, so it can be overriden below
98
118
  if connection
99
- field_proxy.argument :after, "String", "Returns the elements in the list that come after the specified global ID.", required: false
100
- field_proxy.argument :before, "String", "Returns the elements in the list that come before the specified global ID.", required: false
101
- field_proxy.argument :first, "Int", "Returns the first _n_ elements from the list.", required: false
102
- field_proxy.argument :last, "Int", "Returns the last _n_ elements from the list.", required: false
119
+ # TODO: this could be a bit weird, because these fields won't be present
120
+ # after initialization, only in the `to_graphql` response.
121
+ # This calculation _could_ be moved up if need be.
122
+ argument :after, "String", "Returns the elements in the list that come after the specified global ID.", required: false
123
+ argument :before, "String", "Returns the elements in the list that come before the specified global ID.", required: false
124
+ argument :first, "Int", "Returns the first _n_ elements from the list.", required: false
125
+ argument :last, "Int", "Returns the last _n_ elements from the list.", required: false
103
126
  end
104
127
 
105
- if @args_block
106
- field_proxy.instance_eval(&@args_block)
128
+ @arguments.each do |name, defn|
129
+ arg_graphql = defn.to_graphql
130
+ field_defn.arguments[arg_graphql.name] = arg_graphql
107
131
  end
108
132
 
109
133
  field_defn
@@ -120,32 +144,6 @@ module GraphQL
120
144
  end
121
145
  end
122
146
  end
123
-
124
-
125
- # This object exists only to be `instance_eval`'d
126
- # when the `field(...)` method is called with a block.
127
- # This object receives that block.
128
- class FieldProxy
129
- def initialize(field, argument_class:)
130
- @field = field
131
- @argument_class = argument_class
132
- end
133
-
134
- # This is the `argument(...)` DSL for class-based field definitons
135
- def argument(*args)
136
- arg = @argument_class.new(*args)
137
- graphql_arg = arg.graphql_definition
138
- @field.arguments[graphql_arg.name] = graphql_arg
139
- end
140
-
141
- def description(text)
142
- if @field.description
143
- fail "You're overriding the description of #{@field.name} in the provided block!"
144
- else
145
- @field.description = text
146
- end
147
- end
148
- end
149
147
  end
150
148
  end
151
149
  end
@@ -6,6 +6,7 @@ module GraphQL
6
6
  class DynamicResolve
7
7
  def initialize(method_name:, connection:, extras:)
8
8
  @method_name = method_name
9
+ @method_sym = method_name.to_sym
9
10
  @connection = connection
10
11
  @extras = extras
11
12
  end
@@ -15,8 +16,9 @@ module GraphQL
15
16
  public_send_field(obj, @method_name, args, ctx)
16
17
  elsif obj.object.respond_to?(@method_name)
17
18
  public_send_field(obj.object, @method_name, args, ctx)
18
- elsif obj.is_a?(Hash)
19
- obj[@method_name]
19
+ elsif obj.object.is_a?(Hash)
20
+ inner_object = obj.object
21
+ inner_object[@method_name] || inner_object[@method_sym]
20
22
  else
21
23
  raise <<-ERR
22
24
  Failed to implement #{ctx.irep_node.owner_type.name}.#{ctx.field.name}, tried:
@@ -19,34 +19,30 @@ module GraphQL
19
19
 
20
20
  def argument(*args)
21
21
  argument = GraphQL::Schema::Argument.new(*args)
22
- own_arguments << argument
23
22
  arg_name = argument.graphql_definition.name
23
+ own_arguments[arg_name] = argument
24
24
  # Add a method access
25
25
  define_method(Member::BuildType.underscore(arg_name)) do
26
26
  @arguments.public_send(arg_name)
27
27
  end
28
28
  end
29
29
 
30
+ # @return [Hash<String => GraphQL::Schema::Argument] Input fields on this input object, keyed by name.
30
31
  def arguments
31
- all_arguments = own_arguments
32
- inherited_arguments = (superclass <= GraphQL::Schema::InputObject ? superclass.arguments : [])
33
- inherited_arguments.each do |inherited_a|
34
- if all_arguments.none? { |a| a.name == inherited_a.name }
35
- all_arguments << inherited_a
36
- end
37
- end
38
- all_arguments
32
+ inherited_arguments = (superclass <= GraphQL::Schema::InputObject ? superclass.arguments : {})
33
+ # Local definitions override inherited ones
34
+ inherited_arguments.merge(own_arguments)
39
35
  end
40
36
 
41
37
  def own_arguments
42
- @own_arguments ||= []
38
+ @own_arguments ||= {}
43
39
  end
44
40
 
45
41
  def to_graphql
46
42
  type_defn = GraphQL::InputObjectType.new
47
43
  type_defn.name = graphql_name
48
44
  type_defn.description = description
49
- arguments.each do |arg|
45
+ arguments.each do |name, arg|
50
46
  type_defn.arguments[arg.graphql_definition.name] = arg.graphql_definition
51
47
  end
52
48
  # Make a reference to a classic-style Arguments class
@@ -20,7 +20,7 @@ module GraphQL
20
20
  type_defn = GraphQL::InterfaceType.new
21
21
  type_defn.name = graphql_name
22
22
  type_defn.description = description
23
- fields.each do |field_inst|
23
+ fields.each do |field_name, field_inst|
24
24
  field_defn = field_inst.graphql_definition
25
25
  type_defn.fields[field_defn.name] = field_defn
26
26
  end
@@ -4,6 +4,8 @@ module GraphQL
4
4
  class Member
5
5
  # @api private
6
6
  module BuildType
7
+ LIST_TYPE_ERROR = "Use an array of [T] or [T, null: true] for list types; other arrays are not supported"
8
+
7
9
  module_function
8
10
  # @param type_expr [String, Class, GraphQL::BaseType]
9
11
  # @return [GraphQL::BaseType]
@@ -46,12 +48,21 @@ module GraphQL
46
48
  when GraphQL::BaseType, GraphQL::Schema::LateBoundType
47
49
  type_expr
48
50
  when Array
49
- if type_expr.length != 1
50
- raise "Use an array of length = 1 for list types; other arrays are not supported"
51
+ case type_expr.length
52
+ when 1
53
+ list_type = true
54
+ # List members are required by default
55
+ parse_type(type_expr.first, null: false)
56
+ when 2
57
+ inner_type, nullable_option = type_expr
58
+ if nullable_option.keys != [:null] || nullable_option.values != [true]
59
+ raise ArgumentError, LIST_TYPE_ERROR
60
+ end
61
+ list_type = true
62
+ parse_type(inner_type, null: true)
63
+ else
64
+ raise ArgumentError, LIST_TYPE_ERROR
51
65
  end
52
- list_type = true
53
- # List members are required by default
54
- parse_type(type_expr.first, null: false)
55
66
  when Class
56
67
  if Class < GraphQL::Schema::Member
57
68
  type_expr.graphql_definition
@@ -13,25 +13,18 @@ module GraphQL
13
13
  nil
14
14
  end
15
15
 
16
- # @return [Array<GraphQL::Schema::Field>] Fields on this object, including inherited fields
16
+ # @return [Hash<String => GraphQL::Schema::Field>] Fields on this object, keyed by name, including inherited fields
17
17
  def fields
18
- all_fields = own_fields
19
- inherited_fields = (superclass.is_a?(HasFields) ? superclass.fields : [])
20
- # Remove any inherited fields which were overridden on this class:
21
- inherited_fields.each do |inherited_f|
22
- if all_fields.none? {|f| f.name == inherited_f.name}
23
- all_fields << inherited_f
24
- end
25
- end
26
- all_fields
18
+ inherited_fields = (superclass.is_a?(HasFields) ? superclass.fields : {})
19
+ # Local overrides take precedence over inherited fields
20
+ inherited_fields.merge(own_fields)
27
21
  end
28
22
 
29
23
  # Register this field with the class, overriding a previous one if needed
30
24
  # @param field_defn [GraphQL::Schema::Field]
31
25
  # @return [void]
32
26
  def add_field(field_defn)
33
- own_fields.reject! {|f| f.name == field_defn.name}
34
- own_fields << field_defn
27
+ own_fields[field_defn.name] = field_defn
35
28
  nil
36
29
  end
37
30
 
@@ -48,7 +41,7 @@ module GraphQL
48
41
 
49
42
  # @return [Array<GraphQL::Schema::Field>] Fields defined on this class _specifically_, not parent classes
50
43
  def own_fields
51
- @own_fields ||= []
44
+ @own_fields ||= {}
52
45
  end
53
46
  end
54
47
  end
@@ -17,7 +17,7 @@ module GraphQL
17
17
  new_interfaces.each do |int|
18
18
  if int.is_a?(Class) && int < GraphQL::Schema::Interface
19
19
  # Add the graphql field defns
20
- int.fields.each do |field|
20
+ int.fields.each do |_name, field|
21
21
  add_field(field)
22
22
  end
23
23
  # And call the implemented hook
@@ -47,7 +47,7 @@ module GraphQL
47
47
  obj_type.interfaces = interfaces
48
48
  obj_type.introspection = introspection
49
49
 
50
- fields.each do |field_inst|
50
+ fields.each do |field_name, field_inst|
51
51
  field_defn = field_inst.to_graphql
52
52
  obj_type.fields[field_defn.name] = field_defn
53
53
  end
@@ -95,12 +95,18 @@ module GraphQL
95
95
  # Remove newlines -- normalize the text for processing
96
96
  class RemoveNewlinesTransform
97
97
  def apply(input_text)
98
- input_text.gsub(/(?<field>(?:field|connection|argument).*?,)\n(\s*)(?<next_line>(:?"|field)(.*))/) do
99
- field = $~[:field].chomp
100
- next_line = $~[:next_line]
101
-
102
- "#{field} #{next_line}"
98
+ keep_looking = true
99
+ while keep_looking do
100
+ keep_looking = false
101
+ input_text = input_text.gsub(/(?<field>(?:field|connection|argument).*?,)\n(\s*)(?<next_line>.*)/) do
102
+ keep_looking = true
103
+ field = $~[:field].chomp
104
+ next_line = $~[:next_line]
105
+
106
+ "#{field} #{next_line}"
107
+ end
103
108
  end
109
+ input_text
104
110
  end
105
111
  end
106
112
 
@@ -141,7 +147,7 @@ module GraphQL
141
147
  ) do
142
148
  field = $~[:field]
143
149
  block_contents = $~[:block_contents]
144
- kwarg_value = $~[:kwarg_value]
150
+ kwarg_value = $~[:kwarg_value].strip
145
151
 
146
152
  "#{field}, #{@kwarg}: #{kwarg_value} do#{block_contents}"
147
153
  end
@@ -155,6 +161,28 @@ module GraphQL
155
161
  end
156
162
  end
157
163
 
164
+ # Find a keyword whose value is a string or symbol,
165
+ # and if the value is equivalent to the field name,
166
+ # remove the keyword altogether.
167
+ class RemoveRedundantKwargTransform < Transform
168
+ def initialize(kwarg:)
169
+ @kwarg = kwarg
170
+ @finder_pattern = /(field|connection|argument) :(?<name>[a-zA-Z_0-9]*).*#{@kwarg}: ['":](?<kwarg_value>[a-zA-Z_0-9]+)['"]?/
171
+ end
172
+
173
+ def apply(input_text)
174
+ if input_text =~ @finder_pattern
175
+ field_name = $~[:name]
176
+ kwarg_value = $~[:kwarg_value]
177
+ if field_name == kwarg_value
178
+ # It's redundant, remove it
179
+ input_text = input_text.sub(/, #{@kwarg}: ['":]#{kwarg_value}['"]?/, "")
180
+ end
181
+ end
182
+ input_text
183
+ end
184
+ end
185
+
158
186
  # Take camelized field names and convert them to underscore case.
159
187
  # (They'll be automatically camelized later.)
160
188
  class UnderscoreizeFieldNameTransform < Transform
@@ -196,7 +224,13 @@ module GraphQL
196
224
 
197
225
  input_text.match(/(?<field_type>input_field|field|connection|argument) :(?<name>[a-zA-Z_0-9_]*)/)
198
226
  field_name = $~[:name]
199
- field_ast = Parser::CurrentRuby.parse(input_text)
227
+ begin
228
+ field_ast = Parser::CurrentRuby.parse(input_text)
229
+ rescue Parser::SyntaxError
230
+ puts "Error text:"
231
+ puts input_text
232
+ raise
233
+ end
200
234
  processor = ResolveProcProcessor.new
201
235
  processor.process(field_ast)
202
236
  proc_body = input_text[processor.proc_start..processor.proc_end]
@@ -288,9 +322,14 @@ module GraphQL
288
322
  ) do
289
323
  indent = $~[:indent]
290
324
  interfaces = $~[:interfaces].split(',').map(&:strip).reject(&:empty?)
291
- interfaces.map do |interface|
292
- "#{indent}implements #{interface}"
293
- end.join
325
+ # Preserve leading newlines before the `interfaces ...`
326
+ # call, but don't re-insert them between `implements` calls.
327
+ extra_leading_newlines = "\n" * (indent[/^\n*/].length - 1)
328
+ indent = indent.sub(/^\n*/m, "")
329
+ interfaces_calls = interfaces
330
+ .map { |interface| "\n#{indent}implements #{interface}" }
331
+ .join
332
+ extra_leading_newlines + interfaces_calls
294
333
  end
295
334
  end
296
335
  end
@@ -298,23 +337,27 @@ module GraphQL
298
337
  class UpdateMethodSignatureTransform < Transform
299
338
  def apply(input_text)
300
339
  input_text.scan(/(?:input_field|field|connection|argument) .*$/).each do |field|
301
- matches = /(?<field_type>input_field|field|connection|argument) :(?<name>[a-zA-Z_0-9_]*)?, (?<return_type>[^,]*)(?<remainder>.*)/.match(field)
340
+ matches = /(?<field_type>input_field|field|connection|argument) :(?<name>[a-zA-Z_0-9_]*)?, (?<return_type>([A-Za-z\[\]\.\!_0-9]|::|-> ?\{ ?| ?\})+)(?<remainder>( |,|$).*)/.match(field)
302
341
  if matches
303
342
  name = matches[:name]
304
343
  return_type = matches[:return_type]
305
344
  remainder = matches[:remainder]
306
345
  field_type = matches[:field_type]
307
-
308
- # This is a small bug in the regex. Ideally the `do` part would only be in the remainder.
309
- with_block = remainder.gsub!(/\ do$/, '') || return_type.gsub!(/\ do$/, '')
346
+ with_block = remainder.gsub!(/\ do$/, '')
310
347
 
311
348
  remainder.gsub! /,$/, ''
312
349
  remainder.gsub! /^,/, ''
313
350
  remainder.chomp!
314
351
 
315
- has_bang = !(return_type.gsub! '!', '')
316
- return_type = normalize_type_expression(return_type)
317
- return_type = return_type.gsub ',', ''
352
+ if return_type
353
+ non_nullable = return_type.gsub! '!', ''
354
+ nullable = !non_nullable
355
+ return_type = normalize_type_expression(return_type)
356
+ return_type = return_type.gsub ',', ''
357
+ else
358
+ non_nullable = nil
359
+ nullable = nil
360
+ end
318
361
 
319
362
  input_text.sub!(field) do
320
363
  is_argument = ['argument', 'input_field'].include?(field_type)
@@ -325,15 +368,15 @@ module GraphQL
325
368
  end
326
369
 
327
370
  if is_argument
328
- if has_bang
371
+ if nullable
329
372
  f += ', required: false'
330
- else
373
+ elsif non_nullable
331
374
  f += ', required: true'
332
375
  end
333
376
  else
334
- if has_bang
377
+ if nullable
335
378
  f += ', null: true'
336
- else
379
+ elsif non_nullable
337
380
  f += ', null: false'
338
381
  end
339
382
  end
@@ -363,17 +406,29 @@ module GraphQL
363
406
 
364
407
  # Remove redundant newlines, which may have trailing spaces
365
408
  # Remove double newline after `do`
409
+ # Remove double newline before `end`
366
410
  class RemoveExcessWhitespaceTransform < Transform
367
411
  def apply(input_text)
368
412
  input_text
369
- .gsub(/\n{3,}/m, "\n")
370
- .gsub(/do\n\n/m, "do\n")
413
+ .gsub(/\n{3,}/m, "\n\n")
414
+ .gsub(/do\n{2,}/m, "do\n")
415
+ .gsub(/\n{2,}(\s*)end/m, "\n\\1end")
416
+ end
417
+ end
418
+
419
+ # Skip this file if you see any `field`
420
+ # helpers with `null: true` or `null: false` keywords,
421
+ # because it's already been transformed
422
+ class SkipOnNullKeyword
423
+ def skip?(input_text)
424
+ input_text =~ /field.*null: (true|false)/
371
425
  end
372
426
  end
373
427
 
374
428
  class Member
375
- def initialize(member, type_transforms: DEFAULT_TYPE_TRANSFORMS, field_transforms: DEFAULT_FIELD_TRANSFORMS, clean_up_transforms: DEFAULT_CLEAN_UP_TRANSFORMS)
429
+ def initialize(member, skip: SkipOnNullKeyword, type_transforms: DEFAULT_TYPE_TRANSFORMS, field_transforms: DEFAULT_FIELD_TRANSFORMS, clean_up_transforms: DEFAULT_CLEAN_UP_TRANSFORMS)
376
430
  @member = member
431
+ @skip = skip
377
432
  @type_transforms = type_transforms
378
433
  @field_transforms = field_transforms
379
434
  @clean_up_transforms = clean_up_transforms
@@ -390,7 +445,11 @@ module GraphQL
390
445
  PositionalTypeArgTransform,
391
446
  ConfigurationToKwargTransform.new(kwarg: "property"),
392
447
  ConfigurationToKwargTransform.new(kwarg: "description"),
448
+ ConfigurationToKwargTransform.new(kwarg: "deprecation_reason"),
449
+ ConfigurationToKwargTransform.new(kwarg: "hash_key"),
393
450
  PropertyToMethodTransform,
451
+ RemoveRedundantKwargTransform.new(kwarg: "hash_key"),
452
+ RemoveRedundantKwargTransform.new(kwarg: "method"),
394
453
  UnderscoreizeFieldNameTransform,
395
454
  ResolveProcToMethodTransform,
396
455
  UpdateMethodSignatureTransform,
@@ -402,8 +461,14 @@ module GraphQL
402
461
  ]
403
462
 
404
463
  def upgrade
464
+ type_source = @member.dup
465
+ should_skip = @skip.new.skip?(type_source)
466
+ # return the unmodified code
467
+ if should_skip
468
+ return type_source
469
+ end
405
470
  # Transforms on type defn code:
406
- type_source = apply_transforms(@member.dup, @type_transforms)
471
+ type_source = apply_transforms(type_source, @type_transforms)
407
472
  # Transforms on each field:
408
473
  field_sources = find_fields(type_source)
409
474
  field_sources.each do |field_source|
@@ -464,6 +529,10 @@ module GraphQL
464
529
  # - use `gsub` after performing the transformation.
465
530
  field_sources.uniq!
466
531
  field_sources
532
+ rescue Parser::SyntaxError
533
+ puts "Error Source:"
534
+ puts type_source
535
+ raise
467
536
  end
468
537
 
469
538
  class FieldFinder < Parser::AST::Processor
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.8.0.pre3"
3
+ VERSION = "1.8.0.pre4"
4
4
  end
@@ -8,7 +8,7 @@ module Platform
8
8
 
9
9
  scopeless_tokens_as_minimum
10
10
 
11
- # Test multiline interfaces
11
+
12
12
  interfaces [
13
13
  Interfaces::A,
14
14
  Interfaces::B,
@@ -7,7 +7,6 @@ module Platform
7
7
 
8
8
  scopeless_tokens_as_minimum
9
9
 
10
- # Test multiline interfaces
11
10
  implements Interfaces::A
12
11
  implements Interfaces::B
13
12
 
@@ -12,11 +12,14 @@ module Platform
12
12
  interfaces [GraphQL::Relay::Node.interface]
13
13
 
14
14
  field :f1, !Objects::O1, "The x being y."
15
- field :f2, !Enums::E1, "x for the y.", property: :field_2
15
+ field :f2, !Enums::E1, "x for the y.",
16
+ property: :field_2
16
17
  field :f3, Enums::E2, "x for y."
17
18
  field :details, types.String, "Details."
18
19
 
19
20
  field :f4, !Objects::O2, "x as a y inside the z." do
21
+ argument :a1, !Inputs::I1
22
+
20
23
  resolve ->(obj_x, arguments, context) do
21
24
  Class1.new(
22
25
  a: Class2.new(
@@ -30,6 +33,21 @@ module Platform
30
33
  )
31
34
  end
32
35
  end
36
+
37
+ field :f5, -> { !types.String } do
38
+ description "The thing"
39
+ property :custom_property
40
+ visibility :custom_value
41
+ end
42
+
43
+ field :f6, -> { !types.String } do
44
+ description "The thing"
45
+ property :custom_property
46
+ visibility :custom_value
47
+ end
48
+
49
+ field :f7, field: SomeField
50
+ field :f8, function: SomeFunction
33
51
  end
34
52
  end
35
53
  end
@@ -16,9 +16,11 @@ module Platform
16
16
  field :f3, Enums::E2, "x for y.", null: true
17
17
  field :details, String, "Details.", null: true
18
18
 
19
- field :f4, Objects::O2, "x as a y inside the z.", null: false
19
+ field :f4, Objects::O2, "x as a y inside the z.", null: false do
20
+ argument :a1, Inputs::I1, required: true
21
+ end
20
22
 
21
- def f4
23
+ def f4(**arguments)
22
24
  Class1.new(
23
25
  a: Class2.new(
24
26
  b: @object.b_1,
@@ -30,6 +32,13 @@ module Platform
30
32
  )
31
33
  )
32
34
  end
35
+
36
+ field :f5, String, visibility: :custom_value, method: :custom_property, description: "The thing", null: false
37
+
38
+ field :f6, String, visibility: :custom_value, method: :custom_property, description: "The thing", null: false
39
+
40
+ field :f7, field: SomeField
41
+ field :f8, function: SomeFunction
33
42
  end
34
43
  end
35
44
  end
@@ -256,8 +256,9 @@ TABLE
256
256
  }'
257
257
  res = Jazz::Schema.execute(query_str, context: { magic_key: :ignored, normal_key: "normal_value" })
258
258
  expected_values = ["custom_method", "magic_value", "normal_value"]
259
+ expected_values_with_nil = expected_values + [nil]
259
260
  assert_equal expected_values, res["data"]["inspectContext"]
260
- assert_equal expected_values, res["data"]["find"]["inspectContext"]
261
+ assert_equal expected_values_with_nil, res["data"]["find"]["inspectContext"]
261
262
  end
262
263
  end
263
264
  end
@@ -21,7 +21,7 @@ describe GraphQL::Schema::Enum do
21
21
  # values were inherited without modifying the parent
22
22
  assert_equal 6, enum.values.size
23
23
  assert_equal 7, new_enum.values.size
24
- perc_value = new_enum.values.find { |v| v.name == "PERCUSSION" }
24
+ perc_value = new_enum.values["PERCUSSION"]
25
25
  assert_equal "new description", perc_value.description
26
26
  end
27
27
  end
@@ -4,7 +4,7 @@ require "spec_helper"
4
4
  describe GraphQL::Schema::Field do
5
5
  describe "graphql definition" do
6
6
  let(:object_class) { Jazz::Query }
7
- let(:field) { object_class.fields.find { |f| f.name == "inspect_input" } }
7
+ let(:field) { object_class.fields["inspect_input"] }
8
8
 
9
9
  it "uses the argument class" do
10
10
  arg_defn = field.graphql_definition.arguments.values.first
@@ -15,18 +15,18 @@ describe GraphQL::Schema::Field do
15
15
  assert_equal 'inspectInput', field.graphql_definition.name
16
16
  end
17
17
 
18
- describe "description in block" do
19
- it "will raise if description is defined both in the argument and in the block" do
20
- assert_raises RuntimeError, "You're overriding the description of shouldRaise in the provided block!" do
21
- Class.new(Jazz::BaseObject) do
22
- graphql_name "JustAName"
18
+ it "accepts a block for definition" do
19
+ object = Class.new(Jazz::BaseObject) do
20
+ graphql_name "JustAName"
23
21
 
24
- field :should_raise, Jazz::Key, "this should not raise", null: true do
25
- description "This should raise"
26
- end
27
- end.to_graphql
22
+ field :test, String, null: true do
23
+ argument :test, String, required: true
24
+ description "A Description."
28
25
  end
29
- end
26
+ end.to_graphql
27
+
28
+ assert_equal "test", object.fields["test"].arguments["test"].name
29
+ assert_equal "A Description.", object.fields["test"].description
30
30
  end
31
31
  end
32
32
  end
@@ -24,9 +24,9 @@ describe GraphQL::Schema::Object do
24
24
  # inherited interfaces are present
25
25
  assert_equal 2, new_object_class.interfaces.size
26
26
  # The new field is present
27
- assert new_object_class.fields.find { |f| f.name == "newField" }
27
+ assert new_object_class.fields.key?("newField")
28
28
  # The overridden field is present:
29
- name_field = new_object_class.fields.find { |f| f.name == "name" }
29
+ name_field = new_object_class.fields["name"]
30
30
  assert_equal "The new description", name_field.description
31
31
  end
32
32
 
@@ -42,6 +42,23 @@ describe GraphQL::Schema::Object do
42
42
  end
43
43
  end
44
44
 
45
+ describe "wrapping a Hash" do
46
+ it "automatically looks up symbol and string keys" do
47
+ query_str = <<-GRAPHQL
48
+ {
49
+ hashyEnsemble {
50
+ name
51
+ musicians { name }
52
+ }
53
+ }
54
+ GRAPHQL
55
+ res = Jazz::Schema.execute(query_str)
56
+ ensemble = res["data"]["hashyEnsemble"]
57
+ assert_equal "The Grateful Dead", ensemble["name"]
58
+ assert_equal ["Jerry Garcia"], ensemble["musicians"].map { |m| m["name"] }
59
+ end
60
+ end
61
+
45
62
  describe ".to_graphql_type" do
46
63
  let(:obj_type) { Jazz::Ensemble.to_graphql }
47
64
  it "returns a matching GraphQL::ObjectType" do
@@ -17,17 +17,52 @@ describe GraphQL::Upgrader::Member do
17
17
  end
18
18
  end
19
19
 
20
- it 'upgrades the property definition to method' do
21
- old = %{field :name, String, property: :name}
22
- new = %{field :name, String, method: :name, null: true}
20
+ describe "property / method upgrade" do
21
+ it 'upgrades the property definition to method' do
22
+ old = %{field :name, String, property: :full_name}
23
+ new = %{field :name, String, method: :full_name, null: true}
23
24
 
24
- assert_equal new, upgrade(old)
25
+ assert_equal new, upgrade(old)
26
+ end
27
+
28
+ it 'upgrades the property definition in a block to method' do
29
+ old = %{field :name, String do\n property :full_name\nend}
30
+ new = %{field :name, String, method: :full_name, null: true}
31
+ assert_equal new, upgrade(old)
32
+ end
33
+
34
+ it "removes property when redundant" do
35
+ old = %{field :name, String do\n property "name" \nend}
36
+ new = %{field :name, String, null: true}
37
+ assert_equal new, upgrade(old)
38
+
39
+ old = %{field :name, String, property: :name}
40
+ new = %{field :name, String, null: true}
41
+ assert_equal new, upgrade(old)
42
+
43
+ end
25
44
  end
26
45
 
27
- it 'upgrades the property definition in a block to method' do
28
- old = %{field :name, String do\n property :name\nend}
29
- new = %{field :name, String, method: :name, null: true}
30
- assert_equal new, upgrade(old)
46
+ describe "hash_key" do
47
+ it "it moves configuration to kwarg" do
48
+ old = %{field :name, String do\n hash_key :full_name\nend}
49
+ new = %{field :name, String, hash_key: :full_name, null: true}
50
+ assert_equal new, upgrade(old)
51
+ end
52
+
53
+ it "removes it if it's redundant" do
54
+ old = %{field :name, String do\n hash_key :name\nend}
55
+ new = %{field :name, String, null: true}
56
+ assert_equal new, upgrade(old)
57
+
58
+ old = %{field :name, String, hash_key: :name}
59
+ new = %{field :name, String, null: true}
60
+ assert_equal new, upgrade(old)
61
+
62
+ old = %{field :name, String do\n hash_key "name"\nend}
63
+ new = %{field :name, String, null: true}
64
+ assert_equal new, upgrade(old)
65
+ end
31
66
  end
32
67
 
33
68
  describe 'name' do
@@ -224,8 +259,8 @@ describe GraphQL::Upgrader::Member do
224
259
  new = %{field :name, String, null: false}
225
260
  assert_equal new, upgrade(old)
226
261
 
227
- old = %{field :name, !types.String, "description", method: :name}
228
- new = %{field :name, String, "description", method: :name, null: false}
262
+ old = %{field :name, !types.String, "description", method: :name_full}
263
+ new = %{field :name, String, "description", method: :name_full, null: false}
229
264
  assert_equal new, upgrade(old)
230
265
 
231
266
  old = %{field :name, -> { !types.String }}
@@ -347,8 +382,7 @@ describe GraphQL::Upgrader::Member do
347
382
  property: :example_field?
348
383
  }
349
384
  new = %{
350
- field :is_example_field, Boolean, null: true
351
- method: :example_field?
385
+ field :is_example_field, Boolean, method: :example_field?, null: true
352
386
  }
353
387
 
354
388
  assert_equal new, upgrade(old)
@@ -362,8 +396,7 @@ describe GraphQL::Upgrader::Member do
362
396
  property: :example_connections
363
397
  }
364
398
  new = %{
365
- field :example_connection, ExampleConnectionType, null: true, connection: true
366
- method: :example_connections
399
+ field :example_connection, ExampleConnectionType, method: :example_connections, null: true, connection: true
367
400
  }
368
401
 
369
402
  assert_equal new, upgrade(old)
@@ -416,19 +449,26 @@ describe GraphQL::Upgrader::Member do
416
449
  }
417
450
 
418
451
  type_transforms.unshift(ActiveRecordTypeToClassTransform)
419
- upgrader = GraphQL::Upgrader::Member.new(original_text, type_transforms: type_transforms)
452
+ field_transforms = GraphQL::Upgrader::Member::DEFAULT_FIELD_TRANSFORMS
453
+ field_transforms.unshift(GraphQL::Upgrader::ConfigurationToKwargTransform.new(kwarg: "visibility"))
454
+ upgrader = GraphQL::Upgrader::Member.new(original_text, type_transforms: type_transforms, field_transforms: field_transforms)
420
455
  upgrader.upgrade
421
456
  end
422
457
 
423
458
  original_files = Dir.glob("spec/fixtures/upgrader/*.original.rb")
424
459
  original_files.each do |original_file|
425
460
  transformed_file = original_file.sub(".original.", ".transformed.")
461
+ original_text = File.read(original_file)
462
+ expected_text = File.read(transformed_file)
426
463
  it "transforms #{original_file} -> #{transformed_file}" do
427
- original_text = File.read(original_file)
428
- expected_text = File.read(transformed_file)
429
464
  transformed_text = custom_upgrade(original_text)
430
465
  assert_equal(expected_text, transformed_text)
431
466
  end
467
+
468
+ it "is idempotent on #{transformed_file}" do
469
+ retransformed_text = custom_upgrade(expected_text)
470
+ assert_equal(expected_text, retransformed_text)
471
+ end
432
472
  end
433
473
  end
434
474
  end
data/spec/support/jazz.rb CHANGED
@@ -201,13 +201,15 @@ module Jazz
201
201
  description "An object played in order to produce music"
202
202
  end
203
203
  field :favorite_key, Key, null: true
204
- field :inspect_context, [String], null: false
204
+ # Test lists with nullable members:
205
+ field :inspect_context, [String, null: true], null: false
205
206
 
206
207
  def inspect_context
207
208
  [
208
209
  @context.custom_method,
209
210
  @context[:magic_key],
210
- @context[:normal_key]
211
+ @context[:normal_key],
212
+ nil,
211
213
  ]
212
214
  end
213
215
  end
@@ -272,6 +274,7 @@ module Jazz
272
274
  # For asserting that the object is initialized once:
273
275
  field :object_id, Integer, null: false
274
276
  field :inspect_context, [String], null: false
277
+ field :hashyEnsemble, Ensemble, null: false
275
278
 
276
279
  def ensembles
277
280
  Models.data["Ensemble"]
@@ -317,6 +320,17 @@ module Jazz
317
320
  @context[:normal_key]
318
321
  ]
319
322
  end
323
+
324
+ def hashy_ensemble
325
+ # Both string and symbol keys are supported:
326
+
327
+ {
328
+ name: "The Grateful Dead",
329
+ "musicians" => [
330
+ OpenStruct.new(name: "Jerry Garcia"),
331
+ ],
332
+ }
333
+ end
320
334
  end
321
335
 
322
336
  class EnsembleInput < GraphQL::Schema::InputObject
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.0.pre3
4
+ version: 1.8.0.pre4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-12 00:00:00.000000000 Z
11
+ date: 2018-01-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: benchmark-ips