give IT a try

プログラミング、リモートワーク、田舎暮らし、音楽、etc.

富山Ruby会議01で「7〜8年Rubyを使ってみて僕が感じていること」をお話ししてきました #toyamark

はじめに

2019年11月3日に富山Ruby会議01で「○○からRubyへ」という発表をしてきました。
タイトルの"○○"は「今メインで使っている(または使っていた)Ruby以外の言語」の意味です。
僕の場合だとJavaやC#が入ります。

このエントリではこの発表の内容を簡単に紹介します。
あと、発表の前日に妻と観光した金沢の話も書いておきます。

f:id:JunichiIto:20191105080333j:plain

発表スライド

当日使った発表スライドはこちらです。

構成としてはこんな感じになっています。

  • Ruby事始め(ポケベルプログラムのコーディング動画)
  • 他言語経験者が気になりそうなこと
    • 型がないと不安?
    • Rubyって遅いんでしょ?
  • Rubyの好きなところ
  • コミュニティの話

ターゲットになる参加者は「Rubyにちょっと興味があるけど、まだ本格的にRubyを使ったことはない人」です。
「もしRubyを使い始める前の自分が客席にいたら」を想定しながら、7〜8年Rubyを使ってみて僕が感じていることをお話ししました。

コーディング動画

Rubyを使ったことがない人は、まずRubyがどんな言語か知っていないと「Rubyってこうなんです」という話をしてもあまり伝わらないと思います。
また、僕の話だけでなく、そのあとの他の人の発表も「難しくてようわからん」ということになるかもしれません。
しかし、限られた時間の中でRubyの文法をあれこれ説明するのは現実的ではありません。

そこで今回は動画を使って簡単なRubyプログラムを作る様子を見せながら、Rubyの文法やライブラリ(gem)の導入方法などを説明することにしました。
当日使用した動画はYouTubeにアップしています。

○○からRubyへ・Ruby事始めコーディング動画 #toyamark

簡単なプログラムなので他言語経験者だけでなく、「最近Rubyでプログラミングを始めました」という人が見ても参考になるんじゃないかな〜と思います。

動画内では「ポケベルプログラム」を作りました

動画の中で作ったのは「ポケベルプログラム」です。

f:id:JunichiIto:20191105054135j:plain:w350
Image: https://time-space.kddi.com/digicul-column/bunka/20160701/

これはポケベルのカナ送信のように、数字を打ち込むとそれをカタカナに変換するプログラムです。
たとえば、11ならア、12ならイ、32ならシ、のように変換されます。

to_chars('11')
#=> ア

to_chars('12')
#=> イ

to_chars('1112324493')
#=> アイシテル
さらに:誰でもインストールして遊べます

また、このプログラムは"pokeberu"という名前でgemとしてもリリースしています。

pokeberu | RubyGems.org | your community gem host

Rubyがインストールされているパソコンのターミナル(またはコマンドプロンプト)で gem install pokeberu と打ち込むと、gemがインストールされます。

$ gem install pokeberu
Successfully installed pokeberu-0.1.4
Parsing documentation for pokeberu-0.1.4
Done installing documentation for pokeberu after 0 seconds
1 gem installed

それから pokeberu コマンドを実行すると、こんなふうにポケベルプログラムで遊ぶことができます。

f:id:JunichiIto:20191105081147g:plain

ポケベルを実際に触ったことがある人もない人も、一度pokeberu gemで遊んでみてください!📟

ちなみに、コードはこちらに置いてあります。
github.com

Twitter上の反応

発表の中ではポケベルプログラム以外の話もしゃべったのですが、全体的にこのコーディング動画が印象的だったようです😅

なお、当日のツイートは以下のページにまとめられています。
僕の発表は3ページ目から5ページ目付近になります。

togetter.com

登壇の裏話など

スライドにも書きましたが、富山Ruby会議01で「招待講演」として呼んでもらったのは、実は4年前にToyama.rbの麦島さんと出会ったのがきっかけです。

f:id:JunichiIto:20191105070028j:plain
f:id:JunichiIto:20191105070042j:plain

