mise で Ruby をインストール

Ruby をインストールするにはライブラリが足りなかったらしい。

ruby-build のページを参考に、つぎのようにライブラリをインストールしたら、Ruby のインストールもできた。

takatoh@apostrophe:~$ sudo apt install libz-dev libffi-dev libreadline-dev libssl-dev libyaml-dev

ただし、ruby-build のページではつぎのように紹介されている。上の実行例に出てこないパッケージは、いろいろ試行錯誤してたらからいつの間にかインストールされていたのかもしれない。

apt-get install autoconf patch build-essential rustc libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libgmp-dev libncurses5-dev libffi-dev libgdbm6 libgdbm-dev libdb-dev uuid-dev

ともあれ、これで準備は整った。

takatoh@apostrophe:~$ mise use [email protected]
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 19.6M 100 19.6M 0 0 45.2M 0 --:--:-- --:--:-- --:--:-- 45.2M
mise [email protected] ✓ installed mise ~/.mise.toml tools: [email protected]

OK。つぎのようにして Ruby がインストールされているのを確認できる。

takatoh@apostrophe:~$ mise ls
Tool Version Config Source Requested
node 20.17.0 ~/.mise.toml 20
python 3.11.10 ~/.mise.toml 3.11
ruby 3.2.5 ~/.mise.toml 3.2
takatoh@apostrophe:~$ ruby --version
ruby 3.2.5 (2024-07-26 revision 31d0f1a2e7) [x86_64-linux]

Linux Mint で mise を使う

OS のインストールが済んだら次はツール、というかプログラミング言語のインストールだ。よく使う Python や Ruby なんかをインストールする。

で、今までは pyenv とか rbenv を使ってたんだけど、最近見つけた mise (ミース)を使ってみようと思った。

mise は Rust 製のツールなので、まずは Rust をインストールする。公式サイトの手順に従えばいい。rustup という Rust のツールチェインを管理するツールをまずインストールする。

takatoh@apostrophe:~$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
info: downloading installer

Welcome to Rust!

This will download and install the official compiler for the Rust
programming language, and its package manager, Cargo.

Rustup metadata and toolchains will be installed into the Rustup
home directory, located at:

/home/takatoh/.rustup

This can be modified with the RUSTUP_HOME environment variable.

The Cargo home directory is located at:

/home/takatoh/.cargo

This can be modified with the CARGO_HOME environment variable.

The cargo, rustc, rustup and other commands will be added to
Cargo's bin directory, located at:

/home/takatoh/.cargo/bin

This path will then be added to your PATH environment variable by
modifying the profile files located at:

/home/takatoh/.profile
/home/takatoh/.bashrc

You can uninstall at any time with rustup self uninstall and
these changes will be reverted.

Current installation options:


default host triple: x86_64-unknown-linux-gnu
default toolchain: stable (default)
profile: default
modify PATH variable: yes

1) Proceed with standard installation (default - just press enter)
2) Customize installation
3) Cancel installation
>

info: profile set to 'default'
info: default host triple is x86_64-unknown-linux-gnu
info: syncing channel updates for 'stable-x86_64-unknown-linux-gnu'
info: latest update on 2024-09-05, rust version 1.81.0 (eeb90cda1 2024-09-04)
info: downloading component 'cargo'
info: downloading component 'clippy'
info: downloading component 'rust-docs'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: downloading component 'rustfmt'
info: installing component 'cargo'
info: installing component 'clippy'
info: installing component 'rust-docs'
15.9 MiB / 15.9 MiB (100 %) 10.1 MiB/s in 1s ETA: 0s
info: installing component 'rust-std'
26.8 MiB / 26.8 MiB (100 %) 13.5 MiB/s in 2s ETA: 0s
info: installing component 'rustc'
66.9 MiB / 66.9 MiB (100 %) 14.7 MiB/s in 4s ETA: 0s
info: installing component 'rustfmt'
info: default toolchain set to 'stable-x86_64-unknown-linux-gnu'

stable-x86_64-unknown-linux-gnu installed - rustc 1.81.0 (eeb90cda1 2024-09-04)


