ruby_crystal_codemod 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +103 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +4 -0
- data/.gitignore +24 -0
- data/.rspec +2 -0
- data/.rubocop.yml +15 -0
- data/.rufo +2 -0
- data/.vscode/settings.json +4 -0
- data/Gemfile +4 -0
- data/Guardfile +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +141 -0
- data/Rakefile +8 -0
- data/bin/compile_post_process +4 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/docs/developing-ruby-crystal-codemod.md +3 -0
- data/docs/releasing-a-gem.md +17 -0
- data/exe/ruby_crystal_codemod +4 -0
- data/lib/ruby_crystal_codemod.rb +19 -0
- data/lib/ruby_crystal_codemod/command.rb +200 -0
- data/lib/ruby_crystal_codemod/dot_file.rb +52 -0
- data/lib/ruby_crystal_codemod/file_finder.rb +62 -0
- data/lib/ruby_crystal_codemod/formatter.rb +4239 -0
- data/lib/ruby_crystal_codemod/logger.rb +39 -0
- data/lib/ruby_crystal_codemod/settings.rb +29 -0
- data/lib/ruby_crystal_codemod/version.rb +5 -0
- data/rakelib/ruby_crystal_codemod.rake +22 -0
- data/ruby_crystal_codemod.gemspec +34 -0
- data/run_test +5 -0
- data/util/post_process_crystal/shard.lock +14 -0
- data/util/post_process_crystal/shard.yml +11 -0
- data/util/post_process_crystal/spec/fixtures/example.rb +13 -0
- data/util/post_process_crystal/spec/post_process_crystal_spec.cr +124 -0
- data/util/post_process_crystal/spec/spec_helper.cr +4 -0
- data/util/post_process_crystal/src/command.cr +16 -0
- data/util/post_process_crystal/src/post_process_crystal.cr +48 -0
- metadata +208 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7986ec4b8202ba075699a41427f7fbcdd98b468dc08d03cdce440b9677479520
|
4
|
+
data.tar.gz: 10353a8c14d346e7dec11f6848b88ab3463f93ddb203283c245f47338676e53f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 101ae069a8d1f6805ef80c7d5140240eb968f8e4633ef8e1ee361bf3cbb4c89c1ea695b4a3b4b524e0c579014f79747a28c86d0cc2f428bff017407e4634b504
|
7
|
+
data.tar.gz: e5578aefa628eabe37f6cf9ffc2445571bec4466031435ea97c45166cdbac3a06ae6d41651bcc459f8f5f569f28f2b9cd31a054d62c43186ff036939c8534a21
|
@@ -0,0 +1,103 @@
|
|
1
|
+
"-": &dockerbuild
|
2
|
+
steps:
|
3
|
+
- checkout
|
4
|
+
|
5
|
+
# Which version of ruby?
|
6
|
+
- run:
|
7
|
+
name: Which ruby?
|
8
|
+
command: ruby --version | tee ruby-version-for-ci.txt
|
9
|
+
|
10
|
+
# Which version of bundler?
|
11
|
+
- run:
|
12
|
+
name: Which bundler?
|
13
|
+
command: bundle -v
|
14
|
+
|
15
|
+
- run:
|
16
|
+
name: Install Crystal
|
17
|
+
command: >-
|
18
|
+
sudo apt-get update &&
|
19
|
+
sudo apt-get install apt-transport-https ca-certificates &&
|
20
|
+
curl -sSL https://dist.crystal-lang.org/apt/setup.sh | sudo bash &&
|
21
|
+
curl -sL "https://keybase.io/crystal/pgp_keys.asc" | sudo apt-key add - &&
|
22
|
+
echo "deb https://dist.crystal-lang.org/apt crystal main" | sudo tee /etc/apt/sources.list.d/crystal.list &&
|
23
|
+
sudo apt-get update &&
|
24
|
+
sudo apt-get -y install crystal &&
|
25
|
+
crystal --version
|
26
|
+
|
27
|
+
# Restore bundle cache
|
28
|
+
- restore_cache:
|
29
|
+
keys:
|
30
|
+
- bundler-packages-v2-{{ checksum "ruby-version-for-ci.txt" }}-{{ checksum "ruby_crystal_codemod.gemspec" }}
|
31
|
+
|
32
|
+
- run:
|
33
|
+
name: Bundle Install
|
34
|
+
command: bundle check || bundle install
|
35
|
+
|
36
|
+
# Restore crystal cache
|
37
|
+
- restore_cache:
|
38
|
+
keys:
|
39
|
+
- crystal-shards-{{ checksum "util/post_process_crystal/shard.lock" }}
|
40
|
+
|
41
|
+
- run:
|
42
|
+
name: Compile ./util/post_process tool
|
43
|
+
command: ./bin/compile_post_process
|
44
|
+
|
45
|
+
- run:
|
46
|
+
name: Run rspec
|
47
|
+
command: |
|
48
|
+
bundle exec rspec --profile 10 \
|
49
|
+
--format RspecJunitFormatter \
|
50
|
+
--out test_results/rspec.xml \
|
51
|
+
--format progress
|
52
|
+
- run:
|
53
|
+
name: Run RuboCop
|
54
|
+
command: |
|
55
|
+
bundle exec rake rubocop
|
56
|
+
- run:
|
57
|
+
name: Run Rufo
|
58
|
+
command: |
|
59
|
+
bundle exec rufo -c lib/ spec/lib/
|
60
|
+
|
61
|
+
# Store crystal cache
|
62
|
+
- save_cache:
|
63
|
+
key: crystal-shards-{{ checksum "util/post_process_crystal/shard.lock" }}
|
64
|
+
paths:
|
65
|
+
- util/post_process_crystal/lib/
|
66
|
+
- util/post_process_crystal/shard.lock
|
67
|
+
|
68
|
+
# Store bundle cache
|
69
|
+
- save_cache:
|
70
|
+
key: bundler-packages-v2-{{ checksum "ruby-version-for-ci.txt" }}-{{ checksum "ruby_crystal_codemod.gemspec" }}
|
71
|
+
paths:
|
72
|
+
- vendor/bundle
|
73
|
+
- Gemfile.lock
|
74
|
+
|
75
|
+
# Save test results for timing analysis
|
76
|
+
- store_test_results:
|
77
|
+
path: test_results
|
78
|
+
|
79
|
+
|
80
|
+
version: 2
|
81
|
+
jobs:
|
82
|
+
build-2-6-5:
|
83
|
+
<<: *dockerbuild
|
84
|
+
docker:
|
85
|
+
- image: circleci/ruby:2.6.5
|
86
|
+
environment:
|
87
|
+
BUNDLE_JOBS: "3"
|
88
|
+
BUNDLE_RETRY: "3"
|
89
|
+
BUNDLE_PATH: /home/circleci/project/vendor/bundle
|
90
|
+
build-2-4-5:
|
91
|
+
<<: *dockerbuild
|
92
|
+
docker:
|
93
|
+
- image: circleci/ruby:2.4.5
|
94
|
+
environment:
|
95
|
+
BUNDLE_JOBS: "3"
|
96
|
+
BUNDLE_RETRY: "3"
|
97
|
+
BUNDLE_PATH: vendor/bundle
|
98
|
+
workflows:
|
99
|
+
version: 2
|
100
|
+
test:
|
101
|
+
jobs:
|
102
|
+
- build-2-6-5
|
103
|
+
- build-2-4-5
|
data/.gitignore
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
/.bundle/
|
2
|
+
/.yardoc
|
3
|
+
/Gemfile.lock
|
4
|
+
/_yardoc/
|
5
|
+
/coverage/
|
6
|
+
/doc/
|
7
|
+
/pkg/
|
8
|
+
/spec/reports/
|
9
|
+
/tmp/
|
10
|
+
/*.gem
|
11
|
+
|
12
|
+
# rspec failure tracking
|
13
|
+
.rspec_status
|
14
|
+
sample_code
|
15
|
+
|
16
|
+
.byebug_history
|
17
|
+
# *.cr
|
18
|
+
|
19
|
+
test.rb
|
20
|
+
test.cr
|
21
|
+
|
22
|
+
util/post_process
|
23
|
+
util/post_process_crystal/lib/
|
24
|
+
*.dwarf
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/.rufo
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Ary Borenszweig
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
[![CircleCI](https://circleci.com/gh/DocSpring/ruby_crystal_codemod.svg?style=svg)](https://circleci.com/gh/DocSpring/ruby_crystal_codemod)
|
2
|
+
|
3
|
+
# Ruby => Crystal Codemod
|
4
|
+
|
5
|
+
This project is a fork of [Rufo](https://github.com/ruby-formatter/rufo). (Rufo and Crystal were both created by [Ary Borenszweig](https://github.com/asterite)!)
|
6
|
+
|
7
|
+
> 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.
|
8
|
+
|
9
|
+
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](https://github.com/crystal-lang/crystal/wiki/Crystal-for-Rubyists) wiki page to learn more about the syntax differences.
|
10
|
+
|
11
|
+
> Ruby => Crystal Codemod / Rufo supports all Ruby versions >= 2.4.**5**, due to a bug in Ruby's Ripper parser.
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Install the gem with:
|
16
|
+
|
17
|
+
```
|
18
|
+
$ gem install ruby_crystal_codemod
|
19
|
+
```
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
Go to the directory where you want to convert Ruby files into Crystal. Then run:
|
24
|
+
|
25
|
+
```
|
26
|
+
ruby_crystal_codemod .
|
27
|
+
```
|
28
|
+
|
29
|
+
This command will create new `*.cr` files and attempt to fix any simple errors. Then it will
|
30
|
+
run `crystal tool format` to format the generated code.
|
31
|
+
|
32
|
+
## Next Steps:
|
33
|
+
|
34
|
+
* Once you've fixed all of the syntax and type errors, run `crystal tool format` to autoformat your code.
|
35
|
+
* Run [Ameba](https://github.com/crystal-ameba/ameba) for static code analysis (similar to RuboCop), and fix all of the errors.
|
36
|
+
* *(Unfortunately Ameba doesn't have a --fix option yet.)*
|
37
|
+
|
38
|
+
## Writing Ruby / Crystal in the same file
|
39
|
+
|
40
|
+
If you want to write both Ruby and Crystal in a Ruby file, you can use some
|
41
|
+
special `#~# BEGIN <language>` and `#~# END <language>` comments.
|
42
|
+
Code between `#~# BEGIN ruby` and `#~# END ruby` should be uncommented,
|
43
|
+
and code between `#~# BEGIN crystal` and `#~# END crystal` should be commented.
|
44
|
+
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.
|
45
|
+
|
46
|
+
The `BEGIN` / `END` comments can start with either `#~#` or `# ~#`. (Code formatters / linters often enforce a space after the `#` character for comments.)
|
47
|
+
|
48
|
+
> This comment post-processing step is done by a Crystal program in `./util/post_process_crystal`. Run `./bin/compile_post_process` to compile the binary at `./util/post_process`.
|
49
|
+
|
50
|
+
For example, here's how you can define a class that works for both Ruby and Crystal:
|
51
|
+
(Crystal requires type annotations here.)
|
52
|
+
|
53
|
+
```
|
54
|
+
class Foo
|
55
|
+
attr_accessor :foo
|
56
|
+
|
57
|
+
#~# BEGIN ruby
|
58
|
+
def initialize(foo)
|
59
|
+
@foo = foo
|
60
|
+
end
|
61
|
+
#~# END ruby
|
62
|
+
#~# BEGIN crystal
|
63
|
+
# @foo : Int32
|
64
|
+
# def initialize(@foo : Int32); end
|
65
|
+
#~# END crystal
|
66
|
+
end
|
67
|
+
```
|
68
|
+
|
69
|
+
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:
|
70
|
+
|
71
|
+
```
|
72
|
+
class Foo
|
73
|
+
property :foo
|
74
|
+
|
75
|
+
@foo : Int32
|
76
|
+
def initialize(@foo : Int32); end
|
77
|
+
end
|
78
|
+
```
|
79
|
+
|
80
|
+
(The transpiler automatically renames `attr_accessor` to `property`.)
|
81
|
+
|
82
|
+
> See [`spec/fixtures/crystal_codemod_test/example.rb:87`](https://github.com/DocSpring/ruby_crystal_codemod/blob/master/spec/fixtures/crystal_codemod_test/example.rb#L87-L114) for a real-world example that is used in our acceptance specs.
|
83
|
+
|
84
|
+
## Status
|
85
|
+
|
86
|
+
- [x] Rename all file extensions from `.rb` to `.cr`
|
87
|
+
- [x] Replace single quoted strings with double quotes
|
88
|
+
- [x] `require_relative "foo"` -> `require "./foo"`
|
89
|
+
- [x] `$:`, `LOAD_PATH` => Show error and link to docs about CRYSTAL_PATH (for compiler)
|
90
|
+
- [x] Translate methods / keywords / operators:
|
91
|
+
- [x] `include?` -> `includes?`
|
92
|
+
- [x] `key?` -> `has_key?`
|
93
|
+
- [x] `detect` -> `find`
|
94
|
+
- [x] `collect` -> `map`
|
95
|
+
- [x] `respond_to?` -> `responds_to?`
|
96
|
+
- [x] `length`, `count` -> `size`
|
97
|
+
- [x] `__dir__` -> `__DIR__`
|
98
|
+
- [x] `and` -> `&&`
|
99
|
+
- [x] `or` -> `||`
|
100
|
+
- [x] `not` -> `!`
|
101
|
+
- [x] `foo.each(&:method)` -> `foo.each(&.method)`
|
102
|
+
- [x] `foo.map &:method` -> `foo.map &.method`
|
103
|
+
- [x] `attr_accessor` => `property`
|
104
|
+
- [x] `attr_reader` => `getter`
|
105
|
+
- [x] `attr_writer` => `setter`
|
106
|
+
- [ ] `private` / `protected` methods
|
107
|
+
- [ ] `class << self` => `def self.foo` (?)
|
108
|
+
- [ ] `YAML.load_file("./foo.yml")` => `YAML.parse(File.read("./foo.yml"))`
|
109
|
+
- [ ] .each returns nil - Try to warn if it looks like the return value of `.each` is being used
|
110
|
+
- [ ] for loops - Show a warning or link to the docs
|
111
|
+
- [ ] Consistent dot notation - `File::exists?` => `File.exists?`
|
112
|
+
|
113
|
+
## Future
|
114
|
+
|
115
|
+
* Sorbet Type Annotations -> Crystal type annotations
|
116
|
+
* Instrument a Ruby program to track of all the variable assignments at run-time. Store all of the classes that are detected in each variable (including the maximum values of integers, etc.) Convert this run-time type information into Sorbet/Crystal type annotations.
|
117
|
+
|
118
|
+
## Testing
|
119
|
+
|
120
|
+
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.)
|
121
|
+
|
122
|
+
Crystal-specific formatting specs can be found in `spec/lib/ruby_crystal_codemod/formatter_crystal_specs/*`.
|
123
|
+
|
124
|
+
There's also a Crystal acceptance spec at `spec/lib/ruby_crystal_codemod/crystal_codemod_acceptance_spec.rb`.
|
125
|
+
This transpiles the example Ruby code in `spec/fixtures/crystal_codemod_test`, and makes sure that Ruby
|
126
|
+
and Crystal produce the same output when they both run the respective code.
|
127
|
+
|
128
|
+
## Developing
|
129
|
+
|
130
|
+
Before submitting a PR, please run:
|
131
|
+
|
132
|
+
* `bundle exec rake rubocop -a`
|
133
|
+
* `bundle exec rufo lib/ spec/lib/`
|
134
|
+
|
135
|
+
## Contributing
|
136
|
+
|
137
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/DocSpring/ruby_crystal_codemod.
|
138
|
+
|
139
|
+
## License
|
140
|
+
|
141
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "ruby_crystal_codemod"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# Releasing a gem
|
2
|
+
|
3
|
+
1. Ensure that the tests pass and everything is working!
|
4
|
+
|
5
|
+
2. Add missing release notes to `CHANGELOG.md`
|
6
|
+
|
7
|
+
3. Bump version in
|
8
|
+
* `lib/ruby_crystal_codemod/version.rb`
|
9
|
+
* `CHANGELOG.md`
|
10
|
+
|
11
|
+
4. Commit version bump via
|
12
|
+
* `git commit -v "Version X.Y.Z"`
|
13
|
+
|
14
|
+
5. Release gem to RubyGems via
|
15
|
+
* `rake release`
|
16
|
+
|
17
|
+
6. :tada:
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyCrystalCodemod
|
4
|
+
class Bug < StandardError; end
|
5
|
+
|
6
|
+
class SyntaxError < StandardError; end
|
7
|
+
|
8
|
+
def self.format(code, filename, dir, **options)
|
9
|
+
Formatter.format(code, filename, dir, **options)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
require_relative "ruby_crystal_codemod/command"
|
14
|
+
require_relative "ruby_crystal_codemod/logger"
|
15
|
+
require_relative "ruby_crystal_codemod/dot_file"
|
16
|
+
require_relative "ruby_crystal_codemod/settings"
|
17
|
+
require_relative "ruby_crystal_codemod/formatter"
|
18
|
+
require_relative "ruby_crystal_codemod/version"
|
19
|
+
require_relative "ruby_crystal_codemod/file_finder"
|