graphql_migrate_execution 0.0.2 → 1.0.1

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 (30) hide show
  1. checksums.yaml +4 -4
  2. data/bin/graphql_migrate_execution +5 -6
  3. data/lib/graphql_migrate_execution/action.rb +33 -24
  4. data/lib/graphql_migrate_execution/dataloader_all.rb +39 -14
  5. data/lib/graphql_migrate_execution/dataloader_batch.rb +66 -4
  6. data/lib/graphql_migrate_execution/dataloader_manual.rb +2 -5
  7. data/lib/graphql_migrate_execution/dataloader_shorthand.rb +20 -7
  8. data/lib/graphql_migrate_execution/do_nothing.rb +1 -1
  9. data/lib/graphql_migrate_execution/fallback_value.rb +39 -0
  10. data/lib/graphql_migrate_execution/field_definition.rb +32 -4
  11. data/lib/graphql_migrate_execution/hash_key.rb +12 -0
  12. data/lib/graphql_migrate_execution/implicit.rb +22 -6
  13. data/lib/graphql_migrate_execution/migration.rb +45 -0
  14. data/lib/graphql_migrate_execution/not_implemented.rb +1 -1
  15. data/lib/graphql_migrate_execution/resolve_batch.rb +16 -0
  16. data/lib/graphql_migrate_execution/resolve_each.rb +7 -7
  17. data/lib/graphql_migrate_execution/resolve_static.rb +8 -7
  18. data/lib/graphql_migrate_execution/resolver_method.rb +80 -5
  19. data/lib/graphql_migrate_execution/strategy.rb +111 -25
  20. data/lib/graphql_migrate_execution/type_definition.rb +14 -1
  21. data/lib/graphql_migrate_execution/unsupported_current_path.rb +8 -0
  22. data/lib/graphql_migrate_execution/unsupported_extra.rb +8 -0
  23. data/lib/graphql_migrate_execution/version.rb +1 -1
  24. data/lib/graphql_migrate_execution/visitor.rb +54 -18
  25. data/lib/graphql_migrate_execution.rb +7 -30
  26. data/readme.md +158 -4
  27. metadata +8 -5
  28. data/lib/graphql_migrate_execution/add_future.rb +0 -9
  29. data/lib/graphql_migrate_execution/analyze.rb +0 -30
  30. data/lib/graphql_migrate_execution/remove_legacy.rb +0 -9
@@ -15,13 +15,19 @@ module GraphqlMigrateExecution
15
15
  @calls_class = false
16
16
  @calls_dataloader = false
17
17
  @dataloader_call = false
18
+ @uses_current_path = false
19
+ @dataload_association = nil
20
+ @dataload_record = nil
21
+ @dataload_record_using = nil
22
+ @dataload_record_find_by = nil
23
+ @return_expressions = nil
18
24
  end
19
25
 
20
26
  attr_reader :name, :node, :parameter_names, :self_sends
21
27
 
22
- attr_reader :source_class_node, :source_arg_nodes, :load_arg_node, :dataload_association
28
+ attr_reader :source_class_node, :source_arg_nodes, :load_arg_node, :dataload_association, :dataload_record, :dataload_record_using, :dataload_record_find_by
23
29
 
24
- attr_accessor :calls_object, :calls_context, :calls_class, :calls_dataloader
30
+ attr_accessor :calls_object, :calls_context, :calls_class, :calls_dataloader, :uses_current_path
25
31
 
26
32
  attr_accessor :dataloader_call
27
33
 
@@ -42,6 +48,7 @@ module GraphqlMigrateExecution
42
48
  calls_to_self.delete(:dataload_association)
43
49
  calls_to_self.delete(:dataload_record)
44
50
  calls_to_self.delete(:dataload)
51
+ calls_to_self.delete(:dataload_all)
45
52
 
46
53
  # Global-ish methods:
47
54
  calls_to_self.delete(:raise)
@@ -67,6 +74,22 @@ module GraphqlMigrateExecution
67
74
  assoc_sym = assoc_arg.unescaped.to_sym
68
75
  @dataload_association = assoc_sym == name ? true : assoc_sym
69
76
  end