Rust is installed now. Great!

To get started you may need to restart your current shell.
This would reload your PATH environment variable to include
Cargo's bin directory ($HOME/.cargo/bin).

To configure your current shell, you need to source
the corresponding env file under $HOME/.cargo.

This is usually done by running one of the following (note the leading DOT):
. "$HOME/.cargo/env" # For sh/bash/zsh/ash/dash/pdksh
source "$HOME/.cargo/env.fish" # For fish

Rust に関するツールはすべて ~/.cargo/bin にインストールされる。一旦シェルを終了して起動し直すと(あるいは新しいシェルを起動すると)、cargo コマンドが使えるようになる。

takatoh@apostrophe:~$ cargo --version
cargo 1.81.0 (2dbb1af80 2024-08-20)

rustup show コマンドでツールチェインを確認できる。

takatoh@apostrophe:~$ rustup show
Default host: x86_64-unknown-linux-gnu
rustup home: /home/takatoh/.rustup

stable-x86_64-unknown-linux-gnu (default)
rustc 1.81.0 (eeb90cda1 2024-09-04)

mise のインストールには、mise の公式ドキュメントに従ってインストール用のシェルスクリプトを使う。

takatoh@apostrophe:~$ curl https://mise.run | sh
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 6300 100 6300 0 0 50786 0 --:--:-- --:--:-- --:--:-- 50806
mise: installing mise...
######################################################################## 100.0%
mise: installed successfully to /home/takatoh/.local/bin/mise
mise: run the following to activate mise in your shell:
echo "eval \"\$(/home/takatoh/.local/bin/mise activate bash)\"" >> ~/.bashrc

mise: this must be run in order to use mise in the terminal
mise: run `mise doctor` to verify this is setup correctly

これでインストールできた。

takatoh@apostrophe:~$ ~/.local/bin/mise --version
2024.9.5 linux-x64 (1f0f03e 2024-09-17)

インストールはできたが、アクティベートする必要がある。そのためのコードを .bashrc ファイルに書き込む。

takatoh@apostrophe:~$ echo 'eval "$(~/.local/bin/mise activate bash)"' >> ~/.bashrc

それから環境変数 PATH の設定……なのだけど、アクティベートすると設定しくれるらしい。もう一度シェルを起動し直す。

takatoh@apostrophe:~$ mise --version
2024.9.5 linux-x64 (1f0f03e 2024-09-17)

上の通り、フルパスを指定しなくても mise コマンドが使える。

さて、ここまで来たらプログラミング言語のインストールができる。ドキュメントに従って mise use コマンドで Node.js をインストールしてみる。

takatoh@apostrophe:~$ mise use node@20
mise [email protected] ✓ installed mise ~/.mise.toml tools: [email protected]
takatoh@apostrophe:~$ node --version
v20.17.0

うまく行った!

つぎは Python。

takatoh@apostrophe:~$ mise use [email protected]
mise hint use multiple versions simultaneously with mise use [email protected] [email protected]
mise hint disable this hint with mise settings set disable_hints python_multi or all with mise settings set disable_hints "*"
mise hint installing precompiled python from indygreg/python-build-standalone
if you experience issues with this python (e.g.: running poetry), switch to python-build by running mise settings set python_compile 1
mise hint disable this hint with mise settings set disable_hints python_precompiled or all with mise settings set disable_hints "*"
mise [email protected] ✓ installed mise ~/.mise.toml tools: [email protected]
takatoh@apostrophe:~$ python --version
Python 3.11.10

Ruby はどうか。

