rails gem なしで rails new する

Rails のプロジェクトを作る、つまり rails new 実行するためだけに rails gem をグローバス にいれていたりもするのだけど、継続的にバージョンアップしたりしないし、rbenv で新しい ruby を入れるたびにインストールし直すのはやはりだるい(というか実行できなくてようやくきずく)。

のでグローバルに rails gem なしで rails new する雑スクリプトを用意しておいて dotfiles に入れておく。

#!/usr/bin/env ruby
#
# rails-new: Run `rails new` with no rails gem
#

require 'optparse'
require 'pathname'

opt = OptionParser.new
opt.banner = 'Usage: rails-new <app_path> [-h] [options]'
opt.on('-h', '--help', 'Print this help') do
  puts opt
  exit
end

app_path = Pathname.new(ARGV.first)

rails_new_options = []
begin
  opt.parse!
rescue OptionParser::InvalidOption => e
  rails_new_options << e.args[0]
  retry
end

abort "#{app_path} directory exists." if app_path.exist?
app_path.mkpath
Dir.chdir(app_path)

File.open('Gemfile', 'w') do |f|
  f << <<~EOF
    source "https://rubygems.org"
    gem "rails"
  EOF
end

def msg(str)
  puts "\e[1;38;5;215m⚡ #{str}\e[0m"
end

msg 'Prepare to run rails new...'
system('bundle', 'install', '--quiet', exception: true)

msg "rails new #{rails_new_options.join(' ')}"
system('bundle', 'exec', 'rails', 'new', '.', '--force', *rails_new_options, exception: true)

rails new に渡すオプションはこのスクリプトでは処理せずに pass through して渡すようにして、このスクリプト自体をいじらなくても最新のオプションが使えるようにしておく。

➜ rails-new test-app --minimal
⚡ Prepare to run rails new...
⚡ rails new --minimal
       exist
      create  README.md
      create  Rakefile
      create  .ruby-version
      create  config.ru
      create  .gitignore
      create  .gitattributes
       force  Gemfile
         run  git init from "."
  .
  .

Ruby の OptionParser は pass through するオプションがないので rails new に任せるオプションは InvalidOption を拾い集める必要があるのがちょっとあれなのだけど、ググる限りこういうやり方が多いっぽい。