77
+ when :dataload_record
78
+ if (record_args = call_node.arguments.arguments) &&
79
+ (record_arg = record_args.first) &&
80
+ (record_arg.is_a?(Prism::ConstantReadNode) || record_arg.is_a?(Prism::ConstantPathNode)) &&
81
+ (using_arg = record_args[1]) &&
82
+ # Must be `object.{something}`
83
+ (using_arg.is_a?(Prism::CallNode)) &&
84
+ (using_arg.receiver.is_a?(Prism::CallNode) && using_arg.receiver.name == :object)
85
+ @dataload_record = record_arg.full_name
86
+ @dataload_record_using = using_arg.name
87
+
88
+ if (kwargs = record_args.last).is_a?(Prism::KeywordHashNode) && (find_by_kwarg = kwargs.elements.find { |el| el.key.is_a?(Prism::SymbolNode) && el.key.unescaped == "find_by" })
89
+ find_by_node = find_by_kwarg.value
90
+ @dataload_record_find_by = find_by_node.unescaped.to_sym # Assumes a SymbolNode
91
+ end
92
+ end
70
93
  else
71
94
  if (source_call = call_node.receiver) # eg dataloader.with(...).load(...)
72
95
  @source_class_node = source_call.arguments.arguments.first
@@ -78,15 +101,16 @@ module GraphqlMigrateExecution
78
101
  input_is_object = @load_arg_node.is_a?(Prism::CallNode) && @load_arg_node.name == :object
79
102
  # Guess whether these args are free of runtime context:
80
103
  shortcutable_source_args = @source_arg_nodes && (@source_arg_nodes.empty? || (@source_arg_nodes.all? { |a| Visitor.constant_node?(a) }))
81
- if @source_class_node.is_a?(Prism::ConstantPathNode) && shortcutable_source_args && input_is_object
104
+ source_ref_is_constant = @source_class_node.is_a?(Prism::ConstantPathNode) || @source_class_node.is_a?(Prism::ConstantReadNode)
105
+ if source_ref_is_constant && shortcutable_source_args && input_is_object
82
106
  DataloaderShorthand
83
107
  else
84
108
  case call_node.name
85
109
  when :load, :request, :dataload
86
110
  DataloaderAll
87
- when :load_all, :request_all, :dataload_record
111
+ when :load_all, :request_all, :dataload_all
88
112
  DataloaderBatch
89
- when :dataload_association
113
+ when :dataload_association, :dataload_record
90
114
  DataloaderShorthand
91
115
  else
92
116
  DataloaderManual
@@ -101,5 +125,56 @@ module GraphqlMigrateExecution
101
125
  NotImplemented
102
126
  end
103
127
  end
128
+
129
+ def returns_hash?
130
+ return_expressions.all? { |exp_node| exp_node.is_a?(Prism::HashNode) || (exp_node.is_a?(Prism::CallNode) && exp_node.name == :new && exp_node.receiver.is_a?(Prism::ConstantReadNode) && exp_node.receiver.name == :Hash) }
131
+ end
132
+
133
+ def return_expressions
134
+ if @return_expressions.nil?
135
+ @return_expressions = []
136
+ find_return_expressions(@node)
137
+ end
138
+ @return_expressions
139
+ end
140
+
141
+ def returns_string_hash?
142
+ return_expressions.any? { |exp_node| exp_node.is_a?(Prism::HashNode) && exp_node.elements.size > 0 && exp_node.elements.all? { |el| el.key.is_a?(Prism::StringNode) } }
143
+ end
144
+
145
+ private
146
+
147
+ def find_return_expressions(node)
148
+ case node
149
+ when Prism::DefNode
150
+ find_return_expressions(node.body)
151
+ when Prism::StatementsNode
152
+ find_return_expressions(node.body.last)
153
+ when Prism::IfNode # TODO else, `?`, case
154
+ find_return_expressions(node.statements)
155
+ find_return_expressions(node.subsequent)
156
+ when Prism::ElseNode, Prism::WhenNode
157
+ find_return_expressions(node.statements)
158
+ when Prism::UnlessNode
159
+ find_return_expressions(node.statements)
160
+ find_return_expressions(node.else_clause)
161
+ when Prism::CaseNode
162
+ node.conditions.each do |cond_node|
163
+ find_return_expressions(cond_node)
164
+ end
165
+ when Prism::ReturnNode
166
+ find_return_expressions(node.arguments.first)
167
+ when Prism::LocalVariableReadNode
168
+ if (lv_write_node = @node.body.body.find { |n| n.is_a?(Prism::LocalVariableWriteNode) && n.name == node.name })
169
+ find_return_expressions(lv_write_node.value)
170
+ else
171
+ # Couldn't find assignment :'(
172
+ @return_expressions << node
173
+ end
174
+ else
175
+ # This is an expression that produces a return value
176
+ @return_expressions << node
177
+ end
178
+ end
104
179
  end