takatoh@apostrophe:~$ mise use [email protected]
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 14.6M 100 14.6M 0 0 75.4M 0 --:--:-- --:--:-- --:--:-- 75.6M
BUILD FAILED (Linuxmint 22 on x86_64 using ruby-build 20240917)
You can inspect the build directory at /tmp/ruby-build.20240918205818.20859.tDse9P
See the full build log at /tmp/ruby-build.20240918205818.20859.log
mise ~/.cache/mise/ruby/ruby-build/bin/ruby-build failed
==> Downloading openssl-3.0.15.tar.gz...
-> curl -q -fL -o openssl-3.0.15.tar.gz https://dqw8nmjcqpjn7.cloudfront.net/23c666d0edf20f14249b3d8f0368acaee9ab585b09e1de82107c66e1f3ec9533
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 14.6M 100 14.6M 0 0 75.4M 0 --:--:-- --:--:-- --:--:-- 75.6M
==> Installing openssl-3.0.15...
-> ./config "--prefix=$HOME/.local/share/mise/installs/ruby/3.2.5/openssl" "--openssldir=$HOME/.local/share/mise/installs/ruby/3.2.5/openssl/ssl" zlib-dynamic no-ssl3 shared
-> make -j 6

BUILD FAILED (Linuxmint 22 on x86_64 using ruby-build 20240917)

You can inspect the build directory at /tmp/ruby-build.20240918205818.20859.tDse9P
See the full build log at /tmp/ruby-build.20240918205818.20859.log
mise ~/.cache/mise/ruby/ruby-build/bin/ruby-build exited with non-zero status: exit code 1
mise Run with --verbose or MISE_VERBOSE=1 for more information

あれ、なんか失敗したぞ?

でも ruby コマンドは使える。どういうこと?

takatoh@apostrophe:~$ ruby --version
ruby 3.2.3 (2024-01-18 revision 52bb2ac0a6) [x86_64-linux-gnu]

というわけで、今日はここまで。

[追記]

rubymise でインストールされたものじゃなく、もとから入ってるものだった。

takatoh@apostrophe:~$ which ruby
/usr/bin/ruby

これに対して、mise でインストールされた pythonnode は ~/.local/share/mise/installs/ 以下にある。

takatoh@apostrophe:~$ which python
/home/takatoh/.local/share/mise/installs/python/3.11/bin/python
takatoh@apostrophe:~$ which node
/home/takatoh/.local/share/mise/installs/node/20/bin/node

Ruby:HTTPクライアントライブラリを http gem にのりかえた

すごーく久しぶりに、Ruby で HTTP アクセスするってことをやった。使い慣れた httpclient を使ったんだけど、こんなエラーが出た。

