11# frozen_string_literal: true
22
3- require_relative "diff/version"
4- require_relative "diff/file_tracker"
53require "rails"
6- require "thor"
74require "diffy"
85require "fileutils"
96require "open3"
7+ require_relative "diff/cli"
8+ require_relative "diff/file_tracker"
9+ require_relative "diff/logger"
10+ require_relative "diff/rails_app_generator"
11+ require_relative "diff/rails_repo"
12+ require_relative "diff/version"
1013
1114module Rails
1215 module Diff
13- class Error < StandardError ; end
14-
15- RAILS_REPO = "https://github.com/rails/rails.git"
1616 CACHE_DIR = File . expand_path ( "#{ ENV [ "HOME" ] } /.rails-diff/cache" )
17- RAILSRC_PATH = "#{ ENV [ "HOME" ] } /.railsrc"
1817
1918 class << self
2019 def file ( *files , no_cache : false , commit : nil , new_app_options : nil )
21- clear_cache if no_cache
22- ensure_template_app_exists ( commit , new_app_options )
20+ app_generator = RailsAppGenerator . new ( commit : , new_app_options : , no_cache : )
21+ app_generator . create_template_app
2322
24- files . filter_map { |it | diff_with_header ( it ) } . join ( "\n " )
23+ files
24+ . filter_map { |it | diff_with_header ( it , app_generator . template_app_path ) }
25+ . join ( "\n " )
2526 end
2627
2728 def generated ( generator_name , *args , no_cache : false , skip : [ ] , only : [ ] , commit : nil , new_app_options : nil )
28- clear_cache if no_cache
29- ensure_template_app_exists ( commit , new_app_options )
30- install_app_dependencies
29+ app_generator = RailsAppGenerator . new ( commit : , new_app_options : , no_cache : )
30+ app_generator . create_template_app
31+ app_generator . install_app_dependencies
3132
32- generated_files ( generator_name , *args , skip , only )
33- . map { |it | diff_with_header ( it ) }
33+ app_generator . run_generator ( generator_name , *args , skip , only )
34+ . map { |it | diff_with_header ( it , app_generator . template_app_path ) }
3435 . join ( "\n \n " )
3536 end
3637
37- private
38-
39- def system! ( *cmd , abort : true )
38+ def system! ( *cmd , logger :, abort : true )
4039 _ , stderr , status = Open3 . capture3 ( *cmd )
41-
42- debug cmd . join ( " " )
43-
40+ logger . debug ( cmd . join ( " " ) )
4441 if status . success?
4542 true
4643 elsif abort
47- $stderr . puts " \e [1;31mCommand failed:\e [0m #{ cmd . join ( ' ' ) } "
44+ logger . error ( "Command failed:" , cmd . join ( " " ) )
4845 abort stderr
4946 else
5047 false
5148 end
5249 end
5350
54- def info ( message )
55- puts "\e [1;34minfo:\e [0m\t #{ message } "
56- end
57-
58- def debug ( message )
59- return unless ENV [ "DEBUG" ]
60-
61- puts "\e [1;33mdebug:\e [0m\t #{ message } "
62- end
63-
64- def clear_cache
65- info "Clearing cache"
66- FileUtils . rm_rf ( CACHE_DIR )
67- end
68-
69- def ensure_template_app_exists ( commit , new_app_options )
70- FileUtils . mkdir_p ( CACHE_DIR )
71- @new_app_options = new_app_options
72- @commit = commit || latest_commit
73- return if cached_app?
74-
75- create_new_rails_app
76- end
77-
78- def template_app_path
79- @template_app_path ||= File . join ( CACHE_DIR , "rails-#{ commit . first ( 10 ) } " , rails_new_options_hash , app_name )
80- end
81-
82- def rails_path
83- @rails_path ||= begin
84- File . join ( CACHE_DIR , "rails" ) . tap do |path |
85- unless File . exist? ( path )
86- info "Cloning Rails repository"
87- system! ( "git" , "clone" , "--depth" , "1" , RAILS_REPO , path )
88- end
89- end
90- end
91- end
92-
93- def railsrc_options
94- return @railsrc_options if defined? ( @railsrc_options )
95-
96- @railsrc_options = File . read ( RAILSRC_PATH ) . lines if File . exist? ( RAILSRC_PATH )
97- end
98-
99- def app_name = @app_name ||= File . basename ( Dir . pwd )
100-
101- def generated_files ( generator_name , *args , skip , only )
102- Dir . chdir ( template_app_path ) do
103- system! ( "bin/rails" , "destroy" , generator_name , *args )
104- info "Running generator: rails generate #{ generator_name } #{ args . join ( ' ' ) } "
105- FileTracker . new . track_new_files ( template_app_path , skip , only ) { system! ( "bin/rails" , "generate" , generator_name , *args ) }
106- . map { |it | it . delete_prefix ( "#{ template_app_path } /" ) }
107- end
108- end
51+ private
10952
110- def diff_with_header ( file )
111- diff = diff_file ( file )
53+ def diff_with_header ( file , template_app_path )
54+ diff = diff_file ( file , template_app_path )
11255 return if diff . empty?
11356
11457 header = "#{ file } diff:"
11558 [ header , "=" * header . size , diff ] . join ( "\n " )
11659 end
11760
118- def install_app_dependencies
119- Dir . chdir ( template_app_path ) do
120- unless system! ( "bundle check" , abort : false )
121- info "Installing application dependencies"
122- system! ( "bundle install" )
123- end
124- end
125- end
126-
127- def diff_file ( file )
61+ def diff_file ( file , template_app_path )
12862 rails_file = File . join ( template_app_path , file )
12963 repo_file = File . join ( Dir . pwd , file )
13064
@@ -135,129 +69,9 @@ def diff_file(file)
13569 rails_file ,
13670 repo_file ,
13771 context : 2 ,
138- source : ' files'
72+ source : " files"
13973 ) . to_s ( :color ) . chomp
14074 end
141-
142- def cached_app?
143- File . exist? ( template_app_path ) && !out_of_date_rails?
144- end
145-
146- def out_of_date_rails?
147- return true unless File . exist? ( rails_path )
148-
149- Dir . chdir ( rails_path ) do
150- system! ( "git fetch origin main" )
151- current = `git rev-parse HEAD` . strip
152- latest = `git rev-parse origin/main` . strip
153-
154- if current != latest
155- FileUtils . rm_rf ( rails_path )
156- return true
157- end
158- end
159-
160- false
161- end
162-
163- def create_new_rails_app
164- Dir . chdir ( rails_path ) do
165- checkout_rails
166- generate_app
167- end
168- end
169-
170- def generate_app
171- Dir . chdir ( "railties" ) do
172- unless system! ( "bundle check" , abort : false )
173- info "Installing Rails dependencies"
174- system! ( "bundle install" )
175- end
176-
177- if railsrc_options
178- info "Using default options from #{ RAILSRC_PATH } :\n \t > #{ railsrc_options . join ( ' ' ) } "
179- end
180-
181- info "Generating new Rails application\n \t > #{ rails_new_command . join ( ' ' ) } "
182- system! ( *rails_new_command )
183- end
184- end
185-
186- def checkout_rails
187- info "Checking out Rails (at commit #{ commit [ 0 ..6 ] } )"
188- system! ( "git" , "checkout" , commit )
189- end
190-
191- def commit = @commit
192-
193- def new_app_options = @new_app_options
194-
195- def latest_commit
196- Dir . chdir ( rails_path ) do
197- `git rev-parse origin/main` . strip
198- end
199- end
200-
201- def rails_new_command = @rails_new_command ||= [
202- "bundle" ,
203- "exec" ,
204- "rails" ,
205- "new" ,
206- template_app_path ,
207- "--main" ,
208- "--skip-bundle" ,
209- "--force" ,
210- "--quiet" ,
211- *rails_new_options
212- ]
213-
214- def rails_new_options = @rails_new_options ||= [ *new_app_options , *railsrc_options ] . compact
215-
216- def rails_new_options_hash = Digest ::MD5 . hexdigest ( rails_new_options . join ( " " ) )
217- end
218-
219- class CLI < Thor
220- class_option :no_cache , type : :boolean , desc : "Clear cache before running" , aliases : [ "--clear-cache" ]
221- class_option :fail_on_diff , type : :boolean , desc : "Fail if there are differences"
222- class_option :commit , type : :string , desc : "Compare against a specific commit"
223- class_option :new_app_options , type : :string , desc : "Options to pass to the rails new command"
224- class_option :debug , type : :boolean , desc : "Print debug information" , aliases : [ "-d" ]
225-
226- def self . exit_on_failure? = true
227-
228- desc "file FILE [FILE ...]" , "Compare one or more files from your repository with Rails' generated version"
229- def file ( *files )
230- abort "Please provide at least one file to compare" if files . empty?
231- ENV [ "DEBUG" ] = "true" if options [ :debug ]
232-
233- diff = Rails ::Diff . file ( *files , no_cache : options [ :no_cache ] , commit : options [ :commit ] , new_app_options : options [ :new_app_options ] )
234- return if diff . empty?
235-
236- options [ :fail ] ? abort ( diff ) : puts ( diff )
237- end
238-
239- desc "generated GENERATOR [args]" , "Compare files that would be created by a Rails generator"
240- option :skip , type : :array , desc : "Skip specific files or directories" , aliases : [ "-s" ] , default : [ ]
241- option :only , type : :array , desc : "Only include specific files or directories" , aliases : [ "-o" ] , default : [ ]
242- def generated ( generator_name , *args )
243- ENV [ "DEBUG" ] = "true" if options [ :debug ]
244- diff = Rails ::Diff . generated ( generator_name ,
245- *args ,
246- no_cache : options [ :no_cache ] ,
247- skip : options [ :skip ] ,
248- only : options [ :only ] ,
249- commit : options [ :commit ] ,
250- new_app_options : options [ :new_app_options ] )
251- return if diff . empty?
252-
253- options [ :fail ] ? abort ( diff ) : puts ( diff )
254- end
255-
256- map %w[ --version -v ] => :__version
257- desc "--version, -v" , "print the version"
258- def __version
259- puts VERSION
260- end
26175 end
26276 end
26377end
0 commit comments