Ruby => Crystal Codemod
This project is a fork of Rufo. (Rufo and Crystal were both created by Ary Borenszweig!)
Rufo is as an opinionated ruby formatter, intended to be used via the command line as a text-editor plugin, to autoformat files on save or on demand.
The formatting rules have been modified in an attempt to produce some semi-valid Crystal code. Then you need to add some type annotations and fix any other issues manually. See the Crystal for Rubyists wiki page to learn more about the syntax differences.
Ruby => Crystal Codemod / Rufo supports all Ruby versions >= 2.4.5, due to a bug in Ruby's Ripper parser.
Requirements
- Ruby >=
2.4.5
-
Crystal >=
0.31.1
Installation
Install the gem with:
$ gem install ruby_crystal_codemod
Usage
Go to the directory where you want to convert Ruby files into Crystal. Then run:
ruby_crystal_codemod .
This command will create new *.cr
files and attempt to fix any simple errors. Then it will
run crystal tool format
to format the generated code.
Next Steps:
- Once you've fixed all of the syntax and type errors, run
crystal tool format
to autoformat your code. - Run Ameba for static code analysis (similar to RuboCop), and fix all of the errors.
- (Unfortunately Ameba doesn't have a --fix option yet.)
Writing Ruby / Crystal in the same file
If you want to write both Ruby and Crystal in a Ruby file, you can use some
special #~# BEGIN <language>
and #~# END <language>
comments.
Code between #~# BEGIN ruby
and #~# END ruby
should be uncommented,
and code between #~# BEGIN crystal
and #~# END crystal
should be commented.
When transpiling a Ruby file into Crystal, the transpiler will remove all of the Ruby lines between these comments, and it will uncomment all of the Crystal lines.
The BEGIN
/ END
comments can start with either #~#
or # ~#
. (Code formatters / linters often enforce a space after the #
character for comments.)
For example, here's how you can define a class that works for both Ruby and Crystal: (Crystal requires type annotations here.)
class Foo
attr_accessor :foo
#~# BEGIN ruby
def initialize(foo)
@foo = foo
end
#~# END ruby
#~# BEGIN crystal
# @foo : Int32
# def initialize(@foo : Int32); end
#~# END crystal
end
When this file is executed by Ruby, Ruby will ignore all of the commented lines. When the file is run through the Ruby => Crystal transpiler, it will be transformed into the following Crystal code:
class Foo
property :foo
@foo : Int32
def initialize(@foo : Int32); end
end
(The transpiler automatically renames attr_accessor
to property
.)
See
spec/fixtures/crystal_codemod_test/example.rb:87
for a real-world example that is used in our acceptance specs.
Status
- Rename all file extensions from
.rb
to.cr
- Replace single quoted strings with double quotes
-
require_relative "foo"
->require "./foo"
-
$:
,LOAD_PATH
=> Show error and link to docs about CRYSTAL_PATH (for compiler) - Translate methods / keywords / operators:
-
include?
->includes?
-
key?
->has_key?
-
detect
->find
-
collect
->map
-
respond_to?
->responds_to?
-
length
,count
->size
-
__dir__
->__DIR__
-
and
->&&
-
or
->||
-
not
->!
-
foo.each(&:method)
->foo.each(&.method)
-
foo.map &:method
->foo.map &.method
-
-
attr_accessor
=>property
-
attr_reader
=>getter
-
attr_writer
=>setter
-
private
/protected
methods -
class << self
=>def self.foo
(?) -
YAML.load_file("./foo.yml")
=>YAML.parse(File.read("./foo.yml"))
- .each returns nil - Try to warn if it looks like the return value of
.each
is being used - for loops - Show a warning or link to the docs
- Consistent dot notation -
File::exists?
=>File.exists?
Future
- Sorbet Type Annotations -> Crystal type annotations
- Integration with gelauto, to automatically annotate Ruby code with Sorbet type definitions.
Testing
Run rspec
to run all the specs and integration tests. I've kept all of the original rufo specs, because they're all really fast, it doesn't hurt to produce nicely formatted Crystal code (before the crystal format pass.)
Crystal-specific formatting specs can be found in spec/lib/ruby_crystal_codemod/formatter_crystal_specs/*
.
There's also a Crystal acceptance spec at spec/lib/ruby_crystal_codemod/crystal_codemod_acceptance_spec.rb
.
This transpiles the example Ruby code in spec/fixtures/crystal_codemod_test
, and makes sure that Ruby
and Crystal produce the same output when they both run the respective code.
Developing
Before submitting a PR, please run:
bundle exec rake rubocop -a
bundle exec rufo lib/ spec/lib/
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/DocSpring/ruby_crystal_codemod.
License
The gem is available as open source under the terms of the MIT License.