ruby-reforge 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ab4619d1d1f5500df76531c2c3483b1ac972c2b513e5a2ac9017a271219ad605
4
+ data.tar.gz: a4c5435975e097dd6e3241ddc4122fb6e9830bd55e9101a987edbb6f3b392a37
5
+ SHA512:
6
+ metadata.gz: 106c1ed7177fc8010264651a160cf50434f5768762a6cd1ed1536c373464e1f8f32950f0e5d815fcf0b393070a48719ba3bca1e84ddf0b98c84f200676ea32a4
7
+ data.tar.gz: cf2e590eaa44a0d37aff8081d1dc32a338c817be793a88c6c355db628752288e9a2ede674d924270c1e683a6eaf0fd47f8d74be5a52646c4f974c94c439847d1
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --require spec_helper
2
+ --color
3
+ --format documentation
4
+
data/.ruby-version ADDED
@@ -0,0 +1,2 @@
1
+ 3.1.0
2
+
data/CHANGELOG.md ADDED
@@ -0,0 +1,27 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2024-01-XX
9
+
10
+ ### Added
11
+ - Initial release
12
+ - CLI interface with `upgrade`, `report`, and `ai-fix` commands
13
+ - Automatic version file updates (`.ruby-version`, `Gemfile`, `gemspec`)
14
+ - Code scanner using Prism for AST parsing
15
+ - Migration rules for Ruby 3.0, 3.1, 3.2, and 3.3
16
+ - Rails-specific deprecation rules
17
+ - Interactive mode for reviewing changes
18
+ - Dry-run mode
19
+ - Git integration (branch creation and commits)
20
+ - Detailed reporting with colorized output
21
+
22
+ ### Features
23
+ - Detects and fixes deprecated methods (`File.exists?`, `Dir.exists?`, etc.)
24
+ - Pattern-based code rewriting
25
+ - Rails filter to action migrations
26
+ - Comprehensive upgrade reports
27
+
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in ruby-reforge.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ group :development, :test do
11
+ gem "rspec", "~> 3.12"
12
+ gem "rubocop", "~> 1.50"
13
+ end
14
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Ruby Reforge Team
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 all
13
+ 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 THE
21
+ SOFTWARE.
22
+
data/QUICKSTART.md ADDED
@@ -0,0 +1,77 @@
1
+ # Quick Start Guide
2
+
3
+ ## Building the Gem
4
+
5
+ ```bash
6
+ cd ruby-reforge
7
+ gem build ruby-reforge.gemspec
8
+ ```
9
+
10
+ This will create a `ruby-reforge-0.1.0.gem` file.
11
+
12
+ ## Installing Locally
13
+
14
+ ```bash
15
+ gem install ./ruby-reforge-0.1.0.gem
16
+ ```
17
+
18
+ Or install from the local directory:
19
+
20
+ ```bash
21
+ bundle install
22
+ bundle exec ruby-reforge --help
23
+ ```
24
+
25
+ ## Development Setup
26
+
27
+ ```bash
28
+ bundle install
29
+ bundle exec rspec # Run tests
30
+ ```
31
+
32
+ ## Usage Examples
33
+
34
+ ### Generate a report
35
+
36
+ ```bash
37
+ ruby-reforge report
38
+ ```
39
+
40
+ ### Upgrade to Ruby 3.3
41
+
42
+ ```bash
43
+ ruby-reforge upgrade 3.3
44
+ ```
45
+
46
+ ### Interactive mode
47
+
48
+ ```bash
49
+ ruby-reforge upgrade 3.3 --interactive
50
+ ```
51
+
52
+ ### Dry run (see what would change)
53
+
54
+ ```bash
55
+ ruby-reforge upgrade 3.3 --dry-run
56
+ ```
57
+
58
+ ## Project Structure
59
+
60
+ ```
61
+ ruby-reforge/
62
+ ├── lib/
63
+ │ └── ruby/
64
+ │ └── reforge/
65
+ │ ├── cli.rb # CLI interface
66
+ │ ├── scanner.rb # Code scanner
67
+ │ ├── rewriter.rb # Code rewriter
68
+ │ ├── version_updater.rb # Version file updates
69
+ │ ├── migration_rules.rb # Ruby version rules
70
+ │ ├── rails_rules.rb # Rails-specific rules
71
+ │ ├── reporter.rb # Report generator
72
+ │ └── git_integration.rb # Git operations
73
+ ├── exe/
74
+ │ └── ruby-reforge # Executable
75
+ ├── spec/ # Tests
76
+ └── ruby-reforge.gemspec # Gem specification
77
+ ```
data/README.md ADDED
@@ -0,0 +1,115 @@
1
+ # Ruby Reforge
2
+
3
+ 🚀 Automatically upgrade Ruby projects to newer versions and fix deprecated code.
4
+
5
+ ## Overview
6
+
7
+ `ruby-reforge` is a gem that scans a Ruby or Rails project, detects incompatible code for a target Ruby version (e.g., upgrading from 3.1 → 3.3), and automatically rewrites or suggests fixes.
8
+
9
+ ## Features
10
+
11
+ - ✅ Updates `.ruby-version`, `Gemfile`, and `gemspec` files
12
+ - ✅ Fixes deprecated Ruby syntax automatically
13
+ - ✅ Detects and fixes removed features
14
+ - ✅ Rewrites code for new Ruby syntax
15
+ - ✅ Generates detailed upgrade reports
16
+ - ✅ Interactive mode for reviewing changes
17
+ - ✅ Git integration (creates branches, commits changes)
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ gem install ruby-reforge
23
+ ```
24
+
25
+ Or add it to your Gemfile:
26
+
27
+ ```ruby
28
+ gem 'ruby-reforge', group: :development
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ ### Upgrade to a new Ruby version
34
+
35
+ ```bash
36
+ ruby-reforge upgrade 3.3
37
+ ```
38
+
39
+ This will:
40
+ 1. Update version files (`.ruby-version`, `Gemfile`, `gemspec`)
41
+ 2. Scan your codebase for deprecated patterns
42
+ 3. Automatically fix issues
43
+ 4. Create a git branch and commit changes
44
+
45
+ ### Generate a report
46
+
47
+ ```bash
48
+ ruby-reforge report
49
+ ```
50
+
51
+ Shows what needs to be fixed without making changes.
52
+
53
+ ### Interactive mode
54
+
55
+ ```bash
56
+ ruby-reforge upgrade 3.3 --interactive
57
+ ```
58
+
59
+ Prompts before making each change.
60
+
61
+ ### Dry run
62
+
63
+ ```bash
64
+ ruby-reforge upgrade 3.3 --dry-run
65
+ ```
66
+
67
+ Shows what would be changed without making any changes.
68
+
69
+ ## What It Fixes
70
+
71
+ ### Deprecated Methods
72
+
73
+ - `File.exists?` → `File.exist?`
74
+ - `Dir.exists?` → `Dir.exist?`
75
+ - `URI.escape` → `CGI.escape`
76
+ - And more...
77
+
78
+ ### Version Files
79
+
80
+ - Updates `.ruby-version`
81
+ - Updates `Gemfile` ruby version
82
+ - Updates `gemspec` required_ruby_version
83
+
84
+ ### Ruby 3.0+ Keyword Arguments
85
+
86
+ Detects and suggests fixes for keyword argument changes.
87
+
88
+ ## Examples
89
+
90
+ ```bash
91
+ # Upgrade to Ruby 3.3
92
+ ruby-reforge upgrade 3.3
93
+
94
+ # Generate report for Ruby 3.2
95
+ ruby-reforge report --target 3.2
96
+
97
+ # Interactive upgrade
98
+ ruby-reforge upgrade 3.3 --interactive
99
+ ```
100
+
101
+ ## Roadmap
102
+
103
+ - [ ] Rails-specific deprecations
104
+ - [ ] Gem dependency compatibility checker
105
+ - [ ] AI-assisted fixes for complex code
106
+ - [ ] Test framework updates
107
+ - [ ] Performance improvement suggestions
108
+
109
+ ## Contributing
110
+
111
+ Contributions are welcome! Please feel free to submit a Pull Request.
112
+
113
+ ## License
114
+
115
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
9
+
data/exe/ruby-reforge ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "ruby/reforge"
5
+
6
+ Ruby::Reforge::CLI.start(ARGV)
7
+
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor"
4
+ require "rainbow"
5
+ require "tty-prompt"
6
+
7
+ module Ruby
8
+ module Reforge
9
+ class CLI < Thor
10
+ include Thor::Actions
11
+
12
+ desc "upgrade VERSION", "Upgrade Ruby project to target version"
13
+ option :interactive, type: :boolean, aliases: "-i", desc: "Interactive mode - prompts before changes"
14
+ option :dry_run, type: :boolean, aliases: "-d", desc: "Show what would be changed without making changes"
15
+ option :branch, type: :string, desc: "Git branch name (default: upgrade/ruby-VERSION)"
16
+ def upgrade(version)
17
+ say "🚀 Ruby Reforge - Upgrading to Ruby #{version}", :green
18
+ say ""
19
+
20
+ target_version = normalize_version(version)
21
+ current_version = detect_current_ruby_version
22
+
23
+ if current_version == target_version
24
+ say "✓ Already on Ruby #{target_version}", :green
25
+ exit 0
26
+ end
27
+
28
+ say "Current Ruby version: #{current_version || 'unknown'}", :yellow
29
+ say "Target Ruby version: #{target_version}", :yellow
30
+ say ""
31
+
32
+ # Create git branch if in git repo
33
+ if git_integration.available?
34
+ branch_name = options[:branch] || "upgrade/ruby-#{target_version}"
35
+ git_integration.create_branch(branch_name) unless options[:dry_run]
36
+ end
37
+
38
+ # Scan for issues
39
+ say "🔍 Scanning codebase...", :cyan
40
+ issues = scanner.scan(target_version)
41
+
42
+ # Generate report
43
+ reporter.report(issues, current_version, target_version)
44
+
45
+ if options[:dry_run]
46
+ say "\n🔍 DRY RUN - No changes made", :yellow
47
+ exit 0
48
+ end
49
+
50
+ # Interactive mode
51
+ if options[:interactive]
52
+ prompt = TTY::Prompt.new
53
+ unless prompt.yes?("Proceed with automatic fixes?")
54
+ say "Aborted.", :red
55
+ exit 0
56
+ end
57
+ end
58
+
59
+ # Update version files
60
+ say "\n📝 Updating version files...", :cyan
61
+ version_updater.update(target_version)
62
+
63
+ # Apply fixes
64
+ say "\n🔧 Applying fixes...", :cyan
65
+ rewriter.rewrite(issues, interactive: options[:interactive])
66
+
67
+ # Commit changes
68
+ if git_integration.available? && !options[:dry_run]
69
+ git_integration.commit_version_files
70
+ git_integration.commit_code_changes
71
+ end
72
+
73
+ say "\n✅ Upgrade complete!", :green
74
+ say "\nNext steps:"
75
+ say " 1. Review the changes"
76
+ say " 2. Run your test suite"
77
+ say " 3. Update dependencies: bundle update"
78
+ say " 4. Check for any manual fixes needed"
79
+ end
80
+
81
+ desc "report", "Generate a report of deprecated code and upgrade issues"
82
+ option :target, type: :string, desc: "Target Ruby version (default: latest stable)"
83
+ def report
84
+ say "📊 Ruby Reforge - Generating Report", :green
85
+ say ""
86
+
87
+ current_version = detect_current_ruby_version
88
+ target_version = options[:target] ? normalize_version(options[:target]) : "3.3.0"
89
+
90
+ say "Current Ruby version: #{current_version || 'unknown'}", :yellow
91
+ say "Target Ruby version: #{target_version}", :yellow
92
+ say ""
93
+
94
+ say "🔍 Scanning codebase...", :cyan
95
+ issues = scanner.scan(target_version)
96
+
97
+ reporter.report(issues, current_version, target_version)
98
+ end
99
+
100
+ desc "ai-fix", "Use AI to fix complex deprecated code (experimental)"
101
+ option :model, type: :string, default: "local", desc: "AI model to use (local, openai, anthropic)"
102
+ def ai_fix
103
+ say "🤖 AI-Assisted Fix Mode (Experimental)", :green
104
+ say "This feature is coming soon!", :yellow
105
+ end
106
+
107
+ private
108
+
109
+ def normalize_version(version)
110
+ # Normalize version string (e.g., "3.3" -> "3.3.0")
111
+ parts = version.split(".").map(&:to_i)
112
+ parts << 0 while parts.size < 3
113
+ parts[0..2].join(".")
114
+ end
115
+
116
+ def detect_current_ruby_version
117
+ version_updater.detect_current_version
118
+ end
119
+
120
+ def scanner
121
+ @scanner ||= Scanner.new(source_root)
122
+ end
123
+
124
+ def rewriter
125
+ @rewriter ||= Rewriter.new(source_root)
126
+ end
127
+
128
+ def version_updater
129
+ @version_updater ||= VersionUpdater.new(source_root)
130
+ end
131
+
132
+ def reporter
133
+ @reporter ||= Reporter.new
134
+ end
135
+
136
+ def git_integration
137
+ @git_integration ||= GitIntegration.new(source_root)
138
+ end
139
+
140
+ def source_root
141
+ @source_root ||= Dir.pwd
142
+ end
143
+ end
144
+ end
145
+ end
146
+
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+
5
+ module Ruby
6
+ module Reforge
7
+ class GitIntegration
8
+ def initialize(root_path)
9
+ @root_path = root_path
10
+ end
11
+
12
+ def available?
13
+ git_dir = File.join(@root_path, ".git")
14
+ File.directory?(git_dir) && system("git --version > /dev/null 2>&1")
15
+ end
16
+
17
+ def create_branch(branch_name)
18
+ return unless available?
19
+
20
+ system("cd #{@root_path} && git checkout -b #{branch_name} 2>/dev/null") ||
21
+ system("cd #{@root_path} && git checkout #{branch_name} 2>/dev/null")
22
+ end
23
+
24
+ def commit_version_files
25
+ return unless available?
26
+
27
+ files = [".ruby-version", "Gemfile"]
28
+ files += Dir.glob(File.join(@root_path, "**/*.gemspec"))
29
+
30
+ staged_files = files.select { |f| File.exist?(File.join(@root_path, f)) }
31
+ return if staged_files.empty?
32
+
33
+ system("cd #{@root_path} && git add #{staged_files.join(' ')}")
34
+ system("cd #{@root_path} && git commit -m 'chore: update Ruby version files'")
35
+ end
36
+
37
+ def commit_code_changes
38
+ return unless available?
39
+
40
+ # Get list of modified Ruby files
41
+ modified = `cd #{@root_path} && git diff --name-only --diff-filter=M`.split("\n")
42
+ ruby_files = modified.select { |f| f.end_with?(".rb") }
43
+
44
+ return if ruby_files.empty?
45
+
46
+ system("cd #{@root_path} && git add #{ruby_files.join(' ')}")
47
+ system("cd #{@root_path} && git commit -m 'fix: update deprecated Ruby syntax'")
48
+ end
49
+ end
50
+ end
51
+ end
52
+
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ruby
4
+ module Reforge
5
+ class MigrationRules
6
+ RULES = {
7
+ "3.0.0" => {
8
+ deprecated_methods: {
9
+ "File.exists?" => "File.exist?",
10
+ "Dir.exists?" => "Dir.exist?",
11
+ "URI.escape" => "CGI.escape",
12
+ "URI.unescape" => "CGI.unescape"
13
+ },
14
+ deprecated_patterns: {
15
+ /File\.exists\?/ => "File.exist?",
16
+ /Dir\.exists\?/ => "Dir.exist?",
17
+ /URI\.escape/ => "CGI.escape",
18
+ /URI\.unescape/ => "CGI.unescape"
19
+ },
20
+ breaking_changes: [
21
+ "Keyword arguments are now separated from positional arguments"
22
+ ]
23
+ },
24
+ "3.1.0" => {
25
+ deprecated_methods: {
26
+ "File.exists?" => "File.exist?",
27
+ "Dir.exists?" => "Dir.exist?"
28
+ },
29
+ deprecated_patterns: {
30
+ /File\.exists\?/ => "File.exist?",
31
+ /Dir\.exists\?/ => "Dir.exist?"
32
+ }
33
+ },
34
+ "3.2.0" => {
35
+ deprecated_methods: {
36
+ "File.exists?" => "File.exist?",
37
+ "Dir.exists?" => "Dir.exist?"
38
+ },
39
+ deprecated_patterns: {
40
+ /File\.exists\?/ => "File.exist?",
41
+ /Dir\.exists\?/ => "Dir.exist?"
42
+ }
43
+ },
44
+ "3.3.0" => {
45
+ deprecated_methods: {
46
+ "File.exists?" => "File.exist?",
47
+ "Dir.exists?" => "Dir.exist?"
48
+ },
49
+ deprecated_patterns: {
50
+ /File\.exists\?/ => "File.exist?",
51
+ /Dir\.exists\?/ => "Dir.exist?"
52
+ }
53
+ }
54
+ }.freeze
55
+
56
+ def self.for_version(version)
57
+ # Find the most appropriate rule set
58
+ target = normalize_version(version)
59
+ rule_key = RULES.keys.select { |k| normalize_version(k) <= target }.max || RULES.keys.first
60
+ new(RULES[rule_key] || {})
61
+ end
62
+
63
+ def self.normalize_version(version)
64
+ parts = version.to_s.split(".").map(&:to_i)
65
+ parts << 0 while parts.size < 3
66
+ parts[0..2].join(".")
67
+ end
68
+
69
+ attr_reader :deprecated_methods, :deprecated_patterns, :breaking_changes
70
+
71
+ def initialize(rules = {})
72
+ @deprecated_methods = rules[:deprecated_methods] || {}
73
+ @deprecated_patterns = rules[:deprecated_patterns] || {}
74
+ @breaking_changes = rules[:breaking_changes] || []
75
+ end
76
+ end
77
+ end
78
+ end
79
+
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ruby
4
+ module Reforge
5
+ class RailsRules
6
+ RAILS_DEPRECATIONS = {
7
+ "before_filter" => "before_action",
8
+ "after_filter" => "after_action",
9
+ "around_filter" => "around_action",
10
+ "skip_before_filter" => "skip_before_action",
11
+ "skip_after_filter" => "skip_after_action",
12
+ "skip_around_filter" => "skip_around_action",
13
+ "update_attributes" => "update",
14
+ "update_attributes!" => "update!",
15
+ "render :text" => "render plain:",
16
+ "render :nothing" => "head :ok"
17
+ }.freeze
18
+
19
+ RAILS_PATTERNS = {
20
+ /before_filter/ => "before_action",
21
+ /after_filter/ => "after_action",
22
+ /around_filter/ => "around_action",
23
+ /skip_before_filter/ => "skip_before_action",
24
+ /skip_after_filter/ => "skip_after_action",
25
+ /skip_around_filter/ => "skip_around_action",
26
+ /\.update_attributes\(/ => ".update(",
27
+ /\.update_attributes!/ => ".update!",
28
+ /render\s+:text\s*=>/ => "render plain:",
29
+ /render\s+:nothing/ => "head :ok"
30
+ }.freeze
31
+
32
+ def self.deprecated_methods
33
+ RAILS_DEPRECATIONS
34
+ end
35
+
36
+ def self.deprecated_patterns
37
+ RAILS_PATTERNS
38
+ end
39
+
40
+ def self.detect_rails_project?(root_path)
41
+ File.exist?(File.join(root_path, "config", "application.rb")) ||
42
+ File.exist?(File.join(root_path, "Gemfile")) && File.read(File.join(root_path, "Gemfile")).include?("rails")
43
+ end
44
+ end
45
+ end
46
+ end
47
+