ruby_crystal_codemod 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,39 @@
1
+ module RubyCrystalCodemod
2
+ class Logger
3
+ LEVELS = {
4
+ silent: 0,
5
+ error: 1,
6
+ warn: 2,
7
+ log: 3,
8
+ debug: 4,
9
+ }
10
+
11
+ def initialize(level)
12
+ @level = LEVELS.fetch(level)
13
+ end
14
+
15
+ def debug(*args)
16
+ $stdout.puts(*args) if should_output?(:debug)
17
+ end
18
+
19
+ def log(*args)
20
+ $stdout.puts(*args) if should_output?(:log)
21
+ end
22
+
23
+ def warn(*args)
24
+ $stderr.puts(*args) if should_output?(:warn)
25
+ end
26
+
27
+ def error(*args)
28
+ $stderr.puts(*args) if should_output?(:error)
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :level
34
+
35
+ def should_output?(level_to_check)
36
+ LEVELS.fetch(level_to_check) <= level
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,29 @@
1
+ module RubyCrystalCodemod::Settings
2
+ OPTIONS = {
3
+ parens_in_def: [:yes, :dynamic],
4
+ align_case_when: [false, true],
5
+ align_chained_calls: [false, true],
6
+ trailing_commas: [true, false],
7
+ quote_style: [:double, :single],
8
+ store_logs: [false, true],
9
+ }
10
+
11
+ attr_accessor(*OPTIONS.keys)
12
+
13
+ def init_settings(options)
14
+ OPTIONS.each do |name, valid_options|
15
+ default = valid_options.first
16
+ value = options.fetch(name, default)
17
+ unless valid_options.include?(value)
18
+ $stderr.puts "Invalid value for #{name}: #{value.inspect}. Valid " \
19
+ "values are: #{valid_options.map(&:inspect).join(", ")}"
20
+ value = default
21
+ end
22
+ self.public_send("#{name}=", value)
23
+ end
24
+ diff = options.keys - OPTIONS.keys
25
+ diff.each do |key|
26
+ $stderr.puts "Invalid config option=#{key}"
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyCrystalCodemod
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,22 @@
1
+ # desc "Alias for `rake rufo:run`"
2
+ # task :rufo => ["rufo:run"]
3
+
4
+ # namespace :rufo do
5
+ # require "rufo"
6
+
7
+ # def rufo_command(*switches, rake_args)
8
+ # files_or_dirs = rake_args[:files_or_dirs] || "."
9
+ # args = switches + files_or_dirs.split(" ")
10
+ # RubyCrystalCodemod::Command.run(args)
11
+ # end
12
+
13
+ # desc "Format Ruby code in current directory"
14
+ # task :run, [:files_or_dirs] do |_task, rake_args|
15
+ # rufo_command(rake_args)
16
+ # end
17
+
18
+ # desc "Check that no formatting changes are produced"
19
+ # task :check, [:files_or_dirs] do |_task, rake_args|
20
+ # rufo_command("--check", rake_args)
21
+ # end
22
+ # end
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "ruby_crystal_codemod/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ruby_crystal_codemod"
8
+ spec.version = RubyCrystalCodemod::VERSION
9
+ spec.authors = ["Ary Borenszweig", "Nathan Broadbent"]
10
+ spec.email = ["asterite@gmail.com", "nathan@docspring.com"]
11
+
12
+ spec.summary = %q{Ruby => Crystal codemod}
13
+ spec.description = %q{Attempts to transpile Ruby code into Crystal code}
14
+ spec.homepage = "https://github.com/docspring/ruby_crystal_codemod"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+ spec.required_ruby_version = ">= 2.4.5"
24
+
25
+ spec.add_development_dependency "awesome_print", "~> 1.8.0"
26
+ spec.add_development_dependency "bundler", ">= 1.15"
27
+ spec.add_development_dependency "byebug", "~> 10.0.2"
28
+ spec.add_development_dependency "guard-rspec", "~> 4.0"
29
+ spec.add_development_dependency "rake", "~> 10.0"
30
+ spec.add_development_dependency "rspec", "~> 3.0"
31
+ spec.add_development_dependency "rspec_junit_formatter", "~> 0.4.1"
32
+ spec.add_development_dependency "rubocop", "~> 0.63.1"
33
+ spec.add_development_dependency "rufo", "~> 0.7.0"
34
+ end
data/run_test ADDED
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+ set -eo pipefail
3
+
4
+ # For testing things
5
+ ./exe/ruby_crystal_codemod --simple-exit test.rb && crystal run test.cr
@@ -0,0 +1,14 @@
1
+ version: 1.0
2
+ shards:
3
+ mocks:
4
+ github: DocSpring/mocks.cr
5
+ commit: b098c6ae8a34509df88baf4d3664471cd223bdfe
6
+
7
+ singleton:
8
+ github: waterlink/singleton.cr
9
+ version: 1.0.0
10
+
11
+ spectator:
12
+ gitlab: arctic-fox/spectator
13
+ version: 0.8.3
14
+
@@ -0,0 +1,11 @@
1
+ name: process_comments
2
+ version: 0.1.0
3
+
4
+ development_dependencies:
5
+ spectator:
6
+ gitlab: arctic-fox/spectator
7
+ mocks:
8
+ github: DocSpring/mocks.cr
9
+ branch: updates_for_0.31.1
10
+
11
+ license: MIT
@@ -0,0 +1,13 @@
1
+ class Foo
2
+ attr_accessor :foo
3
+
4
+ #~# BEGIN ruby
5
+ def initialize(foo)
6
+ @foo = foo
7
+ end
8
+ #~# END ruby
9
+ #~# BEGIN crystal
10
+ # @foo : Int32
11
+ # def initialize(@foo: Int32); end
12
+ #~# END crystal
13
+ end
@@ -0,0 +1,124 @@
1
+ require "./spec_helper"
2
+
3
+ Mocks.create_mock PostProcessCrystal do
4
+ mock self.file_read_lines(filename)
5
+ end
6
+
7
+ Spectator.describe PostProcessCrystal do
8
+ it "mocks the call to File.read_lines" do
9
+ allow(PostProcessCrystal).to receive(self.file_read_lines("example_file")).and_return([
10
+ "hey, world!",
11
+ "how's it hanging"
12
+ ])
13
+
14
+ ppc = PostProcessCrystal.new("example_file")
15
+ ppc.post_process_crystal
16
+ expect(ppc.contents).to eq("hey, world!\nhow's it hanging\n")
17
+ end
18
+
19
+ it "removes any content between #~# BEGIN ruby and #~# END ruby" do
20
+ allow(PostProcessCrystal).to receive(self.file_read_lines("example_file")).and_return(
21
+ <<-CODE.lines
22
+ def foo
23
+ #~# BEGIN ruby
24
+ return 234
25
+ #~# END ruby
26
+ 123
27
+ end
28
+ #~# BEGIN ruby
29
+ puts "Code called from Ruby!"
30
+ # comment should be removed
31
+ #~# END ruby
32
+
33
+ puts foo
34
+ CODE
35
+ )
36
+
37
+ ppc = PostProcessCrystal.new("example_file")
38
+ ppc.post_process_crystal
39
+ expect(ppc.contents).to eq(
40
+ <<-CODE
41
+ def foo
42
+ 123
43
+ end
44
+
45
+ puts foo
46
+
47
+ CODE
48
+ )
49
+ end
50
+
51
+ it "uncomments any content between #~# BEGIN crystal and #~# END crystal" do
52
+ allow(PostProcessCrystal).to receive(self.file_read_lines("example_file")).and_return(
53
+ <<-CODE.lines
54
+ def foo
55
+ 123
56
+ #~# BEGIN crystal
57
+ # return 456
58
+ #~# END crystal
59
+ end
60
+ #~# BEGIN crystal
61
+ # puts "Code called from Crystal!"
62
+ # # comment should be present
63
+ #~# END crystal
64
+ puts foo
65
+ CODE
66
+ )
67
+
68
+ ppc = PostProcessCrystal.new("example_file")
69
+ ppc.post_process_crystal
70
+ expect(ppc.contents).to eq(
71
+ <<-CODE
72
+ def foo
73
+ 123
74
+ return 456
75
+ end
76
+ puts "Code called from Crystal!"
77
+ # comment should be present
78
+ puts foo
79
+
80
+ CODE
81
+ )
82
+ end
83
+
84
+ it "removes all Ruby lines and uncomments all Crystal lines" do
85
+ allow(PostProcessCrystal).to receive(self.file_read_lines("example_file")).and_return(
86
+ <<-CODE.lines
87
+ class Foo
88
+ property :foo, :bar
89
+
90
+ #~# BEGIN ruby
91
+ def initialize(foo, bar)
92
+ @foo = foo
93
+ @bar = bar
94
+ end
95
+ #~# END ruby
96
+ #~# BEGIN crystal
97
+ # @foo : Int32
98
+ # @bar : Int32
99
+ # def initialize(@foo: Int32, @bar : Int32)
100
+ # end
101
+ #~# END crystal
102
+ end
103
+ CODE
104
+ )
105
+
106
+ ppc = PostProcessCrystal.new("example_file")
107
+ ppc.post_process_crystal
108
+ # NOTE: This is a post-processing step. The rufo formatting code
109
+ # must run before this, and will translate attr_accessor into property, etc.
110
+ expect(ppc.contents).to eq(
111
+ <<-CODE
112
+ class Foo
113
+ property :foo, :bar
114
+
115
+ @foo : Int32
116
+ @bar : Int32
117
+ def initialize(@foo: Int32, @bar : Int32)
118
+ end
119
+ end
120
+
121
+ CODE
122
+ )
123
+ end
124
+ end
@@ -0,0 +1,4 @@
1
+ require "../src/post_process_crystal"
2
+ require "spec"
3
+ require "spectator"
4
+ require "mocks/spec"
@@ -0,0 +1,16 @@
1
+ require "./post_process_crystal"
2
+
3
+ if ARGV.size == 0
4
+ STDERR.puts "Usage: #{PROGRAM_NAME} <files>"
5
+ exit 1
6
+ end
7
+
8
+ ppc = PostProcessCrystal.new
9
+ puts "Processing #~# BEGIN and #~# END comments..."
10
+
11
+ ARGV.each_with_index do |arg, i|
12
+ puts "=> Processing file: #{arg}"
13
+ ppc.filename = arg
14
+ ppc.post_process_crystal
15
+ File.write(arg, ppc.contents)
16
+ end
@@ -0,0 +1,48 @@
1
+ class PostProcessCrystal
2
+ getter :filename, :contents
3
+ @filename : String
4
+ @contents : String
5
+
6
+ def self.file_read_lines(path)
7
+ File.read_lines(path)
8
+ end
9
+
10
+ def initialize(filename : String = "")
11
+ @filename = filename
12
+ @contents = ""
13
+ end
14
+
15
+ def filename=(filename)
16
+ self.initialize(filename)
17
+ end
18
+
19
+ def post_process_crystal
20
+ @contents = String.build do |io|
21
+ lines = self.class.file_read_lines(@filename)
22
+
23
+ current_lang = nil
24
+ regex = /^\s*# ?~# (?<action>(BEGIN|END)) (?<lang>(ruby|crystal))/
25
+ uncomment_regex = /^(?<indent>\s*)# ?/
26
+ lines.each do |line|
27
+ matches = regex.match(line)
28
+ if matches
29
+ case matches["action"]
30
+ when "BEGIN"
31
+ current_lang = matches["lang"]
32
+ when "END"
33
+ current_lang = nil
34
+ end
35
+ next
36
+ end
37
+ case current_lang
38
+ when "ruby"
39
+ next
40
+ when "crystal"
41
+ line = line.sub(uncomment_regex, "\\k<indent>")
42
+ end
43
+
44
+ io << line << "\n"
45
+ end
46
+ end
47
+ end
48
+ end
metadata ADDED
@@ -0,0 +1,208 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby_crystal_codemod
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ary Borenszweig
8
+ - Nathan Broadbent
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2019-12-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: awesome_print
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 1.8.0
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 1.8.0
28
+ - !ruby/object:Gem::Dependency
29
+ name: bundler
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '1.15'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '1.15'
42
+ - !ruby/object:Gem::Dependency
43
+ name: byebug
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: 10.0.2
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: 10.0.2
56
+ - !ruby/object:Gem::Dependency
57
+ name: guard-rspec
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '4.0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '4.0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: rake
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '10.0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '10.0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: rspec
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '3.0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '3.0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: rspec_junit_formatter
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: 0.4.1
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: 0.4.1
112
+ - !ruby/object:Gem::Dependency
113
+ name: rubocop
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: 0.63.1
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: 0.63.1
126
+ - !ruby/object:Gem::Dependency
127
+ name: rufo
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - "~>"
131
+ - !ruby/object:Gem::Version
132
+ version: 0.7.0
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - "~>"
138
+ - !ruby/object:Gem::Version
139
+ version: 0.7.0
140
+ description: Attempts to transpile Ruby code into Crystal code
141
+ email:
142
+ - asterite@gmail.com
143
+ - nathan@docspring.com
144
+ executables:
145
+ - ruby_crystal_codemod
146
+ extensions: []
147
+ extra_rdoc_files: []
148
+ files:
149
+ - ".circleci/config.yml"
150
+ - ".github/PULL_REQUEST_TEMPLATE.md"
151
+ - ".gitignore"
152
+ - ".rspec"
153
+ - ".rubocop.yml"
154
+ - ".rufo"
155
+ - ".vscode/settings.json"
156
+ - Gemfile
157
+ - Guardfile
158
+ - LICENSE.txt
159
+ - README.md
160
+ - Rakefile
161
+ - bin/compile_post_process
162
+ - bin/console
163
+ - bin/setup
164
+ - docs/developing-ruby-crystal-codemod.md
165
+ - docs/releasing-a-gem.md
166
+ - exe/ruby_crystal_codemod
167
+ - lib/ruby_crystal_codemod.rb
168
+ - lib/ruby_crystal_codemod/command.rb
169
+ - lib/ruby_crystal_codemod/dot_file.rb
170
+ - lib/ruby_crystal_codemod/file_finder.rb
171
+ - lib/ruby_crystal_codemod/formatter.rb
172
+ - lib/ruby_crystal_codemod/logger.rb
173
+ - lib/ruby_crystal_codemod/settings.rb
174
+ - lib/ruby_crystal_codemod/version.rb
175
+ - rakelib/ruby_crystal_codemod.rake
176
+ - ruby_crystal_codemod.gemspec
177
+ - run_test
178
+ - util/post_process_crystal/shard.lock
179
+ - util/post_process_crystal/shard.yml
180
+ - util/post_process_crystal/spec/fixtures/example.rb
181
+ - util/post_process_crystal/spec/post_process_crystal_spec.cr
182
+ - util/post_process_crystal/spec/spec_helper.cr
183
+ - util/post_process_crystal/src/command.cr
184
+ - util/post_process_crystal/src/post_process_crystal.cr
185
+ homepage: https://github.com/docspring/ruby_crystal_codemod
186
+ licenses:
187
+ - MIT
188
+ metadata: {}
189
+ post_install_message:
190
+ rdoc_options: []
191
+ require_paths:
192
+ - lib
193
+ required_ruby_version: !ruby/object:Gem::Requirement
194
+ requirements:
195
+ - - ">="
196
+ - !ruby/object:Gem::Version
197
+ version: 2.4.5
198
+ required_rubygems_version: !ruby/object:Gem::Requirement
199
+ requirements:
200
+ - - ">="
201
+ - !ruby/object:Gem::Version
202
+ version: '0'
203
+ requirements: []
204
+ rubygems_version: 3.0.3
205
+ signing_key:
206
+ specification_version: 4
207
+ summary: Ruby => Crystal codemod
208
+ test_files: []