Class: Synvert::Core::Rewriter::Instance

Inherits:
Object
  • Object
show all
Includes:
Helper
Defined in:
lib/synvert/core/rewriter/instance.rb

Overview

Instance is an execution unit, it finds specified ast nodes, checks if the nodes match some conditions, then add, replace or remove code.

One instance can contains one or many [Synvert::Core::Rewriter::Scope] and [Synvert::Rewriter::Condition].

Constant Summary collapse

DEFAULT_OPTIONS =
{ sort_by: 'begin_pos' }.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Helper

#add_arguments_with_parenthesis_if_necessary, #add_curly_brackets_if_necessary, #add_receiver_if_necessary, #reject_keys_from_hash, #strip_brackets

Constructor Details

#initialize(rewriter, file_pattern, options = {}, &block) ⇒ Synvert::Core::Rewriter::Instance

Initialize an instance.



76
77
78
79
80
81
82
83
# File 'lib/synvert/core/rewriter/instance.rb', line 76

def initialize(rewriter, file_pattern, options = {}, &block)
  @rewriter = rewriter
  @actions = []
  @file_pattern = file_pattern
  @options = DEFAULT_OPTIONS.merge(options)
  @block = block
  rewriter.helpers.each { |helper| singleton_class.send(:define_method, helper[:name], &helper[:block]) }
end

Instance Attribute Details

#current_fileObject



65
# File 'lib/synvert/core/rewriter/instance.rb', line 65

attr_accessor :current_node, :current_file

#current_nodeObject



65
66
67
# File 'lib/synvert/core/rewriter/instance.rb', line 65

def current_node
  @current_node
end

Class Method Details

.file_ast(file_path) ⇒ String

Cached file ast.



30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/synvert/core/rewriter/instance.rb', line 30

def file_ast(file_path)
  @file_ast ||= {}
  @file_ast[file_path] ||=
    begin
      buffer = Parser::Source::Buffer.new file_path
      buffer.source = file_source(file_path)

      parser = Parser::CurrentRuby.new
      parser.reset
      parser.parse buffer
    end
end

.file_source(file_path) ⇒ String

Cached file source.



16
17
18
19
20
21
22
23
24
# File 'lib/synvert/core/rewriter/instance.rb', line 16

def file_source(file_path)
  @file_source ||= {}
  @file_source[file_path] ||=
    begin
      source = File.read(file_path)
      source = Engine::ERB.encode(source) if /\.erb$/.match?(file_path)
      source
    end
end

.resetObject

Reset cached file source and ast.



55
56
57
58
# File 'lib/synvert/core/rewriter/instance.rb', line 55

def reset
  @file_source = {}
  @file_ast = {}
end

.write_file(file_path, source) ⇒ Object

Write source to file and remove cached file source and ast.



47
48
49
50
51
52
# File 'lib/synvert/core/rewriter/instance.rb', line 47

def write_file(file_path, source)
  source = Engine::ERB.decode(source) if /\.erb/.match?(file_path)
  File.write file_path, source.gsub(/ +\n/, "\n")
  @file_source[file_path] = nil
  @file_ast[file_path] = nil
end

Instance Method Details

#any_valueObject

Any value but nil.



289
290
291
# File 'lib/synvert/core/rewriter/instance.rb', line 289

def any_value
  Rewriter::AnyValue.new
end

#append(code, options = {}) ⇒ Object

Parse append dsl, it creates a [Synvert::Core::Rewriter::AppendAction] to append the code to the bottom of current node body.



223
224
225
# File 'lib/synvert/core/rewriter/instance.rb', line 223

def append(code, options = {})
  @actions << Rewriter::AppendAction.new(self, code, options)
end

#delete(*selectors) ⇒ Object

Parse delete dsl, it creates a [Synvert::Core::Rewriter::DeleteAction] to delete child nodes.



277
278
279
# File 'lib/synvert/core/rewriter/instance.rb', line 277

def delete(*selectors)
  @actions << Rewriter::DeleteAction.new(self, *selectors)
end

#goto_node(*child_node_names, &block) ⇒ Object

Parse goto_node dsl, it creates a [Synvert::Core::Rewriter::GotoScope] to go to a child node, then continue operating on the child node.



186
187
188
# File 'lib/synvert/core/rewriter/instance.rb', line 186

def goto_node(*child_node_names, &block)
  Rewriter::GotoScope.new(self, *child_node_names, &block).process
end

#if_exist_node(rules, &block) ⇒ Object

Parse if_exist_node dsl, it creates a [Synvert::Core::Rewriter::IfExistCondition] to check if matching nodes exist in the child nodes, if so, then continue operating on each matching ast node.



195
196
197
# File 'lib/synvert/core/rewriter/instance.rb', line 195

def if_exist_node(rules, &block)
  Rewriter::IfExistCondition.new(self, rules, &block).process
end

#if_only_exist_node(rules, &block) ⇒ Object

Parse if_only_exist_node dsl, it creates a [Synvert::Core::Rewriter::IfOnlyExistCondition] to check if current node has only one child node and the child node matches rules, if so, then continue operating on each matching ast node.



214
215
216
# File 'lib/synvert/core/rewriter/instance.rb', line 214

def if_only_exist_node(rules, &block)
  Rewriter::IfOnlyExistCondition.new(self, rules, &block).process
end

#insert(code, options = {}) ⇒ Object

Parse insert dsl, it creates a [Synvert::Core::Rewriter::InsertAction] to insert the code to the top of current node body.



232
233
234
# File 'lib/synvert/core/rewriter/instance.rb', line 232

