Synvert

logo

write snippet code to rewrite your project code.

synvert-ruby

synvert-ruby is a command tool to rewrite ruby code automatically, it depends on synvert-core-ruby and synvert-snippets-ruby.

synvert-core-ruby provides a set of DSLs to rewrite ruby code.

synvert-snippets-ruby provides official snippets to rewrite ruby code.

Here is an example of synvert snippet.

Synvert::Rewriter.new 'ruby', 'map_and_flatten_to_flat_map' do
  description <<~EOS
    It converts `map` and `flatten` to `flat_map`

    ```ruby
    enum.map do
      # do something
    end.flatten
    ```

    =>

    ```ruby
    enum.flat_map do
      # do something
    end
    ```
  EOS

  within_files Synvert::ALL_RUBY_FILES do
    with_node type: 'send', receiver: { type: 'block', caller: { type: 'send', message: 'map' } }, message: 'flatten', arguments: { size: 0 } do
      delete :message, :dot
      replace 'receiver.caller.message', with: 'flat_map'
    end
  end
end

Installation

To install the latest version, run

$ gem install synvert

This will also install synvert-core-ruby.

Before using synvert, you need to sync all official snippets first.

$ synvert-ruby --sync

Then you can use synvert to rewrite your ruby code, e.g.

$ synvert-ruby -r factory_bot/use_short_syntax

Usage

$ synvert-ruby -h
Usage: synvert-ruby [project_path]
    -d, --load SNIPPET_PATHS         load custom snippets, snippet paths can be local file path or remote http url
    -l, --list                       list all available snippets
    -q, --query QUERY                query specified snippets
    -s, --show SNIPPET_NAME          show specified snippet description, SNIPPET_NAME is combined by group and name, e.g. ruby/new_hash_syntax
    -o, --open SNIPPET_NAME          Open a snippet
    -g, --generate NEW_SNIPPET_NAME  generate a new snippet
        --sync                       sync snippets
        --execute                    execute snippet
    -r, --run SNIPPET_NAME           run specified snippet, e.g. ruby/new_hash_syntax
        --show-run-process           show processing files when running a snippet
        --skip FILE_PATTERNS         skip specified files or directories, separated by comma, e.g. app/models/post.rb,vendor/plugins/**/*.rb
    -f, --format FORMAT              output format
    -v, --version                    show this version

Sync snippets

Official Snippets are available on github, you can sync them any time you want.

$ synvert-ruby --sync

List snippets

List all available snippets

$ synvert-ruby -l

$ synvert-ruby --list --form json

Show a snippet

Describe what a snippet does.

$ synvert-ruby -s factory_bot/use_short_syntax

Open a snippet

Open a snippet in your editor, editor is defined in ENV['SNIPPET_EDITOR'] or ENV['EDITOR']

$ synvert-ruby -o factory_bot/use_short_syntax

Run a snippet

Run a snippet, analyze and then rewrite code.

$ synvert-ruby -r factory_bot/use_short_syntax ~/Sites/xinminlabs/synvert-core-ruby

Load custom snippet

$ synvert-ruby --load ~/.custom-snippets/my-own-snippet.rb -r my-own-snippet ~/Sites/xinminlabs/synvert-core-ruby

Show processing files when running a snippet.

$ synvert-ruby -r factory_bot/use_short_syntax --show-run-process ~/Sites/xinminlabs/synvert-core-ruby

Generate a snippet

Generate a new snippet

$ synvert-ruby -g ruby/convert_foo_to_bar

Docker

You can run synvert-ruby command in a docker container.

docker pull xinminlabs/awesomecode-synvert-ruby

docker run xinminlabs/awesomecode-synvert-ruby synvert-ruby --list

docker run -v <your project path>:/app xinminlabs/awesomecode-synvert-ruby synvert-ruby --run default/check_syntax /app

GUI

GUI Snippet Show Screenshot GUI Snippet New Single Screenshot GUI Snippet New Multi First Screenshot GUI Snippet New Multi Second Screenshot GUI Snippet Diff Screenshot

The GUI provides the following features

The best feature is that it can help you to write your own snippets without learning AST, you just need to fill in the inputs and outputs ruby code.

Download

Mac OS

Windows x64, it’s not signed and Microsoft SmartScreen will block it.

How to write inputs/outputs?

You can write input and output code to ask synvert to write the snippet for you.

Let me show you some examples:

Simple Case

From rails 2 to rails 3, I want to change migration code from def self.up to def up.

I fill in input as

def self.up
  # migration code
end

and output as

def up
  # migration code
end

it will generate the snippet as

Synvert::Rewriter.execute do
  within_files '**/*.rb' do
    with_node type: 'defs', name: 'up' do
      delete :self, :dot
    end
  end
end

The generated snippet finds all defs node with name up and delete the self and . keywords.

Complicated Case

From rails 2 to rails 3, I want to change activerecord code from update_all(conditions, values) to where(conditions).update(values).

I fill in inputs as

Post.update_all({ title: 'old title' }, { title: 'new title' })

and

Article.update_all({ name: 'old name' }, { name: 'new name' })

and outputs as

Post.where(title: 'old title').update_all(title: 'new title')

and

Article.where(name: 'old name').update_all(name: 'new name')

it will generate the snippet as


Synvert::Rewriter.execute do
  within_files '**/*.rb' do
    with_node type: 'send', message: 'update_all', arguments: { size: 2, first: { type: 'hash' }, second: { type: 'hash' } } do
      replace_with '{{receiver}}.where({{arguments.first.strip_curly_braces}}).update_all({{arguments.second.strip_curly_braces}})'
    end
  end
end

It finds any send node with message update_all and 2 hash node arguments, and replace the code with where(first_argument).update_all(second_argument).