$ docker run --rm -it ruby:4.0.3 bash
root@601c809deee7:/# gem -v
4.0.6
root@601c809deee7:/# gem install rdoc -v 7.2.0
Fetching rdoc-7.2.0.gem
Successfully installed rdoc-7.2.0
1 gem installed
A new release of RubyGems is available: 4.0.6 → 4.0.11!
Run `gem update --system 4.0.11` to update your installation.
root@601c809deee7:/# gem -v
/usr/local/bundle/gems/rdoc-7.2.0/lib/rdoc/version.rb:8: warning: already initialized constant RDoc::VERSION
/usr/local/lib/ruby/gems/4.0.0/gems/rdoc-7.0.3/lib/rdoc/version.rb:8: warning: previous definition of VERSION was here
/usr/local/bundle/gems/rdoc-7.2.0/lib/rdoc.rb:68: warning: already initialized constant RDoc::VISIBILITIES
/usr/local/lib/ruby/gems/4.0.0/gems/rdoc-7.0.3/lib/rdoc.rb:68: warning: previous definition of VISIBILITIES was here
/usr/local/bundle/gems/rdoc-7.2.0/lib/rdoc.rb:74: warning: already initialized constant RDoc::DOT_DOC_FILENAME
/usr/local/lib/ruby/gems/4.0.0/gems/rdoc-7.0.3/lib/rdoc.rb:74: warning: previous definition of DOT_DOC_FILENAME was here
/usr/local/bundle/gems/rdoc-7.2.0/lib/rdoc.rb:79: warning: already initialized constant RDoc::GENERAL_MODIFIERS
/usr/local/lib/ruby/gems/4.0.0/gems/rdoc-7.0.3/lib/rdoc.rb:79: warning: previous definition of GENERAL_MODIFIERS was here
/usr/local/bundle/gems/rdoc-7.2.0/lib/rdoc.rb:84: warning: already initialized constant RDoc::CLASS_MODIFIERS
/usr/local/lib/ruby/gems/4.0.0/gems/rdoc-7.0.3/lib/rdoc.rb:84: warning: previous definition of CLASS_MODIFIERS was here
/usr/local/bundle/gems/rdoc-7.2.0/lib/rdoc.rb:89: warning: already initialized constant RDoc::ATTR_MODIFIERS
/usr/local/lib/ruby/gems/4.0.0/gems/rdoc-7.0.3/lib/rdoc.rb:89: warning: previous definition of ATTR_MODIFIERS was here
/usr/local/bundle/gems/rdoc-7.2.0/lib/rdoc.rb:94: warning: already initialized constant RDoc::CONSTANT_MODIFIERS
/usr/local/lib/ruby/gems/4.0.0/gems/rdoc-7.0.3/lib/rdoc.rb:94: warning: previous definition of CONSTANT_MODIFIERS was here
/usr/local/bundle/gems/rdoc-7.2.0/lib/rdoc.rb:99: warning: already initialized constant RDoc::METHOD_MODIFIERS
/usr/local/lib/ruby/gems/4.0.0/gems/rdoc-7.0.3/lib/rdoc.rb:99: warning: previous definition of METHOD_MODIFIERS was here
/usr/local/bundle/gems/rdoc-7.2.0/lib/rdoc/markdown.rb:257: warning: already initialized constant RDoc::Markdown::KpegPosInfo
/usr/local/lib/ruby/gems/4.0.0/gems/rdoc-7.0.3/lib/rdoc/markdown.rb:257: warning: previous definition of KpegPosInfo was here
/usr/local/bundle/gems/rdoc-7.2.0/lib/rdoc/markdown/entities.rb:5: warning: already initialized constant RDoc::Markdown::HTML_ENTITIES
/usr/local/lib/ruby/gems/4.0.0/gems/rdoc-7.0.3/lib/rdoc/markdown/entities.rb:5: warning: previous definition of HTML_ENTITIES was here
/usr/local/bundle/gems/rdoc-7.2.0/lib/rdoc/markdown/literals.rb:86: warning: already initialized constant RDoc::Markdown::Literals::KpegPosInfo
/usr/local/lib/ruby/gems/4.0.0/gems/rdoc-7.0.3/lib/rdoc/markdown/literals.rb:86: warning: previous definition of KpegPosInfo was here
/usr/local/bundle/gems/rdoc-7.2.0/lib/rdoc/markdown/literals.rb:446: warning: already initialized constant RDoc::Markdown::Literals::Rules
/usr/local/lib/ruby/gems/4.0.0/gems/rdoc-7.0.3/lib/rdoc/markdown/literals.rb:446: warning: previous definition of Rules was here
/usr/local/bundle/gems/rdoc-7.2.0/lib/rdoc/markdown.rb:584: warning: already initialized constant RDoc::Markdown::EXTENSIONS
/usr/local/lib/ruby/gems/4.0.0/gems/rdoc-7.0.3/lib/rdoc/markdown.rb:584: warning: previous definition of EXTENSIONS was here
/usr/local/bundle/gems/rdoc-7.2.0/lib/rdoc/markdown.rb:589: warning: already initialized constant RDoc::Markdown::DEFAULT_EXTENSIONS
/usr/local/lib/ruby/gems/4.0.0/gems/rdoc-7.0.3/lib/rdoc/markdown.rb:589: warning: previous definition of DEFAULT_EXTENSIONS was here
/usr/local/bundle/gems/rdoc-7.2.0/lib/rdoc/markdown.rb:16430: warning: already initialized constant RDoc::Markdown::Rules
/usr/local/lib/ruby/gems/4.0.0/gems/rdoc-7.0.3/lib/rdoc/markdown.rb:16420: warning: previous definition of Rules was here
4.0.6
root@601c809deee7:/# gem list --exact rdoc
(同様のwarningなので省略)
*** LOCAL GEMS ***
rdoc (7.2.0, 7.0.3)
root@601c809deee7:/#
Ruby 4.0でrdocを追加インストールするとwarningが出るようになる
Ruby 3.4の場合
Rubyの3.4系列では、default gemの1つとして、rdocが標準添付されています。 ここにrdocを追加インストールしてもwarningが出るようにはなりません。
以下はdockerを使ってこのことを確認した様子です。
Ruby 4.0でrdocを追加インストールするとwarningが出るようになる
Ruby 4.0の場合
Rubyの4.0系列では、bundled gemの1つとして、rdocが標準添付されています。 ここにrdocの最新バージョンをインストールするようなシチュエーションで、rdocを追加インストールするとwarningが出るようになります。
この現象は、dockerを使うと、以下のように簡単に再現できます。
ruby:4.0.3において、始めはgem -vコマンドの実行でwarningは出ませんが、 rdocをインストールすると、同じgem -vコマンドでwarningが出ていることが確認できます。Ruby 4.0でrdocを追加インストールするとwarningが出るようになる
Ruby 4.0では、rdoc gemについて最新バージョンなどを追加インストールすると、gemコマンドの実行で大量のwarningが表示される状態に陥いります。
なお、開発サイドもこの状態を把握しているようですので、個人的には解決されるまで様子を見ようと思っています。
【小ネタ】Hash#mergeはキーワード引数でも動作する
RubyのHash#mergeのマニュアルでは、 その引数はハッシュとしか書かれていません。 例えば、以下のような形です。
では、キーワード引数を渡すとどうなるのかと言うと、以下のように動きはしますよ、という小ネタです。
Rubyの言語仕様上OKなのかは不明ですので、自分から積極的に使うというよりは、 他人のコードを読むときに役立つかもしれない小ネタ知識でした。
RubyのArrayやHashのリテラルをdeep freezeするshareable_constant_valueマジックコメント
概要
Rubyでは、以下のように
shareable_constant_value: literalというマジックコメントを記入することで、 以下の例のように、定数に代入したArrayやHashのリテラルを深く(deeply)freezeすることが出来ます。少し詳しい話
shareable_constant_valueマジックコメントはRuby 3.0で導入されました。注意点
このマジックコメントのliteralモードでfreezeされるのは定数が対象であるため、 以下のように、代入先が変数である場合にはfreezeされません。
また、このマジックコメントのliteralモードを指定したファイル内では、以下のように、 「freezeされていないオブジェクト」や「freezeされていないものを含むオブジェクト」を定数に代入しようとするとエラーが発生します。
裏を返せば、以下のように、freezeすればエラーが回避できます。 また、freezeする定数とfreezeしない定数でファイルを分けるという自然なアプローチでもエラーを回避できます。
参考文献
Emailアドレスとして適切かどうかのチェックに使えるURI::MailTo::EMAIL_REGEXP
rubyの標準添付ライブラリであるuriに存在している
URI::MailTo::EMAIL_REGEXPは、 ある文字列が規格上Emailアドレスとして適切かどうかのチェックに以下のように利用できます。Railsガイドでも、 このEMAIL_REGEXPを用いてemailのバリデーションを行う方法を以下のように紹介しています。
URI::MailTo::EMAIL_REGEXPを使うメリット
TechRachoの記事によると、
URI::MailTo::EMAIL_REGEXPの正規表現はHTML規格書から持ってきているため、URI::MailTo::EMAIL_REGEXPを使ったバリデーションはブラウザがinput type="email"で行うバリデーションと一致するというメリットがあります。余談
URI::MailTo::EMAIL_REGEXPは、 Rubyリファレンスマニュアルにも Ruby標準ライブラリリファレンスにも、実は記載されていません。 ですので、URI::MailTo::EMAIL_REGEXPを利用するのは少々裏技的な側面があるのかもしれません。 ただ、上述の通り、Railsガイドや記事で取り上げられるぐらいに認知されていますので、利用しても大丈夫なのではないかと思います。Pythonライブラリの作成に関する公式情報
pyproject.tomlの[build-system]の指定が、サンプルプロジェクトではsetuptoolsである一方、チュートリアルではデフォルト扱いがhatchlingになっていて、両者で違いがあって興味深いですね。 公式(PyPA)としては、setuptoolsがまだまだ現役であることを認めつつ、今後はhatchlingを推していきたいということなのかな?Pythonライブラリの作成に関する公式情報
なるほど、Pythonのパッケージングに関して、公式情報に相当する情報があるのですね。勉強になります。
それから、言及されているチュートリアルとサンプルプロジェクトのどちらも設定を書くのが
setup.pyではなくpyproject.tomlなのですね。 自分でも少し調べてみたのですが、python.orgにあるガイドには、「(パッケージングの設定には)pyproject.tomlファイルを使うのが標準的な慣習です。」と書いてあり、Zennの記事でも「現在でも歴史的経緯から setup.py のみで設定を行っているプロジェクトは多く存在しますが、このような構成は現在では非推奨」、「setup.pyを設定ファイルの一部として用いる場合であってもpyproject.tomlは配置するのが現在の標準的なセットアップ方法」と書いてある記事を見つけました。どうやら、Pythonパッケージには
pyproject.tomlを用意すべし、pyproject.tomlを起点にしてPythonパッケージをビルドすべし、というのが公式見解のようですね。コマンドラインからsetup.pyを直接実行するやり方はlegacy、つまり、廃止予定とのことですし。Pythonライブラリの作成に関する公式情報
Pythonのライブラリを自作するときに、フォルダ構成や設定ファイルの書き方について知りたくなることがあります。ライブラリを公開するかどうかやどの程度きっちり作るかなど状況は様々だと思いますが、ソフトウェアの規模が大きいときやあちこちから呼び出す関数を整備するときなどに、ライブラリやライブラリに準じるものを作成することはよくあることだと思います。そういうときにどういう作り方が行儀の良いやり方なのかを探していて見つけた情報が以下のものです。
Python公式サイトのパッケージングに関するチュートリアル
https://packaging.python.org/ja/latest/tutorials/packaging-projects/
Pythonの公式サイトである「python.org」に置いてある文書ですので、信頼できる情報なのだと思います。
PyPAのサンプルプロジェクト
https://github.com/pypa/sampleproject
Pythonでのパッケージングに関するとりまとめを行っている組織であるPyPAが出しているサンプルですので、信頼できるのではないかと思います。
__init__.pyの解説記事
__init__.pyとモジュール・パッケージ・名前空間の関係について、以下の記事が分かりやすかったです。単に「こう書けば動く」ではなく、概念が説明されていてありがたいです。