105
180
  end
@@ -1,66 +1,152 @@
1
1
  # frozen_string_literal: true
2
+ require_relative "./action"
3
+
2
4
  module GraphqlMigrateExecution
3
5
  class Strategy
4
- def add_future(field_definition, new_source)
6
+ include Action::Colorize
7
+ def initialize(action, field_definitions)
8
+ @action = action
9
+ @migration = action.migration
10
+ @filepath = action.filepath
11
+ @action_method = @migration.action_method
12
+ @message = action.message
13
+ @result_source = action.result_source
14
+ @field_definitions = field_definitions
15
+ end
16
+
17
+ def migrate(field_definition)
18
+ end
19
+
20
+ def cleanup(field_definition)
5
21
  end
6
22
 
7
- def remove_legacy(field_definition, new_source)
23
+ def run
24
+ case @action_method
25
+ when :analyze
26
+ @message << "\n#{colorize("#{colorize(self.class.strategy_name, self.class.color)} (#{@field_definitions.size})", :BOLD)}:\n"
27
+ max_path = @field_definitions.map { |f| f.path.size }.max + 2
28
+ @field_definitions.each do |field_defn|
29
+ name = field_defn.path.ljust(max_path)
30
+ @message << "\n - #{name} (#{field_defn.resolve_mode.inspect} -> #{field_defn.resolve_mode_key.inspect}) @ #{@filepath}:#{field_defn.source_line}"
31
+ end
32
+ @message << "\n"
33
+ when :migrate, :cleanup
34
+ indent_size = @action.strategy_name_padding + 1
35
+ indent = " " * indent_size
36
+ indent2_size = @action.field_name_padding
37
+ @message << "\n#{colorize(self.class.strategy_name.ljust(indent_size), self.class.color)}"
38
+ first = true
39
+ @field_definitions.each do |field_defn|
40
+ @message << "#{first ? "" : "#{indent}"}#{field_defn.path.ljust(indent2_size)} @ #{@filepath}:#{field_defn.source_line}\n"
41
+ first = false
42
+ public_send(@action_method, field_defn)
43
+ end
44
+ end
45
+ end
46
+
47
+ def self.strategy_name
48
+ name.split("::").last
8
49
  end
9
50
 
10
51
  class << self
11
52
  attr_accessor :color
12
53
  end
13
54
 
55
+ # These methods conflict with built-in class methods,
56
+ # so when migrating them, prefix them.
57
+ METHODS_TO_RENAME = [
58
+ "name",
59
+ "fields",
60
+ "arguments",
61
+ ]
62
+
63
+ def self.prefix_if_necessary(method_name)
64
+ name_s = method_name.to_s
65
+ if METHODS_TO_RENAME.include?(name_s)
66
+ "resolve_#{method_name}"
67
+ else
68
+ name_s
69
+ end
70
+ end
71
+
14
72
  private
15
73
 
16
- def inject_resolve_keyword(new_source, field_definition, keyword)
74
+ def inject_resolve_keyword(field_definition, keyword)
17
75
  value = field_definition.future_resolve_shorthand.inspect
18
- inject_field_keyword(new_source, field_definition, keyword, value)
76
+ inject_field_keyword(field_definition, keyword, value)
19
77
  end
20
78
 
21
- def inject_field_keyword(new_source, field_definition, keyword, value)
79
+ def inject_field_keyword(field_definition, keyword, value)
22
80
  field_definition_source = field_definition.source
