Class: NodeMutation
- Inherits:
-
Object
- Object
- NodeMutation
- Defined in:
- lib/node_mutation.rb,
lib/node_mutation/version.rb
Defined Under Namespace
Modules: Engine Classes: Action, Adapter, AppendAction, ConflictActionError, DeleteAction, InsertAction, MethodNotSupported, NoopAction, ParserAdapter, PrependAction, RemoveAction, ReplaceAction, ReplaceWithAction, Result, Strategy, WrapAction
Constant Summary collapse
- VERSION =
"1.9.0"
Instance Attribute Summary collapse
-
#actions ⇒ Object
readonly
Returns the value of attribute actions.
Class Method Summary collapse
-
.adapter ⇒ NodeMutation::Adapter
Get the adapter.
-
.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, **options) ⇒ Object
Delete source code of the child ast node.
-
#initialize(source) ⇒ NodeMutation
constructor
Initialize a NodeMutation.
-
#insert(node, code, at: 'end', to: nil) ⇒ 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, **options) ⇒ 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, with:) ⇒ Object
Wrap source code of the ast node with new code.
Constructor Details
#initialize(source) ⇒ NodeMutation
Initialize a NodeMutation.
68 69 70 71 |
# File 'lib/node_mutation.rb', line 68 def initialize(source) @source = source @actions = [] end |
Instance Attribute Details
#actions ⇒ Object (readonly)
Returns the value of attribute actions.
26 27 28 |
# File 'lib/node_mutation.rb', line 26 def actions @actions end |
Class Method Details
.adapter ⇒ NodeMutation::Adapter
Get the adapter
48 49 50 |
# File 'lib/node_mutation.rb', line 48 def adapter @adapter ||= ParserAdapter.new end |
.configure(options) ⇒ Object
Configure NodeMutation
34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/node_mutation.rb', line 34 def configure() if [:adapter] @adapter = [:adapter] end if [:strategy] @strategy = [:strategy] end if [:tab_width] @tab_width = [:tab_width] 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
88 89 90 |
# File 'lib/node_mutation.rb', line 88 def append(node, code) @actions << AppendAction.new(node, code).process end |
#delete(node, *selectors, **options) ⇒ 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(...)
104 105 106 |
# File 'lib/node_mutation.rb', line 104 def delete(node, *selectors, **) @actions << DeleteAction.new(node, *selectors, **).process end |
#insert(node, code, at: 'end', to: nil) ⇒ 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')
120 121 122 |
# File 'lib/node_mutation.rb', line 120 def insert(node, code, at: 'end', to: nil) @actions << InsertAction.new(node, code, at: at, to: to).process end |
#noop(node) ⇒ Object
No operation.
207 208 209 |
# File 'lib/node_mutation.rb', line 207 def noop(node) @actions << NoopAction.new(node).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
139 140 141 |
# File 'lib/node_mutation.rb', line 139 def prepend(node, code) @actions << PrependAction.new(node, code).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.
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
# File 'lib/node_mutation.rb', line 218 def process if @actions.length == 0 return NodeMutation::Result.new(affected: false, conflicted: false) end conflict_actions = [] source = +@source @actions.sort_by! { |action| [action.start, action.end] } conflict_actions = get_conflict_actions if conflict_actions.size > 0 && strategy?(Strategy::THROW_ERROR) raise ConflictActionError, "mutation actions are conflicted" end @actions.reverse_each do |action| source[action.start...action.end] = action.new_code if action.new_code end NodeMutation::Result.new( affected: true, conflicted: !conflict_actions.empty?, new_source: source ) end |
#remove(node, **options) ⇒ 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
153 154 155 |
# File 'lib/node_mutation.rb', line 153 def remove(node, **) @actions << RemoveAction.new(node, **).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)
169 170 171 |
# File 'lib/node_mutation.rb', line 169 def replace(node, *selectors, with:) @actions << ReplaceAction.new(node, *selectors, with: with).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)
183 184 185 |
# File 'lib/node_mutation.rb', line 183 def replace_with(node, code) @actions << ReplaceWithAction.new(node, code).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.
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
# File 'lib/node_mutation.rb', line 247 def test if @actions.length == 0 return NodeMutation::Result.new(affected: false, conflicted: false, actions: []) end conflict_actions = [] @actions.sort_by! { |action| [action.start, action.end] } conflict_actions = get_conflict_actions if conflict_actions.size > 0 && strategy?(Strategy::THROW_ERROR) raise ConflictActionError, "mutation actions are conflicted" end NodeMutation::Result.new( affected: true, conflicted: !conflict_actions.empty?, actions: format_actions(@actions) ) end |
#wrap(node, with:) ⇒ Object
Wrap source code of the ast node with new code. source code of the ast node is
class Foobar
end
then we call
wrap(node, with: 'module Synvert')
the source code will be rewritten to
module Synvert
class Foobar
end
end
201 202 203 |
# File 'lib/node_mutation.rb', line 201 def wrap(node, with:) @actions << WrapAction.new(node, with: with).process end |