それ以来、ネット上ではちょくちょくやりとりしていたんですが、オフラインで顔を合わせるのは今回が4年ぶり2回目でした。

f:id:JunichiIto:20191105070356j:plain

遠く離れた兵庫県と富山県のITエンジニアを引き寄せてくれたRubyというプログラミング言語に感謝です🙏

ただ、この日は用事があって午前中で自宅に戻らなければならず、最後まで参加することができませんでした。
面白そうな発表がたくさんあったのに、とても残念です😣

おまけ:金沢観光あれこれ

富山Ruby会議01の前日は妻と一緒に金沢観光をしてきました。

f:id:JunichiIto:20191105073121j:plain
浅野川大橋と私

f:id:JunichiIto:20191105073051j:plain
「たかしま」というお店で何年かぶりの「回らない鮨」を堪能

f:id:JunichiIto:20191105073059j:plainf:id:JunichiIto:20191105074054j:plain
f:id:JunichiIto:20191105073054j:plainf:id:JunichiIto:20191105074332j:plain
どのお寿司も超美味しかった!!

f:id:JunichiIto:20191105073104j:plain
ひがし茶屋町でぶらぶらお散歩

f:id:JunichiIto:20191105073108j:plain
金沢城の前で記念撮影

f:id:JunichiIto:20191105073112j:plain
この日は快晴で暑くもなく寒くもなく、最高のお天気でした☀️

f:id:JunichiIto:20191105074320j:plain
こちらは「ル ミュゼ ドゥ アッシュ」というお店でいただいたケーキです

f:id:JunichiIto:20191105073128j:plain
兼六園の周辺も、とっても風情があります

f:id:JunichiIto:20191105073116j:plain
夜は金沢駅付近を歩いていました

金沢の観光情報については、金沢出身のITエンジニア、りほやん(@rllllho)がお役立ち情報を教えてくれました。
りほやん、どうもありがとう!!

まとめ

というわけで、このエントリでは富山Ruby会議01での僕の発表内容と、金沢観光のお話を書いてみました。
金沢ではゆっくりできたのですが、北陸のエンジニアのみなさんとの交流や富山観光はほとんどできずじまいだったのが、唯一の心残りです😭

あと、富山市内にはBlue Guitarsという結構通好みなギター屋さんもあるので、ここにも行ってみたかったんですが・・・。
gctoyama.kaishindo-music.co.jp

今回達成できなかった目的に関しては、また次の機会にリベンジしたいと思います!

最後に、僕の発表を聞いてくださったみなさんと、麦島さんをはじめ、富山Ruby会議01を開催してくれたみなさんに感謝したいと思います。
どうもありがとうございました!

f:id:JunichiIto:20191105080405j:plain
みなさん、ありがとうございました!😊

あわせて読みたい

僕が発表をするときに心がけている点をまとめたエントリです。
よかったらこちらの記事もどうぞ。

blog.jnito.com

【動画付き】Everyday RailsのサンプルアプリをRails 6で動かす際に必要なテストコードの変更点

はじめに

前々回のブログでは、2019年10月時点における「Everyday Rails - RSpecによるRailsテスト入門」(以下、Everyday Rails)のサンプルアプリケーションの環境セットアップ手順を紹介しました。

blog.jnito.com

ただし、この手順に従ってもサンプルアプリの実行環境はRails 5.1.1、rspec-rails 3.8.0にとどまります。 2019年10月時点での最新環境はRails 6.0.0、rspec-rails 3.9.0です。

そこで、このエントリでは Everyday Railsのサンプルアプリを最新のRails環境にアップデートした際に発生する、テストコードの変更点を紹介します。

【もくじ】

対象となる実行環境について

あらためて実行環境の情報をまとめておきます。

変更前の環境 変更後の環境 *
Rails 5.1.1 6.0.0
rspec-rails 3.8.0 4.0.0.beta2
(正式版は3.9.0)
Ruby 2.4.9 2.6.5
その他のgem - できる限り最新化

* 2019年10月時点の最新環境

この記事では上の表のように、GitHubのこのコミット(8b3800f)を2019年10月時点の最新の環境にアップグレードすることを想定します。

