Class: GraphQL::Upgrader::ResolveProcToMethodTransform

Inherits:
Transform
  • Object
show all
Defined in:
lib/graphql/upgrader/member.rb

Defined Under Namespace

Classes: ResolveProcProcessor

Instance Method Summary collapse

Methods inherited from Transform

#apply_processor, #normalize_type_expression, #reindent_lines, #trim_lines, #underscorize

Instance Method Details

#apply(input_text) ⇒ Object


513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
# File 'lib/graphql/upgrader/member.rb', line 513

def apply(input_text)
  if input_text =~ /resolve\(? ?->/
    # - Find the proc literal
    # - Get the three argument names (obj, arg, ctx)
    # - Get the proc body
    # - Find and replace:
    #  - The ctx argument becomes `context`
    #  - The obj argument becomes `object`
    # - Args is trickier:
    #   - If it's not used, remove it
    #   - If it's used, abandon ship and make it `**args`
    #   - Convert string args access to symbol access, since it's a Ruby **splat
    #   - Convert camelized arg names to underscored arg names
    #   - (It would be nice to correctly become Ruby kwargs, but that might be too hard)
    #   - Add a `# TODO` comment to the method source?
    # - Rebuild the method:
    #   - use the field name as the method name
    #   - handle args as described above
    #   - put the modified proc body as the method body

    input_text.match(/(?<field_type>input_field|field|connection|argument) :(?<name>[a-zA-Z_0-9_]*)/)
    field_name = $~[:name]
    processor = apply_processor(input_text, ResolveProcProcessor.new)

    processor.resolve_proc_sections.reverse.each do |resolve_proc_section|
      proc_body = input_text[resolve_proc_section.proc_start..resolve_proc_section.proc_end]
      obj_arg_name, args_arg_name, ctx_arg_name = resolve_proc_section.proc_arg_names
      # This is not good, it will hit false positives
      # Should use AST to make this substitution
      if obj_arg_name != "_"
        proc_body.gsub!(/([^\w:.]|^)#{obj_arg_name}([^\w:]|$)/, '\1object\2')
      end
      if ctx_arg_name != "_"
        proc_body.gsub!(/([^\w:.]|^)#{ctx_arg_name}([^\w:]|$)/, '\1context\2')
      end

      method_def_indent = " " * (resolve_proc_section.resolve_indent - 2)
      # Turn the proc body into a method body
      method_body = reindent_lines(proc_body, from_indent: resolve_proc_section.resolve_indent + 2, to_indent: resolve_proc_section.resolve_indent)
      # Add `def... end`
      method_def = if input_text.include?("argument ")
        # This field has arguments
        "def #{field_name}(**#{args_arg_name})"
      else
        # No field arguments, so, no method arguments
        "def #{field_name}"
      end
      # Wrap the body in def ... end
      method_body = "\n#{method_def_indent}#{method_def}\n#{method_body}\n#{method_def_indent}end\n"
      # Update Argument access to be underscore and symbols
      # Update `args[...]` and `args.key?`
      method_body = method_body.gsub(/#{args_arg_name}(?<method_begin>\.key\?\(?|\[)["':](?<arg_name>[a-zA-Z0-9_]+)["']?(?<method_end>\]|\))?/) do
        method_begin = $~[:method_begin]
        arg_name = underscorize($~[:arg_name])
        method_end = $~[:method_end]
        "#{args_arg_name}#{method_begin}:#{arg_name}#{method_end}"
      end

      # Replace the resolve proc with the method
      input_text[resolve_proc_section.resolve_start..resolve_proc_section.resolve_end] = ""
      # The replacement above might have left some preceeding whitespace,
      # so remove it by deleting all whitespace chars before `resolve`:
      preceeding_whitespace = resolve_proc_section.resolve_start - 1
      while input_text[preceeding_whitespace] == " " && preceeding_whitespace > 0
        input_text[preceeding_whitespace] = ""
        preceeding_whitespace -= 1
      end
      input_text += method_body
      input_text
    end
  end

  input_text
end