Class: Synvert::Command

Inherits:
Object
  • Object
show all
Defined in:
lib/synvert/command.rb

Class Method Summary collapse

Class Method Details

.available_rewritersObject



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/synvert/command.rb', line 61

def available_rewriters
  output = []
  Core::Rewriter.availables.each do |group, rewriters|
    rewriters.each do |name, rewriter|
      rewriter.process_with_sandbox
      sub_snippets =
        rewriter.sub_snippets.map { |sub_snippet|
          { group: sub_snippet.group, name: sub_snippet.name }
        }
      item = { group: group, name: name, description: rewriter.description, sub_snippets: sub_snippets }
      item[:ruby_version] = rewriter.ruby_version.version if rewriter.ruby_version
      item[:gem_spec] = { name: rewriter.gem_spec.name, version: rewriter.gem_spec.version } if rewriter.gem_spec
      output << item
    end
  end
  output
end

.default_snippets_homeObject



233
234
235
236
237
# File 'lib/synvert/command.rb', line 233

def default_snippets_home
  # ENV['HOME'] may use \ as file separator,
  # but File.join always uses / as file separator.
  ENV['SYNVERT_SNIPPETS_HOME'] || File.join(ENV['HOME'].gsub("\\", "/"), '.synvert-ruby')
end

.generate_snippet(snippet_name) ⇒ Object

generate a new snippet



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/synvert/command.rb', line 113

def generate_snippet(snippet_name)
  group, name = snippet_name.split('/')
  FileUtils.mkdir_p("lib/#{group}")
  FileUtils.mkdir_p("spec/#{group}")
  lib_content = <<~EOF
    # frozen_string_literal: true

    Synvert::Rewriter.new '#{group}', '#{name}' do
      configure(parser: Synvert::PRISM_PARSER)

      description <<~EOS
        It converts Foo to Bar

        ```ruby
        Foo
        ```

        =>

        ```ruby
        Bar
        ```
      EOS

      within_files '**/*.rb' do
        with_node type: 'const', to_source: 'Foo' do
          replace_with 'Bar'
        end
      end
    end
  EOF
  spec_content = <<~EOF
    # frozen_string_literal: true

    require 'spec_helper'

    RSpec.describe 'Convert Foo to Bar' do
      let(:rewriter_name) { '#{group}/#{name}' }
      let(:fake_file_path) { 'foobar.rb' }
      let(:test_content) { 'Foo' }
      let(:test_rewritten_content) { 'Bar' }

      include_examples 'convertable'
    end
  EOF
  File.write("lib/#{group}/#{name}.rb", lib_content)
  File.write("spec/#{group}/#{name}_spec.rb", spec_content)
end

.list_available_rewriters(format) ⇒ Object

List and print all available rewriters.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/synvert/command.rb', line 42

def list_available_rewriters(format)
  if Core::Rewriter.availables.empty?
    puts "There is no snippet under #{default_snippets_home}, please run `synvert-ruby --sync` to fetch snippets."
    return
  end

  if format == 'json'
    puts available_rewriters.to_json
  else
    Core::Rewriter.availables.each do |group, rewriters|
      puts group
      rewriters.each do |name, _rewriter|
        puts '    ' + name
      end
    end
    puts
  end
end

.open_rewriter(snippet_name) ⇒ Object

Open one rewriter.



29
30
31
32
33
34
35
36
37
38
39
# File 'lib/synvert/command.rb', line 29

def open_rewriter(snippet_name)
  editor = [ENV['SYNVERT_EDITOR'], ENV['EDITOR']].find { |e| !e.nil? && !e.empty? }
  return puts 'To open a synvert snippet, set $EDITOR or $SYNVERT_EDITOR' unless editor

  path = File.expand_path(File.join(default_snippets_home, "lib/#{snippet_name}.rb"))
  if File.exist?(path)
    system editor, path
  else
    puts "Can't run #{editor} #{path}"
  end
end

.query_available_rewriters(query, format) ⇒ Object

Query and print available rewriters.



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/synvert/command.rb', line 80

def query_available_rewriters(query, format)
  if format == 'json'
    puts available_rewriters.select { |rewriter|
           rewriter[:group].include?(query) || rewriter[:name].include?(query)
         }.to_json
  else
    Core::Rewriter.availables.each do |group, rewriters|
      if group.include?(query)
        puts group
        rewriters.each do |name, _rewriter|
          puts '    ' + name
        end
      elsif rewriters.keys.any? { |name| name.include?(query) }
        puts group
        rewriters.each do |name, _rewriter|
          puts '    ' + name if name.include?(query)
        end
      end
    end
  end
