DSL

Synvert provides a simple dsl to define a snippet.

Synvert::Rewriter.new "group_name", "snippet_name" do
  description "description"

  if_gem gem_name, {gte: gem_version}

  within_file file_pattern do
    within_node rules do
      with_node rules do
        remove
      end
    end
  end

  within_files files_pattern do
    with_node rules do
      unless_exist_node rule do
        insert code
      end
    end
  end
end

describe

Describe what the snippet does.

description 'description of snippet'

if_ruby

Checks if current ruby version is greater than or equal to the specified version.

if_ruby '2.0.0'

if_gem

Checks the gem in Gemfile.lock, if gem version in Gemfile.lock is less than, greater than or equal to the version in if_gem, the snippet will be executed, otherwise, the snippet will be ignored.

if_gem 'factory_girl', {eq: '2.0.0'}
if_gem 'factory_girl', {ne: '2.0.0'}
if_gem 'factory_girl', {gt: '2.0.0'}
if_gem 'factory_girl', {lt: '2.0.0'}
if_gem 'factory_girl', {gte: '2.0.0'}
if_gem 'factory_girl', {lte: '2.0.0'}

add_file

Add a new file and write content.

content =<<- EOF
ActiveSupport.on_load(:action_controller) do
  wrap_parameters format: [:json]
end
EOF
add_file 'config/initializers/wrap_parameters.rb', content.strip

remove_file

Remove a file.

remove_file 'config/initiliazers/secret_token.rb'

within_file / within_files

Find files according to file pattern, the block will be executed only for the matching files.

within_file 'spec/spec_helper.rb' do
  # find nodes
  # check nodes
  # add / replace / remove code
end
within_files 'spec/**/*_spec.rb' do
  # find nodes
  # check nodes
  # add / replace / remove code
end

with_node / within_node

Find ast nodes according to the rules, the block will be executed for the matching nodes.

with_node type: 'send', receiver: 'FactoryGirl', message: 'create' do
  # check nodes
  # add / replace / remove code
end
within_node type: 'class', name: 'Test::Unit::TestCase' do
  # find child nodes
  # check nodes
  # add / replace / remove code
end

goto_node

Go to the specified child code.

with_node type: 'block' do
  goto_node :caller do
    # change code in block caller
  end
end

if_exist_node

Check if the node matches rules exists, if matches, then executes the block.

if_exist_node type: 'send', receiver: 'params', message: '[]' do
  # add / replace / remove code
end

unless_exist_node

Check if the node matches rules does not exist, if does not match, then executes the block.

unless_exist_node type: 'send', message: 'include', arguments: ['FactoryGirl::Syntax::Methods'] do
  # add / replace / remove code
end

if_only_exist_node

Check if the current node contains only one child node and the child node matches rules, if matches, then executes the node.

if_only_exist_node type: 'send', receiver: 'self', message: 'include_root_in_json=', arguments: [false] do
  # add / replace / remove code
end

append

Add the code at the bottom of the current node body.

append 'config.eager_load = false'

insert

Add the code at the top of the current node body.

insert "include FactoryGirl::Syntax::Methods"

insert_after

Add the code next to the current node.


secret = SecureRandom.hex(64)
insert_after "{{receiver}}.secret_key_base = '#{secret}'"

replace_with

Replace the current node with the code.


replace_with "create({{arguments}})"

remove

Remove the current node.

with_node type: 'send', message: 'rename_index' do
  remove
end

replace_erb_stmt_with_expr

Replace erb statemet code with expression code.

with_node type: 'block', caller: {type: 'send', receiver: nil, message: 'form_for'} do
  replace_erb_stmt_with_expr
end

warn

Don’t change any code, but will give a warning message.

warn 'Using a return statement in an inline callback block causes a LocalJumpError to be raised when the callback is executed.'

add_snippet

Add other snippet, it’s easy to reuse other snippets.

add_snippet 'rails', 'convert_dynamic_finders'

helper_method

Add a method which is available in the current snippet.

helper_method :method1 do |arg1, arg2|
  # do anything you want
end

method1(arg1, arg2)

todo

List somethings the snippet should do, but not do yet.

todo <<-EOF
Rails 4.0 no longer supports loading plugins from vendor/plugins. You
must replace any plugins by extracting them to gems and adding them to
your Gemfile. If you choose not to make them gems, you can move them
into, say, lib/my_plugin/* and add an appropriate initializer in
config/initializers/my_plugin.rb.
EOF

Check out the source code of DSLs here: this and that