また、ここには細かく書きませんが、サンプルアプリケーションが依存する各種ライブラリ(gem)もRails 6で動作するよう、最新版にアップデートします。

実行環境を最新化する手順について

Everyday Railsのサンプルアプリケーションを最新化するためには、順を追って作業を進めていく必要があります。 ここでは以前僕がQiitaに書いた以下の記事に従ってアップグレードを進めるものとします。

qiita.com

また、アップグレードに伴う一連の作業は動画としてもアップしているので、こちらも参考にしてみてください。 (前編、後編、各30分)

【前編】永久保存版!?伊藤さん式・Railsアプリのアップグレード手順

【後編】永久保存版!?伊藤さん式・Railsアプリのアップグレード手順

Rails 6環境で発生するテストコード関連の変更点

実行環境をRails 6にアップグレードすると(そしてRailsだけでなく依存するgemを一通り最新版にアップデートすると)、Everyday Railsのサンプルアプリケーションのテストコードは以下のような変更作業が必要になります。

chromedriver-helperをwebdriversに変更する

chromedriver-helper gemは開発が終了してしまったので、webdrivers gemに変更します。

 # Gemfile
-gem 'chromedriver-helper'
+gem 'webdrivers'

chromedriver-helper gemを使い続けていると、gemをアップデート(bundle update)した際に以下のメッセージが表示されます。

Post-install message from chromedriver-helper:

  +--------------------------------------------------------------------+
  |                                                                    |
  |  NOTICE: chromedriver-helper is deprecated after 2019-03-31.       |
  |                                                                    |
  |  Please update to use the 'webdrivers' gem instead.                |
  |  See https://github.com/flavorjones/chromedriver-helper/issues/83  |
  |                                                                    |
  +--------------------------------------------------------------------+

参考 サポートが終了したchromedriver-helperからwebdrivers gemに移行する手順 - Qiita

また、この変更にあわせて、spec/support/vcr.rbignore_hosts の設定を追加します。

 # spec/support/vcr.rb

 config.ignore_localhost = true
+config.ignore_hosts 'chromedriver.storage.googleapis.com'
 config.configure_rspec_metadata!

この設定がないと、テスト実行時に以下のようなエラーが発生します。

VCR::Errors::UnhandledHTTPRequestError:


  ================================================================================
  An HTTP request has been made that VCR does not know how to handle:
    GET https://chromedriver.storage.googleapis.com/LATEST_RELEASE_77.0.3865
  
  (中略)
  ================================================================================
# ./spec/system/tasks_spec.rb:3:in `<main>'
# -e:1:in `<main>'

shoulda-matchersのブランチ指定を解除する

Everyday Railsのサンプルコードが作成された当時は shoulda-matchers gemは rails-5 ブランチを指定していましたが、現在はブランチを指定せずに最新版をインストールすれば大丈夫です。(バージョン4.1.0以上であればRails 6もサポート済み)

 # Gemfile
-gem 'shoulda-matchers',
-  git: 'https://github.com/thoughtbot/shoulda-matchers.git',
-  branch: 'rails-5'
+gem 'shoulda-matchers'

factory_botの記法を変更する

factory_bot 4.11からファクトリの記法が次のように変わっています。

# 4.10以前
factory :user do
  first_name "Aaron"
end

# 4.11以降(ブロックの { } が必要)
factory :user do
  first_name { "Aaron" }
end

記法が古いと、テスト実行時に以下のようなエラーが発生します。