23
- new_definition_source = if field_definition_source[/ [a-z_]+:/] # Does it already have keywords?
24
- field_definition_source.sub(/(field.+?)((?: do)|(?: {)|$)/, "\\1, #{keyword}: #{value}\\2")
81
+ pair = "#{keyword}: #{value}"
82
+ if field_definition_source.include?(pair)
83
+ # Pass, don't re-add it
84
+ elsif field_definition_source.include?("#{keyword}:")
85
+ warn "Can't re-inject #{keyword} because it's already present in the definition:\n\n#{field_definition_source}"
25
86
  else
26
- field_definition_source + ", #{keyword}: #{value}"
87
+ new_definition_source = if field_definition_source[/ [a-z_]+:/] # Does it already have keywords?
88
+ field_definition_source.sub(/(field.+?)((?:,$)|(?: do)|(?: {)|$)/, "\\1, #{pair}\\2")
89
+ elsif (block_node = field_definition.node.block)
90
+ block_source = block_node.location.slice
91
+ field_definition_source.sub(" " + block_source, ", #{pair} #{block_source}")
92
+ else
93
+ field_definition_source + ", #{pair}"
94
+ end
95
+ @result_source.sub!(field_definition_source, new_definition_source)
27
96
  end
28
- new_source.sub!(field_definition_source, new_definition_source)
29
97
  end
30
98
 
31
99
 
32
- def remove_field_keyword(new_source, field_definition, keyword)
100
+ def remove_field_keyword(field_definition, keyword)
33
101
  field_definition_source = field_definition.source
34
102
  new_definition_source = field_definition_source.sub(/, #{keyword}: \S+(,|$)/, "\\1")
35
- new_source.sub!(field_definition_source, new_definition_source)
103
+ @result_source.sub!(field_definition_source, new_definition_source)
36
104
  end
37
105
 
38
- def replace_resolver_method(new_source, field_definition, new_params)
106
+ def replace_resolver_method(field_definition, new_params)
39
107
  resolver_method = field_definition.resolver_method
40
- method_name = resolver_method.name
108
+ old_method_name = resolver_method.name.to_s
109
+ new_method_name = self.class.prefix_if_necessary(old_method_name)
41
110
  old_method = resolver_method.source
42
- new_class_method = old_method
43
- .sub("def ", 'def self.')
111
+
112
+ method_prefix = field_definition.type_definition.is_interface ? "def #{new_method_name}" : "def self.#{new_method_name}"
113
+ new_class_method = old_method.sub("def #{old_method_name}", method_prefix)
44
114
 
45
115
  if resolver_method.parameter_names.empty?
46
- new_class_method.sub!(method_name.to_s, "#{method_name}(#{new_params})")
116
+ new_class_method.sub!(new_method_name, "#{new_method_name}(#{new_params})")
47
117
  else
48
- new_class_method.sub!("def self.#{method_name}(", "def self.#{method_name}(#{new_params}, ")
118
+ new_class_method.sub!("#{method_prefix}(", "#{method_prefix}(#{new_params}, ")
119
+ end
120
+
121
+ if field_definition.type_definition.is_interface
122
+ indent = new_class_method[/\A +/]
123
+ new_class_method.gsub!(/^#{indent}/, "#{indent} ")
124
+ new_class_method = "#{indent}resolver_methods do\n#{new_class_method}#{indent}end\n"
49
125
  end
50
126
 
51
127
  old_lines = old_method.split("\n")
52
- new_body = old_lines.first[/^ +/] + " self.class.#{method_name}(#{new_params}#{resolver_method.parameter_names.map { |n| ", #{n}: #{n}"}.join})"
53
- new_inst_method = [old_lines.first, new_body, old_lines.last].join("\n")
128
+ forward_previous_params = resolver_method.parameter_names.map { |n| ", #{n}: #{n}"}.join
129
+ if old_lines.size == 1
130
+ # TODO doesn't support `def ... =` syntax
131
+ previous_def = /def [^;]+;/.match(old_method)
132
+ new_inst_method = old_lines.first[/^ +/] + "#{previous_def} self.class.#{new_method_name}(#{new_params}#{forward_previous_params}); end"
133
+ else
134
+ new_body = old_lines.first[/^ +/] + " self.class.#{new_method_name}(#{new_params}#{forward_previous_params})"
135
+ new_inst_method = [old_lines.first, new_body, old_lines.last].join("\n")
136
+ end
54
137
 
55
138
  new_double_definition = new_class_method + "\n" + new_inst_method + "\n"
56
- new_source.sub!(old_method, new_double_definition)
139
+ @result_source.sub!(old_method, new_double_definition)
57
140
  end
58
141
 
59
- def remove_resolver_method(new_source, field_definition)
60
- src_pattern = /(\n*)(#{Regexp.quote(field_definition.resolver_method.source)})(\n*)/
61
- new_source.sub!(src_pattern) do
62
- # $2 includes a newline, too
63
- "#{$1.length > 1 ? "\n" : ""}#{$3.length > 0 ? "\n" : ""}"
142
+
143
+ def remove_resolver_method(field_definition)
144
+ if field_definition.resolver_method
145
+ src_pattern = /(\n*)(#{Regexp.quote(field_definition.resolver_method.source)})(\n*)/
146
+ @result_source.sub!(src_pattern) do
147
+ # $2 includes a newline, too
148
+ "#{$1.length > 1 ? "\n" : ""}#{$3.length > 0 ? "\n" : ""}"
149
+ end
64
150
  end
65
151
  end
66
152
  end
@@ -1,12 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphqlMigrateExecution
3
3
  class TypeDefinition
4
- def initialize(name)
4
+ def initialize(name, migration)
5
5
  @name = name
6
+ @migration = migration
6
7
  @field_definitions = {}
7
8
  @resolver_methods = {}
9
+ @is_resolver = false
10
+ @is_interface
8
11
  end
9
12
 
13
+ attr_accessor :is_resolver, :migration, :is_interface
14
+
10
15
  attr_reader :resolver_methods, :name, :field_definitions
11
16
 
12
17
  def field_definition(name, node)
@@ -16,5 +21,13 @@ module GraphqlMigrateExecution
16
21
  def resolver_method(name, node)
17
22
  @resolver_methods[name] = ResolverMethod.new(name, node)
18
23
  end
24
+
25
+ def returns_hash?
26
+ @resolver_methods.each_value.first.returns_hash?
27
+ end
28
+
29
+ def returns_string_hash?
30
+ @resolver_methods.each_value.first.returns_string_hash?
31
+ end
19
32
  end
20
33
  end
@@ -0,0 +1,8 @@
1
+ module GraphqlMigrateExecution
2
+ # These use `context[:current_path]` or `context.current_path` which isn't supported. Refactor these fields then try migrating again.
3
+ #
4
+ # Open an issue on GraphQL-Ruby's GitHub repo to discuss further.
5
+ class UnsupportedCurrentPath < Strategy
6
+ self.color = :RED
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module GraphqlMigrateExecution
2
+ # These use a field `extra` which isn't supported. Remove this configuration and refactor the field, then try migrating again.
3
+ #
4
+ # (Currently, only `:ast_node` and `:lookahead` are currently supported. Please open an issue on GraphQL-Ruby if this is a problem for you.)
5
+ class UnsupportedExtra < Strategy
6
+ self.color = :RED
7
+ end
8
+ end
@@ -1,3 +1,3 @@
1
1
  module GraphqlMigrateExecution
2
- VERSION = "0.0.2"
2
+ VERSION = "1.0.1"
3
3
  end
@@ -7,6 +7,7 @@ module GraphqlMigrateExecution
7
7
  @type_definition_stack = []
8
8
  @current_field_definition = nil
9
9
  @current_resolver_method = nil
10
+ @is_public = true
10
11
  end
11
12
 
12
13
  def visit_class_node(node)
@@ -23,6 +24,7 @@ module GraphqlMigrateExecution
23
24
 
24
25
  def visit_module_node(node)
25
26
  td = @type_definitions[node.name]
27
+ td.is_interface = true
26
28
  @type_definition_stack << td
27
29
  super
28
30
  ensure
@@ -37,7 +39,7 @@ module GraphqlMigrateExecution
37
39
  when "hash_key"
38
40
  @current_field_definition.resolve_mode ||= :hash_key
39
41
  @current_field_definition.hash_key = get_keyword_value(assoc.value)
40
- when "resolver"
42
+ when "resolver", "mutation", "subscription"
41
43
  @current_field_definition.resolve_mode ||= :resolver
42
44
  @current_field_definition.resolver = get_keyword_value(assoc.value)
43
45
  when "method"
@@ -49,10 +51,15 @@ module GraphqlMigrateExecution
49
51
  when "dig"
50
52
  @current_field_definition.resolve_mode ||= :dig
51
53
  @current_field_definition.dig = get_keyword_value(assoc.value)
52
- when "resolve_each", "resolve_static", "resolve_batch"
54
+ when "resolve_each", "resolve_static", "resolve_batch", "resolve_legacy_instance_method"
53
55
  # These should override any other keywords that are discovered
54
56
  @current_field_definition.resolve_mode = :already_migrated
55
57
  @current_field_definition.already_migrated = { assoc.key.unescaped.to_sym => get_keyword_value(assoc.value) }
58
+ when "fallback_value"
59
+ if !assoc.value.is_a?(Prism::NilNode)
60
+ @current_field_definition.resolve_mode ||= :fallback_value
61
+ @current_field_definition.fallback_value = self.class.source_for_constant_node(assoc.value)
62
+ end
56
63
  else
57
64
  # fallback_value, connection, extensions, extras, resolver, mutation, subscription
58
65
  @current_field_definition.unknown_options << assoc.key.unescaped
@@ -64,15 +71,18 @@ module GraphqlMigrateExecution
64
71
  end
65
72
 
66
73
  def visit_call_node(node)
67
- if node.receiver.nil? && node.name == :field
74
+ if node.receiver.nil? && node.name == :field && node.arguments
68
75
  first_arg = node.arguments.arguments.first # rubocop:disable Development/ContextIsPassedCop
69
- if first_arg.is_a?(Prism::SymbolNode)
76
+ if first_arg.is_a?(Prism::SymbolNode) && (td = @type_definition_stack.last)
70
77
  field_name = first_arg.unescaped
71
- td = @type_definition_stack.last
72
78
  @current_field_definition = td.field_definition(field_name, node)
73
79
  else
74
80
  warn "GraphQL-Ruby warning: Skipping unrecognized field definition: #{node.inspect}"
75
81
  end
82
+ elsif node.receiver.nil? && ((node.name == :private) || (node.name == :protected))
83
+ @is_public = false
84
+ elsif node.receiver.nil? && node.name == :public
85
+ @is_public = true
76
86
  elsif @current_resolver_method
77
87
  if node.receiver.nil? || node.receiver.is_a?(Prism::SelfNode)
78
88
  @current_resolver_method.self_sends.add(node.name)
@@ -89,6 +99,15 @@ module GraphqlMigrateExecution
89
99
  case node.name
90
100
  when :dataloader, :dataload, :dataload_association, :dataload_record, :dataload_all
91
101
  @current_resolver_method.calls_dataloader = true
102
+ when :current_path
103
+ if node.receiver.is_a?(Prism::CallNode) && node.receiver.name == :context
104
+ @current_resolver_method.uses_current_path = true
105
+ end
106
+ when :[]
107
+ if node.receiver.is_a?(Prism::CallNode) && node.receiver.name == :context &&
108
+ (arg = node.arguments.arguments.first).is_a?(Prism::SymbolNode) && (arg.unescaped == "current_path")
109
+ @current_resolver_method.uses_current_path = true
110
+ end
92
111
  end
93
112
  end
94
113
  super
@@ -99,22 +118,39 @@ module GraphqlMigrateExecution
99
118
  end
100
119
 
101
120
  def visit_def_node(node)
102
- if node.receiver.nil?
103
- td = @type_definition_stack.last
104
- @current_resolver_method = td.resolver_method(node.name, node)
105
- end
121
+ if @is_public && (td = @type_definition_stack.last)
122
+ if node.receiver.nil?
123
+ @current_resolver_method = td.resolver_method(node.name, node)
124
+ end
125
+
126
+ if node.name == :resolve && td.resolver_methods.size == 1
127
+ td.is_resolver = true
128
+ elsif td.is_resolver && td.resolver_methods.size > 1
129
+ td.is_resolver = false
130
+ end
106
131
 
107
- body = node.body.body
108
- if body.length == 1 && (call_node = body.first).is_a?(Prism::CallNode)
109
- case call_node.name
110
- when :load, :request, :load_all, :request_all
111
- if (call_node2 = call_node.receiver).is_a?(Prism::CallNode) && call_node2.name == :with
132
+ body = case node.body
133
+ when Prism::StatementsNode
134
+ node.body.body
135
+ when Prism::BeginNode
136
+ node.body.statements.body
137
+ when nil
138
+ nil
139
+ else
140
+ raise ArgumentError, "Unexpected DefNode body for `def #{node.name}`: #{node.body.class}"
141
+ end
142
+
143
+ if body && @current_resolver_method && body.length == 1 && (call_node = body.first).is_a?(Prism::CallNode)
144
+ case call_node.name
145
+ when :load, :request, :load_all, :request_all
146
+ if (call_node2 = call_node.receiver).is_a?(Prism::CallNode) && call_node2.name == :with
147
+ @current_resolver_method.dataloader_call = true
148
+ end
149
+ when :dataload_record, :dataload_association, :dataload, :dataload_all
112
150
  @current_resolver_method.dataloader_call = true
151
+ else
152
+ # not a single dataloader call
113
153
  end
114
- when :dataload_record, :dataload_association, :dataload
115
- @current_resolver_method.dataloader_call = true
116
- else
117
- # not a single dataloader call
118
154
  end
119
155
  end
120
156
  super
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
+ require "prism"
2
3
  require "graphql_migrate_execution/action"
3
- require "graphql_migrate_execution/add_future"
4
- require "graphql_migrate_execution/remove_legacy"
5
- require "graphql_migrate_execution/analyze"
4
+ require "graphql_migrate_execution/migration"
6
5
  require "graphql_migrate_execution/field_definition"
7
6
  require "graphql_migrate_execution/resolver_method"
8
7
  require "graphql_migrate_execution/type_definition"
@@ -10,6 +9,8 @@ require "graphql_migrate_execution/visitor"
10
9
  require "graphql_migrate_execution/strategy"
11
10
  require "graphql_migrate_execution/implicit"
12
11
  require "graphql_migrate_execution/do_nothing"
12
+ require "graphql_migrate_execution/fallback_value"
13
+ require "graphql_migrate_execution/resolve_batch"
13
14
  require "graphql_migrate_execution/resolve_each"
14
15
  require "graphql_migrate_execution/resolve_static"
15
16
  require "graphql_migrate_execution/not_implemented"
@@ -17,35 +18,11 @@ require "graphql_migrate_execution/dataloader_all"
17
18
  require "graphql_migrate_execution/dataloader_batch"
18
19
  require "graphql_migrate_execution/dataloader_manual"
19
20
  require "graphql_migrate_execution/dataloader_shorthand"
21
+ require "graphql_migrate_execution/hash_key"
22
+ require "graphql_migrate_execution/unsupported_extra"
23
+ require "graphql_migrate_execution/unsupported_current_path"
20
24
  require "graphql_migrate_execution/not_implemented"
21
25
  require "irb"
22
26
 
23
27
  module GraphqlMigrateExecution
24
- class Migration
25
- def initialize(glob, concise: false, migrate: false, cleanup: false, only: nil, implicit: nil, colorable: IRB::Color.colorable?)
26
- @glob = glob
27
- @skip_description = concise
28
- @colorable = colorable
29
- @only = only
30
- @implicit = implicit
31
- @action_class = if migrate
32
- AddFuture
33
- elsif cleanup
34
- RemoveLegacy
35
- else
36
- Analyze
37
- end
38
- end
39
-
40
- attr_reader :skip_description, :colorable
41
-
42
-
43
- def run
44
- Dir.glob(@glob).each do |filepath|
45
- source = File.read(filepath)
46
- file_migrate = @action_class.new(self, filepath, source)
47
- puts file_migrate.run
48
- end
49
- end
50
- end
51
28
  end