def insert(code, options = {})
  @actions << Rewriter::InsertAction.new(self, code, options)
end

#insert_after(node, options = {}) ⇒ Object

Parse insert_after dsl, it creates a [Synvert::Core::Rewriter::InsertAfterAction] to insert the code next to the current node.



241
242
243
# File 'lib/synvert/core/rewriter/instance.rb', line 241

def insert_after(node, options = {})
  @actions << Rewriter::InsertAfterAction.new(self, node, options)
end

#nodeParser::AST::Node

Gets current node, it allows to get current node in block code.



130
131
132
# File 'lib/synvert/core/rewriter/instance.rb', line 130

def node
  @current_node
end

#processObject

Process the instance. It finds all files, for each file, it executes the block code, gets all rewrite actions, and rewrite source code back to original file.



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/synvert/core/rewriter/instance.rb', line 88

def process
  file_pattern = File.join(Configuration.path, @file_pattern)
  Dir.glob(file_pattern).each do |file_path|
    next if Configuration.skip_files.include? file_path

    begin
      conflict_actions = []
      source = +self.class.file_source(file_path)
      ast = self.class.file_ast(file_path)

      @current_file = file_path

      process_with_node ast do
        begin
          instance_eval(&@block)
        rescue NoMethodError
          puts @current_node.debug_info
          raise
        end
      end

      if @actions.length > 0
        @actions.sort_by! { |action| action.send(@options[:sort_by]) }
        conflict_actions = get_conflict_actions
        @actions.reverse_each do |action|
          source[action.begin_pos...action.end_pos] = action.rewritten_code
          source = remove_code_or_whole_line(source, action.line)
        end
        @actions = []

        update_file(file_path, source)
      end
    rescue Parser::SyntaxError
      puts "[Warn] file #{file_path} was not parsed correctly."
      # do nothing, iterate next file
    end while !conflict_actions.empty?
  end
end

#process_with_node(node) { ... } ⇒ Object

Set current_node to node and process.

Yields:

  • process



138
139
140
141
142
# File 'lib/synvert/core/rewriter/instance.rb', line 138

def process_with_node(node)
  self.current_node = node
  yield
  self.current_node = node
end

#process_with_other_node(node) { ... } ⇒ Object

Set current_node properly, process and set current_node back to original current_node.

Yields:

  • process



148
149
150
151
152
153
# File 'lib/synvert/core/rewriter/instance.rb', line 148

def process_with_other_node(node)
  original_node = current_node
  self.current_node = node
  yield
  self.current_node = original_node
end

#removeObject

Parse remove dsl, it creates a [Synvert::Core::Rewriter::RemoveAction] to remove current node.



270
271
272
# File 'lib/synvert/core/rewriter/instance.rb', line 270

def remove
  @actions << Rewriter::RemoveAction.new(self)
end

#replace(*selectors, with:) ⇒ Object

Parse replace with dsl, it creates a [Synvert::Core::Rewriter::ReplaceAction] to replace child nodes with code.



259
260
261
# File 'lib/synvert/core/rewriter/instance.rb', line 259

def replace(*selectors, with:)
  @actions << Rewriter::ReplaceAction.new(self, *selectors, with: with)
end

#replace_erb_stmt_with_exprObject

Parse replace_erb_stmt_with_expr dsl, it creates a [Synvert::Core::Rewriter::ReplaceErbStmtWithExprAction] to replace erb stmt code to expr code.



265
266
267
# File 'lib/synvert/core/rewriter/instance.rb', line 265

def replace_erb_stmt_with_expr
  @actions << Rewriter::ReplaceErbStmtWithExprAction.new(self)
end

#replace_with(code, options = {}) ⇒ Object

Parse replace_with dsl, it creates a [Synvert::Core::Rewriter::ReplaceWithAction] to replace current node with code.



250
251
252
# File 'lib/synvert/core/rewriter/instance.rb', line 250

def replace_with(code, options = {})
  @actions << Rewriter::ReplaceWithAction.new(self, code, options)
end

#unless_exist_node(rules, &block) ⇒ Object

Parse unless_exist_node dsl, it creates a [Synvert::Core::Rewriter::UnlessExistCondition] to check if matching nodes doesn’t exist in the child nodes, if so, then continue operating on each matching ast node.



204
205
206
# File 'lib/synvert/core/rewriter/instance.rb', line 204

def unless_exist_node(rules, &block)
  Rewriter::UnlessExistCondition.new(self, rules, &block).process
end

#warn(message) ⇒ Object

Parse warn dsl, it creates a [Synvert::Core::Rewriter::Warning] to save warning message.



284
285
286
# File 'lib/synvert/core/rewriter/instance.rb', line 284

def warn(message)
  @rewriter.add_warning Rewriter::Warning.new(self, message)
end

#within_direct_node(rules, &block) ⇒ Object Also known as: with_direct_node

Parse within_direct_node dsl, it creates a [Synvert::Core::Rewriter::WithinScope] to find direct matching ast nodes, then continue operating on each matching ast node.



175
176
177
# File 'lib/synvert/core/rewriter/instance.rb', line 175

def within_direct_node(rules, &block)
  Rewriter::WithinScope.new(self, rules, { recursive: false }, &block).process
end

#within_node(rules, &block) ⇒ Object Also known as: with_node

Parse within_node dsl, it creates a [Synvert::Core::Rewriter::WithinScope] to find recursive matching ast nodes, then continue operating on each matching ast node.



164
165
166
# File 'lib/synvert/core/rewriter/instance.rb', line 164

def within_node(rules, &block)
  Rewriter::WithinScope.new(self, rules, { recursive: true }, &block).process
end