/Users/jnito/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/factory_bot-5.1.1/lib/factory_bot/definition_proxy.rb:99:in `method_missing': undefined method 'first_name' in 'user' factory
Did you mean? 'first_name { "Aaron" }'
 (NoMethodError)
    from /Users/jnito/dev/sandbox/everydayrails-rspec-2017/spec/factories/users.rb:3:in `block (2 levels) in <main>'
    from /Users/jnito/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/factory_bot-5.1.1/lib/factory_bot/syntax/default.rb:18:in `instance_eval'
    (以下略)

ただし、既存のファクトリをいちいち手で書き換える必要はありません。以下のコマンドを実行すれば自動的に書き換えてくれます。

# rubocop-rspec gemをインストール
$ gem install rubocop-rspec

# 自動修正コマンドを実行
$ rubocop --require rubocop-rspec --only FactoryBot/AttributeDefinedStatically --auto-correct

参考 【翻訳】factory_bot 4.11で非推奨になった静的属性(static attributes) - Qiita

rspec-railsは4.0.0.beta2をインストールする

2019年10月時点ではrspec-railsがまだ完全にRails 6に対応していません。正式対応が完了するまではbeta版を使用します。

 # Gemfile
-gem 'rspec-rails', '~> 3.8.0'
+gem 'rspec-rails', '~> 4.0.0.beta2'

なお、Rails 6とrspec-rails 3.9.0を組み合わせると、以下のようなエラーや警告が発生します(実際に発生するエラーから一部抜粋)。

DEPRECATION WARNING: formats is deprecated and will be removed from Rails 6.1 (called from block (3 levels) in <main> at /Users/jnito/dev/sandbox/everydayrails-rspec-2017/spec/controllers/home_controller_spec.rb:6)
DEPRECATION WARNING: ActionView::Template#initialize requires a locals parameter (called from block (3 levels) in <main> at /Users/jnito/dev/sandbox/everydayrails-rspec-2017/spec/controllers/home_controller_spec.rb:6)

ActionView::Template::Error: wrong number of arguments (given 2, expected 1)

  0) HomeController returns a 200 response
     Failure/Error: get :index

     ActionView::Template::Error:
       wrong number of arguments (given 2, expected 1)
     # ./spec/controllers/home_controller_spec.rb:12:in `block (2 levels) in <main>'
     # -e:1:in `<main>'
     # ------------------
     # --- Caused by: ---
     # ArgumentError:
     #   wrong number of arguments (given 2, expected 1)
     #   ./spec/controllers/home_controller_spec.rb:12:in `block (2 levels) in <main>'

[2019-10-24 20:01:00.338] ERROR -- #<Double (anonymous)> received unexpected message :deliver_now with (no args): nil