end

.read_helpersObject

read all helpers.



24
25
26
# File 'lib/synvert/command.rb', line 24

def read_helpers
  Dir.glob(File.join(default_snippets_home, 'lib/helpers/**/*.rb')).each { |file| require file }
end

.read_rewritersObject

read all rewriters.



19
20
21
# File 'lib/synvert/command.rb', line 19

def read_rewriters
  Dir.glob(File.join(default_snippets_home, 'lib/**/*.rb')).each { |file| require file }
end

.run_snippet(rewriter, format) ⇒ Object

run a snippet



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/synvert/command.rb', line 163

def run_snippet(rewriter, format)
  if format == 'json'
    rewriter.process
    output = {
      affected_files: rewriter.affected_files.union(rewriter.sub_snippets.sum(Set.new, &:affected_files)).to_a,
      warnings: rewriter.warnings.union(rewriter.sub_snippets.sum([], &:warnings))
    }
    puts output.to_json
  else
    puts "===== #{rewriter.group}/#{rewriter.name} started ====="
    rewriter.process
    rewriter.warnings.each do |warning|
      puts '[Warn] ' + warning.message
    end
    puts "===== #{rewriter.group}/#{rewriter.name} done ====="
  end
rescue StandardError => e
  if ENV['DEBUG']
    puts e.backtrace.join("\n")
  end
  if format == 'json'
    puts({ error: e.message }.to_json)
  else
    puts "Error: #{e.message}"
  end
  raise
end

.show_rewriter(snippet_name) ⇒ Object

Show and print one rewriter.



103
104
105
106
107
108
109
110
# File 'lib/synvert/command.rb', line 103

def show_rewriter(snippet_name)
  path = File.expand_path(File.join(default_snippets_home, "lib/#{snippet_name}.rb"))
  if File.exist?(path)
    puts File.read(path)
  else
    puts "snippet #{snippet_name} not found"
  end
end

.sync_snippetsObject

sync snippets



7
8
9
10
11
12
13
14
15
16
# File 'lib/synvert/command.rb', line 7

def sync_snippets
  if File.exist?(default_snippets_home)
    Dir.chdir(default_snippets_home) do
      Kernel.system('git checkout . && git pull --rebase')
    end
  else
    Kernel.system("git clone https://github.com/synvert-hq/synvert-snippets-ruby.git #{default_snippets_home}")
  end
  puts 'synvert snippets are synced'
end

.test_snippet(rewriter) ⇒ Object

test a snippet



192
193
194
195
196
197
198
199
200
201
# File 'lib/synvert/command.rb', line 192

def test_snippet(rewriter)
  results = rewriter.test
  puts results.to_json
rescue StandardError => e
  if ENV['DEBUG']
    puts e.backtrace.join("\n")
  end
  puts({ error: e.message }.to_json)
  raise
end

.test_snippet_in_bundle_gems(rewriter) ⇒ Object



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/synvert/command.rb', line 203

def test_snippet_in_bundle_gems(rewriter)
  Dir.chdir(Core::Configuration.root_path) do
    bundle_list_output = `/bin/bash -c -l 'BUNDLE_GEMFILE=./Gemfile bundle list'`
    raise bundle_list_output unless bundle_list_output.start_with?('Gems included by the bundle:')

    bundle_list_output.lines.each do |line|
      next unless line.start_with?('  * ')

      _, gem_name, gem_version_with_parentheses = line.split(' ')
      path = `/bin/bash -c -l 'BUNDLE_GEMFILE=./Gemfile bundle info #{gem_name} --path'`.strip

      Core::Configuration.root_path = path
      rewriter.reset
      test_results = rewriter.test
      affected_file_paths = test_results.filter { |result| result.affected? }
                                        .map { |result| result.file_path }
      if affected_file_paths.size > 0
        puts "#{gem_name} #{gem_version_with_parentheses}"
        affected_file_paths.each { |affected_file_path| puts "    #{affected_file_path}" }
      end
    end
  end
rescue StandardError => e
  if ENV['DEBUG']
    puts e.backtrace.join("\n")
  end
  puts "Error: #{e.message}"
  raise
end