C:/Ruby32-x64/lib/ruby/gems/3.2.0/gems/httpclient-2.8.3/lib/httpclient/ssl_socket.rb:103:in `connect': SSL_connect returned=1 errno=0 peeraddr=[2606:4700:3032::ac43:8791]:443 state=error: certificate verify failed (certificate has expired) (OpenSSL::SSL::SSLError)
(以下略)

SSLのエラーだ。

ググって調べてみると、httpclient gem は信頼できる証明書を独自に持っていてそれがもうメンテされていない、ということが解った。

上の記事では、システムのデフォルトの証明書を利用することで回避する方法も書かれている。けど、RubyGems.org をみると httpclient gem の最後のリリースは 2016/9 で、これはもう他のライブラリにのりかえるべきだろう。

で、さらにググって調べたところ、http という gem の評判がよさげに見えたのでこれを使ってみることにした。こんな名前よく空いてたな。

使い方は GitHub の Wiki にまとまっている。

httpclient みたいにインスタンスを作る必要がなくて 、require "http" したら HTTP モジュールの関数が使える。例えば GET の場合はこう。

irb(main):001:0> require "http"
=> true
irb(main):002:0> res = HTTP.get("https://blog.panicblanket.com")
=> #<HTTP::Response/1.1 200 OK {"Date"=>"Sat, 06 Jan 2024 20:51:15 GMT", "Content-Type"=>"text/...

レスポンスのコードは #code 、ボディは #to_s で取得できる。

irb(main):003:0> res.code
=> 200
irb(main):004:0> res.to_s
=> "<!DOCTYPE html>\n\n<html lang=\"ja\">\n\n\t<head>\n\n\t\t<meta http-equiv=\"content-type\" content=\"text/html\" charset=\"UTF-8\" />\n\t\t<meta 
(以下略)

こんな感じ。

GETPOSTPUTDELETE などの HTTP メソッドの他にも WEBDAV なんかもサポートしてる。使いやすそうだ。

Rails6の最新版までアップグレード

ローカルネットワークで運用してる Rails アプリを Rails6 系の最新版 6.1.7.3 までアップグレードした。Ruby のバージョンは 3.1.4。Docker コンテナ上で動かしている。

以下、メモ。後で時間があればもう少し詳しく書く。

Rails 5.2 以降の credentials.yml.enc について。

Active Storage について。

Rails の設定について。

Ruby:ランダムな文字列を作る

以前、Python で UUID をもとにしてランダムな文字列を作るってのをやった。

同じことを Ruby でやろうとしたら存外に手間を食ったのでメモしておく。

UUID は 標準ライブラリの SecureRandom モジュールで生成できる。

irb(main):001:0> require "securerandom"
=> true
irb(main):002:0> u = SecureRandom.uuid
=> "4ad31376-3c83-49d7-a70a-14467f2b4022"

が、Python では UUID クラスのインスタンスが返ってきてバイト列を得るのも簡単だったけど、Ruby では文字列が返ってくる。なので、16進数表示の文字列からバイト列に変換してやらないといけない。とりあえず邪魔なハイフンを取り除いておく。

irb(main):003:0> h = u.tr("-", "")
=> "4ad313763c8349d7a70a14467f2b4022"

で、バイト列に変換するメソッドを書く。

はじめは2文字ずつに切り分けて String#to_i で整数に変換してやればいいかと思ったけど、これだと得られるのは Fixnum クラスのインスタンス(の配列)であってバイト列じゃない。

結局次のようになった。2文字ずつ切り分けるのは同じだけど、切り出した2文字は別々に整数に変換して、1文字目のほうは 4bit 左シフトしてから2文字目のほうと論理OR をとる。そうすると整数16個の配列ができる。これをバイト列に変換するには Array#pack を使う。

irb(main):004:1* def hex2bstring(hex)
irb(main):005:1*   barray = []
irb(main):006:2*   hex.split("").each_slice(2) do |pair|
irb(main):007:2*     barray.push((pair[0].to_i(16) << 4) | (pair[1].to_i(16)))
irb(main):008:1*   end
irb(main):009:1*   barray.pack("C")
irb(main):010:0> end
=> :hex2bstring

このメソッドを使ってバイト列に変換。

irb(main):011:0> b = hex2bstring(h)
=> "J\xD3\x13v<\x83I\xD7\xA7\n\x14F\x7F+@\"" 

さらに Base32 エンコードするんだけど、Ruby の標準ライブラリには Base32 がないので、あらかじめ gem install base32 しておく必要がある。エンコードしたらパディングの = を取り除く。

irb(main):012:0> require "base32"
=> true
irb(main):013:0> s = Base32.encode(b)
=> "JLJRG5R4QNE5PJYKCRDH6K2AEI======"
irb(main):014:0> s2 = s.tr("=", "")
=> "JLJRG5R4QNE5PJYKCRDH6K2AEI"

最後は見た目のランダムさを増すために小文字を混ぜるようにする。if の条件式には Array#sample を使って [true, false].sample としてもよかったんだけど、せっかく UUID を生成するのに SecureRandom を使ってるのでここでも使ってみた。でもわかりにくいかも。

irb(main):015:1 def down(c)
irb(main):016:2*  if SecureRandom.random_number(2) > 0
irb(main):017:2*    c.downcase
irb(main):018:2*  else
irb(main):019:2*    c
irb(main):020:1*  end
irb(main):021:0> end
=> :down

最終的にはこうなった。

irb(main):022:0> s2.split("").map{|c| down(c) }.join
=> "JljRg5r4qnE5pjyKCRdH6k2aei"

Python より面倒だな。

古いRailsアプリをDockerコンテナに乗せた

もう1年も前になるけどこんな記事を書いた。

ローカルネットワークのサーバ置き換えに際して、古いサーバ(Ubuntu 16.04 LTS)で動かしていた Rails アプリが、新しいサーバ(Ubuntu 20.04 LTS)上で動かせなくてどうしよう……っていう記事だ。

あれから1年も過ぎてしまったけど、今年に入ってから週末ごとにちまちまと作業をして、なんとか新しいサーバの Dockerコンテナで動かせるまでになった。

ベースにした Docker イメージは、Ruby の公式イメージのうち一番古いバージョン、2.6.9。とにかく Ruby の公式 Docker イメージをベースにしたコンテナで動かせることを目標にした。

Rails は 5.0.7.2まで上げた。これだって今となっては古いバージョン(RubyGems.org を見ると2019/3/13リリース)だけど、ローカルネットワークでしか使ってない web アプリなのでひとまず妥協する。

作業は開発用メインマシン(Ubuntu 20.04 LTS)の rbenv で Ruby そのもののバージョンも変えながらやった。作業を始めた時点では Rails 4.1.4 で、最後のコミットはなんと 2016/3/3 だった。6年近くもメンテしてなかったってことだよ。アプリを使う分には不自由してなかったからだけど、だからってほったらかしだとこうやって後になってツケが来るんだよな。Rails にさわるもの6年ぶりってわけで、少しずつバージョンを上げるたびに出るエラー(主に依存関係)で苦労した。これからはもう少し面倒を見て、追いつくようにしよう。

さて、そういうわけで、動かしていたアプリがなくなって、めでたく古いサーバが空いた。春になれば Ubuntu 22.04 LTS もリリースされるだろうから、そのテスト用にしようかな。

ローカルネットワークのサーバを置き換えるんだけど、古いRailsアプリはどうすりゃいいんだ……

置き換え用にと新しく買ったサーバ用の PC が明日届く、とメールがきた。まあ、届いたからって平日なのですぐに作業できるわけでもないんだけど……

それはさておき。現状でローカルネットワークで運用してるサーバ3台を順繰りに押し出すように置き換えていって、最後に押し出される一番古い PC は万が一のときの予備にまわす。この際だからサーバは全部 Ubuntu Server 20.04 に統一しようと、すでにインストールメディアの準備は済んでいる。

で、さて。その一番古いサーバでもいくつかの web アプリを動かしているんだけど、そのひとつがだいぶ前に Ruby on Rails で作ったものなんだ。なんと Rails 4.1.4。ちなみに OS やなんかのバージョンは次の通り:

  • Ubuntu 16.04
  • Ruby 2.3.1
  • Ruby on Rails 4.1.4
  • unicorn 5.4.1

これを最新の Ubuntu 20.04 の環境に置き換えようとしてるんだけど……

とにかく、開発用マシン(Ubuntu 20.04,Ruby 2.7.1)で動かせるかどうか試してみたら、案の定ダメだった。アプリを動かすどころか bundle install の途中でコケる。どうも json とか therubyracer の gem をインストールするところでうまく行かないみたいだ。なら DockerHub にある Ubuntu 公式イメージの一番古いバージョン 16.04 の上でならどうか、と試してみたけどやっぱりダメ。

新しい OS のサーバに移行するのに合わせてアプリもバージョンアップしなきゃな、とは思ってたんだけど、開発環境で動作させられないんじゃそれも難しい。どうすりゃいいんだか…… 

ISBNを操作するgem petrarcaをリリースした

はてさて、前の記事からもうひと月も経ってしまった。

その、ひと月まえの記事に書いた、Ruby 用の ISBN を操作するライブラリを RubyGems.org にリリースした。名前は petrarca。

最初のリリース(v0.2.0)が8月30日で、その後バージョンアップして現在は v0.4.0(9月8日)。とりあえず自分では満足しているので、しばらくは大きな更新はないはず。

他のライブラリにはない(ざっと調べた限りではなさそうな)機能としては、ハイフン無しの ISBN をハイフンつきに変換する Petrarca.hyphenate メソッドが日本だけでなく世界中の ISBN に対応していること。こんな感じ。

irb(main):001:0> Petrarca.hyphenate("9780061052811")
=> "978-0-06-105281-1"

国や出版社の番号はそれぞれで桁数が違うわけだけど、International ISBN Agency のページから xml ファイルでダウンロードできるのでそれを利用している。このデータはときどき更新されるらしいので、バージョンアップするとしたらその対応だな。