(UserMailer (class)).welcome_email(#<User id: 1, email: "tester44@example.com", created_at: "2019-10-24 11:01:00", updated_at: "2019-10-...rst_name: "Aaron", last_name: "Sumner", authentication_token: "9aDKu2QFE7M3DY_nQscq", location: nil>)
    expected: 1 time with arguments: (#<User id: 1, email: "tester44@example.com", created_at: "2019-10-24 11:01:00", updated_at: "2019-10-...rst_name: "Aaron", last_name: "Sumner", authentication_token: "9aDKu2QFE7M3DY_nQscq", location: nil>)
    received: 2 times with arguments: (#<User id: 1, email: "tester44@example.com", created_at: "2019-10-24 11:01:00", updated_at: "2019-10-...rst_name: "Aaron", last_name: "Sumner", authentication_token: "9aDKu2QFE7M3DY_nQscq", location: nil>)

  0) User sends a welcome email on account creation
     Failure/Error: expect(UserMailer).to have_received(:welcome_email).with(user)

       (UserMailer (class)).welcome_email(#<User id: 1, email: "tester44@example.com", created_at: "2019-10-24 11:01:00", updated_at: "2019-10-...rst_name: "Aaron", last_name: "Sumner", authentication_token: "9aDKu2QFE7M3DY_nQscq", location: nil>)
           expected: 1 time with arguments: (#<User id: 1, email: "tester44@example.com", created_at: "2019-10-24 11:01:00", updated_at: "2019-10-...rst_name: "Aaron", last_name: "Sumner", authentication_token: "9aDKu2QFE7M3DY_nQscq", location: nil>)
           received: 2 times with arguments: (#<User id: 1, email: "tester44@example.com", created_at: "2019-10-24 11:01:00", updated_at: "2019-10-...rst_name: "Aaron", last_name: "Sumner", authentication_token: "9aDKu2QFE7M3DY_nQscq", location: nil>)
     # ./spec/models/user_spec.rb:32:in `block (2 levels) in <main>'
     # -e:1:in `<main>'

コントローラスペックの be_success を be_successful に変更する

Rails 5.2ではコントローラスペックで be_success を使うと以下のような警告が出ます。

DEPRECATION WARNING: The success? predicate is deprecated and will be removed in Rails 6.0. Please use successful? as provided by Rack::Response::Helpers. (called from matches? at rspec/rails/matchers/have_http_status.rb:263)

この警告に対応するため、 be_successful を使うようにします。

-expect(response).to be_success
+expect(response).to be_successful

参考 Rails 6 will remove success? predicate in favor of successful? · Issue #1857 · rspec/rspec-rails

コントローラスペックの have_content_type :json の記法を変更する

Rails 6からはコントローラスペックで expect(response).to have_content_type :json のようなエクスペクテーションを書いていると、以下のようにテストが失敗するようになりました。

Expected "unknown content type (application/json; charset=utf-8)" to be Content Type "application/json" (json)

そこでこのエクスペクテーションを次のように変更します。

-expect(response).to have_content_type :json
+expect(response.content_type).to eq "application/json; charset=utf-8"

参考:アプリ全体のdiffはこちら

Everyday RailsのサンプルアプリケーションをRails 6にアップグレードした際に発生するすべてのdiffについては以下のプルリクエストで確認できます。 何か不明な点があればこちらも参考にしてみてください。

github.com

Rails 6環境でうまく動かない場合

この記事を参考にしてもRails 6でサンプルアプリのテストがパスしない!という場合は、僕の方でできるかぎりサポートします。 ただし、その場合はTeratailやStackOverflowなど、「みんなが見える場所」で質問するようにしてください。(DMでの技術的な質問は基本的にNGです) その上でTwitter等で「ここに質問を書いたので見てください」とメンションをもらえれば、何らかのコメントを返すようにします。

なぜ「みんなが見える場所」での質問をお願いするのかという理由については以下のエントリにまとめてあるので、こちらも参考にどうぞ。

blog.jnito.com

その他、本書の内容と異なる部分

その他、Everyday Railsの2019-10-01版に記載した内容と、現在の情報で異なる部分を以下にピックアップしておきます。

システムスペックにジェネレータが追加された

Everyday Railsの付録Aでは「バージョン3.8.0の時点では、rspec-rails はシステムスペックを作成するジェネレータを提供していません」と書いてありますが、バージョン3.9.0からジェネレータが追加されました。

システムスペックのジェネレータは、以下のようなコマンドで起動することができます。

$ rails g rspec:system users

すると、以下のようなファイルが生成されます。

# spec/system/users_spec.rb 
require 'rails_helper'

RSpec.describe "Users", type: :system do
  before do
    driven_by(:rack_test)
  end

  pending "add some scenarios (or delete) #{__FILE__}"
end

ただし、Everyday Railsの場合、beforeブロックの中の driven_by(:rack_test) に相当する処理は spec/support/capybara.rb 内に書いてあります。 ですので、このbeforeブロックは削除して構いません。

RSpec本体とrspec-railsのバージョン番号が同期しなくなった

これまではRSpec本体(rspec gem)とrspec-railsは必ずバージョン番号が同期するようにリリースされていましたが、rspec-rails 4.0がリリースされるタイミングでRSpec本体とrspec-railsのバージョン番号が同期しなくなります。(rspec gemの最新版が3.9.xで、rspec-railsの最新版が4.0.xのような形になる)

これはrspec-railsの足並みをRSpec本体に合わせるよりも、Railsのアップデートに合わせるようにして、rspec-railsを頻繁にアップデートできるようにすることが目的のようです。

参考 RSpec 3.9 has been released, and RSpec team changes.

まとめ(というか結論):Everyday Railsの基本的な内容はほとんど古くなっていません!

というわけで、この記事ではEveryday RailsのサンプルアプリケーションをRails 6にアップグレードした際に発生する、テストコード関連の変更点についてまとめました。

Rails 5.1からRails 6にアップグレードするためにはgemのアップデート等、いろいろな作業が必要になりますが、 テストコードだけに着目すればそれほど大きな変更点はありません。 実際、テストコードで変更する必要があるのはfactory_botの記法と、コントローラスペックの一部だけです。

これはすなわち、Everyday Railsの内容が現在でもほとんど古くなっていないことを意味しています。 ですので、Everyday Railsでテストコードの書き方を学べば、Rails 6環境でも十分しっかりしたテストを書けるようになるはずです。

これからも安心してEveryday RailsをRSpecの参考書としてご活用ください!

2021.1.19追記:Rails 6.1 + Ruby 3.0バージョンも作ってみました

このエントリで説明したコードをさらにRails 6.1 + Ruby 3.0に対応させてみました。 diffはこちらに載せています。

github.com

アップデートの手順はこのエントリで紹介した方法とほぼ同じで、実質的なテストコードの修正はゼロです。(Railsの設定ファイル等が変更されただけ)

自力でアップデートするも良し、さくっとgit cloneするも良し。 最新の実行環境で動かしたい、という方はこちらも参考にしてみてください!

「Everyday Rails - RSpecによるRailsテスト入門」について

「Everyday Rails - RSpecによるRailsテスト入門」はその名の通り、RailsアプリケーションをRSpecを使ってテストする方法を解説した電子書籍です。 難易度的には「RailsやRubyがある程度わかっている人であればOK、RSpecの知識はゼロでも大丈夫」というレベルになっています。

内容の詳細や購入方法等については以下のエントリにまとめてあるので、こちらを参考にしてください。 blog.jnito.com

ご購入は以下のサイトからどうぞ。 Everyday Rails - RSpecによるRailsテスト入門 - Leanpub f:id:JunichiIto:20190424050314p:plain

macOSをアップグレードする際は30GB〜40GBぐらい容量を空けておいた方が安心ですよ、という話

はじめに:インストーラに騙された!?

我が家には仕事用のMacBook Proと家族用のMacBook Pro(以下MBP)があります。
仕事用のMBPがトラブると、文字通り仕事にならないので、まずは実験的に家族用のMBPをmacOS Catalinaにアップグレードしてみることにしました。

Catalinaのダウンロードが終わり、アップグレードを進めようとすると、インストール先のディスクを選択する画面で「空き容量が足りません」というメッセージが標示されました。
「たしかに空き容量が少ないよな。さすがにもうちょっと増やさないとダメか」と思い、「空き容量が足りません」メッセージが出なくなるまでストレージの空き容量を増やしました。
そして、メッセージが出なくなったところでインストールを開始。
これでしばらく待てばCatalinaにアップグレードされるはず・・・と思いきや!!

こんなメッセージが出てインストールが途中で止まってしまいました!!

コンピュータにmacOSをインストールできませんでした
 

Macintosh HDにはインストールに必要な空き領域がありません
インストーラを終了してコンピュータを再起動してからやり直してください。

f:id:JunichiIto:20191020134437j:plain

なんでやねん〜!!さっきお前が「これでOK」っていうところまで空き容量を増やしたやんけー!!と心の中で絶叫しましたが、そんなことを言っても仕方ありません。

画面に再起動ボタンが出ているので、これをクリックすればアップデート前の状態に戻ってファイルが削除できるようになるんだろう・・・と思いきや、再び同じメッセージが!!

コンピュータにmacOSをインストールできませんでした
 

Macintosh HDにはインストールに必要な空き領域がありません
インストーラを終了してコンピュータを再起動してからやり直してください。

おいおい、こんなことしてたら永遠の無限ループじゃないですか😱

で、ここから約6時間にわたる試行錯誤の旅が始まるのですが、いったんその過程はすっ飛ばして「こうすれば一番速かった」という結論を書いておきます。

一番速い解決策

この解決策は事前にTimeMachineでバックアップを取ってあることが前提条件となります。
具体的な手順は以下のとおりです。

Macを復元する(元のmacOSに戻す)

まず、Macを「macOS 復元システム」から起動します。
再起動ボタンをクリックしてマシンがシャットダウンしたら、すぐにCommand + Rボタンを押します。
すると、「macOSユーティリティ」の画面が立ち上がるので、そこから「TimeMachineバックアップから復元」を選びます。

参考 macOS 復元について - Apple サポート

運が良ければMac本体に保存されている「ローカルスナップショット」からMacを復元できます。
ローカルスナップショットを使えば、5分から10分程度の時間でMacをアップグレード前のMojaveに戻すことができます。

参考 Time Machine のローカルスナップショットについて - Apple サポート

空き容量を増やす(注意点あり)

普通にMacが起動すれば、あとはファイルを削除して空き容量を増やすだけ・・・なのですが、いくつか注意点があります。

まず、Catalinaは「空き容量が最低8GBあればインストール可能」らしいのですが(Appleサポート談)、「30GB〜40GBぐらい空けておいた方が安心」だそうです(これもAppleサポート談)。

さらに、念のため気を付けた方がいいのが、「パージ可能」という表示です。
ディスクユーティリティというツールでストレージの空き容量を確認してみてください。
画面上部に表示されている横バーグラフの「空き」という表示が30GB〜40GB以上になっている方が望ましいです。

画面下部の「利用可能」という項目を見たときに、もし「30.22GB(20.46GBパージ可能)」というような表示になっていたら、それは実質10GB程度しか空き容量がありません。

下の図はディスクユーティリティの表示例です。

f:id:JunichiIto:20191020152450p:plain

参考情報:パージ可能な容量だけを増やしても意味がない!?

僕は試行錯誤の過程で、ターゲットディスクモードを使って仕事用のMBPからエラーが出て起動しなくなったMBPのストレージにアクセスし、不要なファイルを20GBほど削除しました。
ですが、「パージ可能」の容量が増えただけで、「空き」には変化が現れず、Macを再起動しても相変わらずインストーラの「空き領域がありません」エラーが出続けました(ちなみにそのときの状況はディスクユーティリティ上の「空き」が11GB程度でした)。

このことから、「パージ可能な領域」をどれだけ増やしても、インストーラの「空き容量が足りません」エラーは解消しないんじゃないかと推測しています。

「パージ可能な領域」を削除して「空き」を増やす方法(やや難)

しかし、この「パージ可能な領域」を削除するのは結構難易度が高いです。
僕は以下のページに載っている手順に従って、なんとかパージ可能な領域を減らすことができました。

How to Reclaim Purgeable Space on Mac - Bambielli’s Blog

ターミナルの操作に慣れている人でないと自信をもって作業できないと思うので、「ようわからん」という人はとりあえず「利用可能」の容量を可能な限り増やしてCatalinaのインストールに再チャレンジしてみましょう。
もしかしたら、それでもうまくいくかもしれません。

「空き」を十分確保したら、アップグレードに成功した

僕は上記の表示例のように、約72GBの「空き」を作ってからCatalinaを再インストールしました。
これだとエラーが出ることなく、スムーズにCatalinaをインストールすることができました。

f:id:JunichiIto:20191020152849p:plain
この表示を見るまで、約6時間かかりました・・・(くたびれた〜)

参考情報:空き容量を増やすための便利アイテム

「空き容量を増やしましょう」といっても、状況によっては削除できるファイルには限界があると思います。
そういう場合は一時的に外部ハードディスクにデータを避難してから、Mac本体のデータを削除しましょう。
macOSのアップグレードが終わったら、外部ハードディスクからMac本体にデータを戻せばOKです。

僕も外部ハードディスクを持っていたので、これに一時的にデータを避難させました。
1TBでも7000円前後、大きさもiPhoneぐらいのコンパクトサイズなので、何かあったときのために1台持っておくと便利です。

I-O DATA HDD ポータブルハードディスク 1TB USB3.0バスパワー対応 日本製 EC-PHU3W1

I-O DATA HDD ポータブルハードディスク 1TB USB3.0バスパワー対応 日本製 EC-PHU3W1

試行錯誤のあれこれ

上の解決策は「一番の最短ルート」ですが、その解決策に至るまではかなり試行錯誤しました。
僕が試してもダメだった方法を以下に紹介します。

🙅🏻‍♀️Startup Managerを使ってMacintosh HDを選択する

Macが再起動するタイミングでOptionキーを押し続けると、Startup Managerが立ち上がります。
ここで、MBP本体のMacintosh HDを選択すると、アップグレード前のmacOSに戻れる・・・というWeb記事があったのですが、僕の場合は選択直後に「🚫」みたいなマークが出て、そこからMacを起動することができませんでした。

参考 別の起動ディスクを選択する方法 - Apple サポート

🙅🏻‍♀️ターゲットディスクモードで別のMacから不要なデータを削除する

起動しない方のMBPをTボタンを押しながら起動させると、ターゲットディスクモードになります。
この状態でMBP同士をThunderbolt対応のUSB-Cケーブルで接続すると、正常なMBPから起動しないMBPのストレージの内容を読み書きできるようになります。

ここで注意しなければならないのは「Thunderbolt対応のUSB-Cケーブル」という点です。
MBPとACアダプタを繋ぐ白いUSB-CケーブルはThunderbolt対応ではないので、接続しても外部ディスクとして認識されません。
僕はたまたまUSB-C接続できる外部モニタを持っていたので、このモニタのUSB-Cケーブルを「Thunderbolt対応のUSB-Cケーブル」として利用することができました。

ただし、ターゲットディスクモードで接続してもFinderからは多くのディレクトリがアクセス不可になっています。
このため、ファイルの確認やコピー、削除といった作業はターミナルから操作する必要がありました。

参考 ターミナルから外付けHDDとかの外部にアクセスしてみる - 悲喜交々 -へたれの技術メモ置き場-

が、前述の通り、この方法でファイルを削除してもディスクユーティリティ上の「空き」容量が変わらず、結局徒労に終わりました。

f:id:JunichiIto:20191020151441j:plain
ターゲットディスクモードで頑張って2台のMBPを接続する様子(でもダメだった😫)

🙅🏻‍♀️ターゲットディスクモードで動かないMacを起動ディスクとして選択する

これはAppleサポートの人に教えてもらった方法です。
上で書いたようにターゲットディスクモードでMBP同士をつないでおき、正常に動いている方のMacからシステム環境設定の「起動ディスク」を選択、そこから動かないMacのハードディスクを選択して・・・という手順を教えてもらったのですが、そもそも動かない方のMacのハードディスクが一覧に表示されず、そこから何もできずに終わりました。
Appleのサポートの人も「うーん、おかしいですね〜」と頭を抱えておられました。

🙅🏻‍♀️First Aidでストレージを検証する

Command + Rを押しながらMacを起動すると、macOSユーティリティメニューからディスクユーティリティを選択できます。
ディスクユーティリティにはFirst Aidという検証ツールがあるので、このツールを使って動かないMacのストレージを検証してみました。
が、特に異常は報告されず、空き容量にも変化は現れませんでした。

まとめ:よくあるエラーらしいので、みなさん気を付けて!

というわけで、このエントリでは僕がmacOSをアップグレードするために繰り広げた、死闘の数々を紹介してみました。

ちなみに、数時間かけても一向に進まず、自分では解決できそうになかったので、途中でAppleのサポートに電話したのですが、サポート担当の方いわく、僕と同じようにインストールの途中で「容量不足」と怒られて先に進めなくなる事象は、結構たくさん報告されているようです。
というか、電話で話していたサポートの人自身が「実は先日、私もプライベートで同じ問題に遭遇しまして・・・」と話していました(おいおい)。

毎年macOSをアップデートしてきましたが、ここまで四苦八苦したのは今回が初めてです。
これからは「インストールの準備ができましたよ!」というインストーラの言葉を鵜呑みにせず、ストレージの空き容量が十分あることを確認してからインストールを実行しようと思います。

このブログを読んでいるMacユーザーのみなさんも、OSアップグレードには十分注意してください!

あわせて読みたい

Macが起動しないのは非常に焦ったのですが、「最悪TimeMachineから復元すれば何とかなる」と思えたのは不幸中の幸いでした。
みなさんも万一のトラブルに備えてバックアップ環境はちゃんと整えておきましょう。

blog.jnito.com