lib/node_mutation.rb in node_mutation-1.22.2 vs lib/node_mutation.rb in node_mutation-1.22.3
- old
+ new
@@ -5,10 +5,11 @@
class NodeMutation
class MethodNotSupported < StandardError; end
class ConflictActionError < StandardError; end
class InvalidAdapterError < StandardError; end
+ autoload :Actionable, "node_mutation/actionable"
autoload :Adapter, "node_mutation/adapter"
autoload :ParserAdapter, "node_mutation/adapter/parser"
autoload :SyntaxTreeAdapter, "node_mutation/adapter/syntax_tree"
autoload :Action, 'node_mutation/action'
autoload :AppendAction, 'node_mutation/action/append_action'
@@ -24,10 +25,12 @@
autoload :Result, 'node_mutation/result'
autoload :Strategy, 'node_mutation/strategy'
autoload :Struct, 'node_mutation/struct'
autoload :Helper, 'node_mutation/helper'
+ include Actionable
+
# @!attribute [r] actions
# @return [Array<NodeMutation::Struct::Action>]
attr_reader :actions, :adapter
# @!attribute [rw] transform_proc
@@ -69,186 +72,10 @@
@source = source
@actions = []
@adapter = get_adapter_instance(adapter)
end
- # Append code to the ast node.
- # @param node [Node] ast node
- # @param code [String] new code to append
- # @example
- # source code of the ast node is
- # def teardown
- # clean_something
- # end
- # then we call
- # mutation.append(node, 'super')
- # the source code will be rewritten to
- # def teardown
- # clean_something
- # super
- # end
- def append(node, code)
- @actions << AppendAction.new(node, code, adapter: @adapter).process
- end
-
- # Delete source code of the child ast node.
- # @param node [Node] ast node
- # @param selectors [Array<Symbol>] selector names of child node.
- # @param and_comma [Boolean] delete extra comma.
- # @example
- # source code of the ast node is
- # FactoryBot.create(...)
- # then we call
- # mutation.delete(node, :receiver, :dot)
- # the source code will be rewritten to
- # create(...)
- def delete(node, *selectors, and_comma: false)
- @actions << DeleteAction.new(node, *selectors, and_comma: and_comma, adapter: @adapter).process
- end
-
- # Insert code to the ast node.
- # @param node [Node] ast node
- # @param code [String] code need to be inserted.
- # @param at [String] insert position, beginning or end
- # @param to [String] where to insert, if it is nil, will insert to current node.
- # @param and_comma [Boolean] insert extra comma.
- # @example
- # source code of the ast node is
- # open('http://test.com')
- # then we call
- # mutation.insert(node, 'URI.', at: 'beginning')
- # the source code will be rewritten to
- # URI.open('http://test.com')
- def insert(node, code, at: 'end', to: nil, and_comma: false)
- @actions << InsertAction.new(node, code, at: at, to: to, and_comma: and_comma, adapter: @adapter).process
- end
-
- # Prepend code to the ast node.
- # @param node [Node] ast node
- # @param code [String] new code to prepend.
- # @example
- # source code of the ast node is
- # def setup
- # do_something
- # end
- # then we call
- # mutation.prepend(node, 'super')
- # the source code will be rewritten to
- # def setup
- # super
- # do_something
- # end
- def prepend(node, code)
- @actions << PrependAction.new(node, code, adapter: @adapter).process
- end
-
- # Remove source code of the ast node.
- # @param node [Node] ast node
- # @param and_comma [Boolean] delete extra comma.
- # @example
- # source code of the ast node is
- # puts "test"
- # then we call
- # mutation.remove(node)
- # the source code will be removed
- def remove(node, and_comma: false)
- @actions << RemoveAction.new(node, and_comma: and_comma, adapter: @adapter).process
- end
-
- # Replace child node of the ast node with new code.
- # @param node [Node] ast node
- # @param selectors [Array<Symbol>] selector names of child node.
- # @param with [String] code need to be replaced with.
- # @example
- # source code of the ast node is
- # assert(object.empty?)
- # then we call
- # mutation.replace(node, :message, with: 'assert_empty')
- # mutation.replace(node, :arguments, with: '{{arguments.first.receiver}}')
- # the source code will be rewritten to
- # assert_empty(object)
- def replace(node, *selectors, with:)
- @actions << ReplaceAction.new(node, *selectors, with: with, adapter: @adapter).process
- end
-
- # Replace source code of the ast node with new code.
- # @param node [Node] ast node
- # @param code [String] code need to be replaced with.
- # @example
- # source code of the ast node is
- # obj.stub(:foo => 1, :bar => 2)
- # then we call
- # replace_with 'allow({{receiver}}).to receive_messages({{arguments}})'
- # the source code will be rewritten to
- # allow(obj).to receive_messages(:foo => 1, :bar => 2)
- def replace_with(node, code)
- @actions << ReplaceWithAction.new(node, code, adapter: @adapter).process
- end
-
- # Wrap source code of the ast node with prefix and suffix code.
- # @param node [Node] ast node
- # @param prefix [String] prefix code need to be wrapped with.
- # @param suffix [String] suffix code need to be wrapped with.
- # @param newline [Boolean] add newline after prefix and before suffix.
- # @example
- # source code of the ast node is
- # class Foobar
- # end
- # then we call
- # wrap(node, prefix: 'module Synvert', suffix: 'end', newline: true)
- # the source code will be rewritten to
- # module Synvert
- # class Foobar
- # end
- # end
- def wrap(node, prefix:, suffix:, newline: false)
- if newline
- indentation = @adapter.get_start_loc(node).column
- group do
- insert node, prefix + "\n" + (' ' * indentation), at: 'beginning'
- insert node, "\n" + (' ' * indentation) + suffix, at: 'end'
- indent node
- end
- else
- group do
- insert node, prefix, at: 'beginning'
- insert node, suffix, at: 'end'
- end
- end
- end
-
- # Indent source code of the ast node
- # @param node [Node] ast node
- # @example
- # source code of ast node is
- # class Foobar
- # end
- # then we call
- # indent(node)
- # the source code will be rewritten to
- # class Foobar
- # end
- def indent(node)
- @actions << IndentAction.new(node, adapter: @adapter).process
- end
-
- # No operation.
- # @param node [Node] ast node
- def noop(node)
- @actions << NoopAction.new(node, adapter: @adapter).process
- end
-
- # group multiple actions
- def group
- current_actions = @actions
- group_action = GroupAction.new
- @actions = group_action.actions
- yield
- @actions = current_actions
- @actions << group_action.process
- end
-
# Process actions and return the new source.
#
# If there's an action range conflict,
# it will raise a ConflictActionError if strategy is set to THROW_ERROR,
# it will process all non conflicted actions and return `{ conflict: true }`
@@ -260,19 +87,18 @@
flatten_actions = flat_actions(@actions)
if flatten_actions.length == 0
return NodeMutation::Result.new(affected: false, conflicted: false)
end
- source = +@source
@transform_proc.call(@actions) if @transform_proc
sorted_actions = sort_flatten_actions(flatten_actions)
conflict_actions = get_conflict_actions(sorted_actions)
if conflict_actions.size > 0 && strategy?(Strategy::THROW_ERROR)
raise ConflictActionError, "mutation actions are conflicted"
end
actions = sort_flatten_actions(flat_actions(get_filter_actions(conflict_actions)))
- new_source = rewrite_source(source, actions)
+ new_source = rewrite_source(+@source, actions)
result = NodeMutation::Result.new(affected: true, conflicted: !conflict_actions.empty?)
result.new_source = new_source
result
end