Class: NodeMutation
- Inherits:
-
Object
- Object
- NodeMutation
- Defined in:
- lib/node_mutation.rb,
lib/node_mutation/version.rb
Defined Under Namespace
Classes: Action, Adapter, AppendAction, ConflictActionError, DeleteAction, GroupAction, Helper, IndentAction, InsertAction, InvalidAdapterError, MethodNotSupported, NoopAction, ParserAdapter, PrependAction, PrismAdapter, RemoveAction, ReplaceAction, ReplaceWithAction, Result, Strategy, Struct, SyntaxTreeAdapter
Constant Summary collapse
- VERSION =
"1.24.4"
Instance Attribute Summary collapse
- #actions ⇒ Array<NodeMutation::Struct::Action> readonly
-
#adapter ⇒ Object
readonly
Returns the value of attribute adapter.
-
#transform_proc ⇒ Proc
Proc to transfor the actions.
Class Method Summary collapse
-
.configure(options) ⇒ Object
Configure NodeMutation.
-
.strategy ⇒ Integer
Get the strategy by default is Strategy::KEEP_RUNNING.
-
.tab_width ⇒ Integer
Get tab width.
Instance Method Summary collapse
-
#append(node, code) ⇒ Object
Append code to the ast node.
-
#delete(node, *selectors, and_comma: false) ⇒ Object
Delete source code of the child ast node.
-
#group ⇒ Object
group multiple actions.
-
#indent(node) ⇒ Object
Indent source code of the ast node source code of ast node is class Foobar end then we call indent(node) the source code will be rewritten to class Foobar end.
-
#initialize(source, adapter:) ⇒ NodeMutation
constructor
Initialize a NodeMutation.
-
#insert(node, code, at: 'end', to: nil, and_comma: false) ⇒ Object
Insert code to the ast node.
-
#noop(node) ⇒ Object
No operation.
-
#prepend(node, code) ⇒ Object
Prepend code to the ast node.
-
#process ⇒ NodeMutation::Result
Process actions and return the new source.
-
#remove(node, and_comma: false) ⇒ Object
Remove source code of the ast node.
-
#replace(node, *selectors, with:) ⇒ Object
Replace child node of the ast node with new code.
-
#replace_with(node, code) ⇒ Object
Replace source code of the ast node with new code.
-
#test ⇒ NodeMutation::Result
Test actions and return the actions.
-
#wrap(node, prefix:, suffix:, newline: false) ⇒ Object
Wrap source code of the ast node with prefix and suffix code.
Constructor Details
#initialize(source, adapter:) ⇒ NodeMutation
Initialize a NodeMutation.
69 70 71 72 73 |
# File 'lib/node_mutation.rb', line 69 def initialize(source, adapter:) @source = source @actions = [] @adapter = get_adapter_instance(adapter) end |
Instance Attribute Details
#actions ⇒ Array<NodeMutation::Struct::Action> (readonly)
32 33 34 |
# File 'lib/node_mutation.rb', line 32 def actions @actions end |
#adapter ⇒ Object (readonly)
Returns the value of attribute adapter.
32 33 34 |
# File 'lib/node_mutation.rb', line 32 def adapter @adapter end |
#transform_proc ⇒ Proc
Returns proc to transfor the actions.
36 37 38 |
# File 'lib/node_mutation.rb', line 36 def transform_proc @transform_proc end |
Class Method Details
.configure(options) ⇒ Object
Configure NodeMutation
43 44 45 46 47 48 49 50 |
# File 'lib/node_mutation.rb', line 43 def configure() if [:strategy] @strategy = [:strategy] end if [:tab_width] @tab_width = [:tab_width].to_i end end |
.strategy ⇒ Integer
Get the strategy by default is NodeMutation::Strategy::KEEP_RUNNING
55 56 57 |
# File 'lib/node_mutation.rb', line 55 def strategy @strategy ||= Strategy::KEEP_RUNNING end |
.tab_width ⇒ Integer
Get tab width
61 62 63 |
# File 'lib/node_mutation.rb', line 61 def tab_width @tab_width ||= 2 end |
Instance Method Details
#append(node, code) ⇒ Object
Append code to the ast node. 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
90 91 92 |
# File 'lib/node_mutation.rb', line 90 def append(node, code) @actions << AppendAction.new(node, code, adapter: @adapter).process end |
#delete(node, *selectors, and_comma: false) ⇒ Object
Delete source code of the child ast node. 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(...)
105 106 107 |
# File 'lib/node_mutation.rb', line 105 def delete(node, *selectors, and_comma: false) @actions << DeleteAction.new(node, *selectors, and_comma: and_comma, adapter: @adapter).process end |
#group ⇒ Object
group multiple actions
242 243 244 245 246 247 248 249 |
# File 'lib/node_mutation.rb', line 242 def group current_actions = @actions group_action = GroupAction.new @actions = group_action.actions yield @actions = current_actions @actions << group_action.process end |
#indent(node) ⇒ Object
Indent source code of the ast node source code of ast node is
class Foobar
end
then we call
indent(node)
the source code will be rewritten to
class Foobar
end
231 232 233 |
# File 'lib/node_mutation.rb', line 231 def indent(node) @actions << IndentAction.new(node, adapter: @adapter).process end |
#insert(node, code, at: 'end', to: nil, and_comma: false) ⇒ Object
Insert code to the ast node. 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')
122 123 124 |
# File 'lib/node_mutation.rb', line 122 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 |
#noop(node) ⇒ Object
No operation.
237 238 239 |
# File 'lib/node_mutation.rb', line 237 def noop(node) @actions << NoopAction.new(node, adapter: @adapter).process end |
#prepend(node, code) ⇒ Object
Prepend code to the ast node. 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
141 142 143 |
# File 'lib/node_mutation.rb', line 141 def prepend(node, code) @actions << PrependAction.new(node, code, adapter: @adapter).process end |
#process ⇒ NodeMutation::Result
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 }` if strategy is set to KEEP_RUNNING.
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
# File 'lib/node_mutation.rb', line 258 def process @actions = optimize_group_actions(@actions) flatten_actions = flat_actions(@actions) if flatten_actions.length == 0 return NodeMutation::Result.new(affected: false, conflicted: false) end @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) result = NodeMutation::Result.new(affected: true, conflicted: !conflict_actions.empty?) result.new_source = new_source result end |
#remove(node, and_comma: false) ⇒ Object
Remove source code of the ast node. source code of the ast node is
puts "test"
then we call
mutation.remove(node)
the source code will be removed
154 155 156 |
# File 'lib/node_mutation.rb', line 154 def remove(node, and_comma: false) @actions << RemoveAction.new(node, and_comma: and_comma, adapter: @adapter).process end |
#replace(node, *selectors, with:) ⇒ Object
Replace child node of the ast node with new code. 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)
170 171 172 |
# File 'lib/node_mutation.rb', line 170 def replace(node, *selectors, with:) @actions << ReplaceAction.new(node, *selectors, with: with, adapter: @adapter).process end |
#replace_with(node, code) ⇒ Object
Replace source code of the ast node with new code. 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 (:foo => 1, :bar => 2)
184 185 186 |
# File 'lib/node_mutation.rb', line 184 def replace_with(node, code) @actions << ReplaceWithAction.new(node, code, adapter: @adapter).process end |
#test ⇒ NodeMutation::Result
Test actions and return the actions.
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 }` if strategy is set to KEEP_RUNNING.
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
# File 'lib/node_mutation.rb', line 287 def test @actions = optimize_group_actions(@actions) flatten_actions = flat_actions(@actions) if flatten_actions.length == 0 return NodeMutation::Result.new(affected: false, conflicted: false) end @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 result = NodeMutation::Result.new(affected: true, conflicted: !conflict_actions.empty?) actions = sort_actions(get_filter_actions(conflict_actions)) result.actions = actions.map(&:to_struct) result end |
#wrap(node, prefix:, suffix:, newline: false) ⇒ Object
Wrap source code of the ast node with prefix and suffix code. 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
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/node_mutation.rb', line 204 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 |