Ruby ベストプラクティス

作成日: 2010-06-12
最終更新日:

本書の目的

Ruby で問題解決に取り組む際のやり方を身に付ける。

付録は次の3点である。

テキスト処理

正規表現を使うコツは次のように紹介されている。

第2の点。ショートカットとは、特定の文字集合を表す記法だ。
. \s \S \w \W \d \D

第3の点。アンカーは「錨」の意味で、特定の文字にマッチするのではなく、 文字と文字の関係や文字列の関係にマッチする。次の記法がある。
\A \Z \Z ^ $ \b

最後の点は最近身に染みて感じたことである。

文化の壁を取り払う

L10N (Localization)と M17N (Multilingualization) について述べられている。 ここを検証したかったが、sinatra と gibberish::simple の稼働がうまくできず諦めた。 再度挑戦したい。(2010-06-12)

Sinatra 再挑戦

再度挑戦と書いてから 10 年が経過した。今、試してみてみることにした。 WSL の Ubuntu 20.04 で確認している。 まず、果たして Sinatra は今でも使えるのだろうか。 調べてみると、今でも使えそうである。インストールしてみることにした。

$ gem install sinatra
(前略)
Sucessfully installed sinatra-2.1.0
(中略)
Parsing documentation for sinatra-2.1.0
Installing ri documentation for sinatra-2.1.0

ここでプロンプトが返ってこない。

^CERROR:  Interrupted
$ gem install sinatra
Successfully installed sinatra-2.1.0
Parsing documentation for sinatra-2.1.0
Done installing documentation for sinatra after 1 seconds
1 gem installed
$ gem install sinatra-reloader
(中略)
3 gems installed

なんとか終わったようだ。いよいよ Sinatra である。

Sinatra で WEB アプリケーションを動かす

Sinatra が動作することを試すため、次のようにフォルダとファイルを配置した。 ch7 というのは、対象が本書の第7章にあることを反映している。

ch7 +
    + rbs.rb
        |
        + views + rps_start.erb
                + rps_end.rb

p.216 の上段の ERB テンプレートが rps_start.rb 、 下段の ERP テンプレートが rps_end.rb 、 p.217 のソースが rbs.rb である。 なお、p.217 のソースの、require "sinatra" の次に、 require "sinatra/reloads" を加えておくといいだろう。 このアプリケーションは次のように起動する。

$ ruby rbs.rb

WEBrick による簡易サーバが動いた。 ブラウザから、http://localhost:4567/rps を起動すると、 本書図 7-1 の画面が現れる。この画面で、 Rock, Paper, Scissors のいずれを選択して Shoot! ボタンをクリックすると、 図 7-2 の画面が表示される。ひとまずは Sinatra の動作が確認できた。

次は gibberish である。Sinatra は使えたが、こちらはどうか。 調べてみると、さすがに今は更新されていないようだ。 もともと gibberish は暗号化・復号を行なうライブラリである。 この用途そのものが、既に別ライブラリに道を譲ったようなのだ。

では、他に M17N や L10N を実現できるライブラリはあるのだろうか。 どうやら、Ruby on Rails (RoR) に適用されることを目指して作られた i18n というライブラリがあることがわかった。これが事実上の Ruby での標準らしい。 I18N は Internationalization の略で、国際化を意味する。 では、i18n はどのように使うのか。今回は RoR とは無関係だから、 これに適した用例があるのだろうか。i18n 単独で使う用法があれば知りたい。 また、Sinatra で使えればその方法も知りたい。

gibberish を用いた本書の方法では、言語コード xx に応じて URI を /rps/xx/ とすることで対応している。 たとえば日本語であれば、 xx = ja として /rps/ja でグー・パー・チョキ、 フランス語であれば xx = fr だから /rps/fr で Caillou, Feuille, Ciseaux のように変えている。

sinatra-r18n

ということで調べると、https://hawksnowlog.blogspot.com/2020/10/ruby-i18n-samples-with-sinatra.html というブログの記事が見つかった。これを参考にできないだろうか。

このブログ記事では、ブラウザなどのユーザーエージェントが保持する Accept-Language ヘッダを見て言語を分けている。 WEB のアプリケーションであれば、Accept-Language ヘッダを使うのがスマートだと思う。

さらにいろいろ調べてみると、sinatra-r18n というライブラリが見つかったので、 これを使ってみるのがよいのではないかと考えた。 2020-11-03 時点でバージョンは 4.0.0 である。
https://rubygems.org/gems/sinatra-r18n/

まずはインストールしてみた。

$ gem install sinatra-r18n
	

さて、r18n は、sinatra だけではないらしい。 https://github.com/r18n/r18n を見ると、RoR 、Sinatra、 デスクトップ(いわゆる web アプリケーションではないことを指すのだろう) の例があるようだ。 https://github.com/r18n/r18n/tree/master/sinatra-r18n の How To に従って確認する。
なお、(省略)とした個所は、 上記の URL を見てほしい。

1. 翻訳用のディレクトリ ./i18n を作る。これは決め打ちのようだ。 現在のディレクトリは、ruby hoge.rb の hoge.rb があるディレクトリと 考えてよい。

2. 翻訳ファイルを ./i18n/ の下、言語名をファイル名に含めて作成する。 例:英語なら en.yml 、アメリカ英語なら en-us.yml など。
./i18n/en.yml の例

post:
 friends: Post only for friends
 tags: Post tags are %1

comments: !!pl
 0: No comments
 1: One comment
 n: '%1 comments'

html: !!html
 <b>Don't escape HTML</b>

3. Sinatra アプリケーションに r18n を追加する。
require 'sinatra/i18n'
これは追加である。もとのアプリケーションで、
require 'sinatra/i18n'
require 'sinatra/reload'
があれば、 当然そのままである。
アプリケーションのクラスが Sinatra::Base を継承しているときは、 追記が必要だ(省略)。

4. locale を URL に追記する(省略)。 ユーザーが locale を変更するときはセーブする(省略)。

5. ビューの翻訳メッセージを使う。HAML の例:

	%p= t.post.friends
	%p= t.post.tags(@post.tags.join(', '))
	
	%h2= t.comments(@post.comments.size)

t がグローバルなオブジェクトである。 t のメンバーに post や comments があって、 post メンバーに friends や tags などがある。これは yml ファイルに対応している。

6. locale に対応した時間や数値を表示する(省略)。

7. 利用可能な翻訳を表示する。HAML の例(省略)。

さらなる苦闘

ここまでの情報でうまくいったと思い込んだが、誤りだった。 だいぶ苦労したが、苦労したところを明かすのは恥ずかしいので隠す。 言語ファイルを合わせて次のディレクトリとファイルで構成する。

    ch7 +
        + rps_i18n.rb
        |
        + views + rps_start.erb
        |       + rps_end.rb
        + i18n  + en.yml
                + ja.yml
                + fr.yml		
                + eo.yml

rps_i18n.rb

サーバーとなるプログラムの中心である。

# rps_i18n.rb
# Ruby ベストプラクティス p.217 をもとに改変
# サーバー
# $ ruby rps_i18n.rb
# クライアント
# http://localhost:4567/rps

require 'sinatra'
require 'sinatra/reloader'
require 'sinatra/r18n'
 
get '/rps' do
  erb :rps_start
end
  
post '/rps' do
  @player_weapon    = params[:weapon]
  @opponent_weapon  = %w[Rock Paper Scissors].sample
  erb :rps_end
end
	
helpers do
  def end_game_message
    return "#{t.rps.tie}" if @player_weapon == @opponent_weapon

    winning_combos = [["Paper","Rock"],["Rock","Scissors"],["Scissors","Paper"]]
    if winning_combos.include?([@player_weapon, @opponent_weapon])
      "#{t.rps.win}" 
    else
      "#{t.rps.loose}"
    end
  end
  def weapon_name(weapon)
    t.rps.send(weapon.downcase)
  end
end

rps_start.rb

じゃんけんの手を決める画面である。

<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <form method="post">
      <% ["Rock", "Paper", "Scissors"].each do |weapon| %>
      <input type="radio" name="weapon" value="<%= weapon %>">  
      <%= weapon_name(weapon) %>
      <% end %>
      <input type="submit" value="<%= t.rps.shoot %>">
    </form>
  </body>
</html>

rps_end.rb

結果を示す画面である。

<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
      <p> <%= t.rbs.you(@get) %> : <%= weapon_name(@player_weapon) %></p>
      <p> <%= t.rbs.opponent(@get) %> : <%= weapon_name(@opponent_weapon) %></p>
      <p> <%= end_game_message %></p>
  </body>
</html>

en.yml

英語である。決着をつけるタイミングは Shoot であることを初めて知った。 映画で本番撮影をする掛け声が Shoot! だったと思う。

rps:
  win: You win
  loose: You loose
  tie: Tie
  rock: Rock
  paper: Paper
  scissors: Scissors
  shoot: Shoot
  you: You
  opponent: Opponent

ja.yml

日本語である。日本語の順番ではグー、チョキ、パーなのだが、この順番までは直せなかった。 なお、表示を直すのは、rps_start.rb で行う必要がある。

rps:
  win: あなたの勝ち
  loose: あなたの負け
  tie: あいこ
  rock: グー
  paper: パー
  scissors: チョキ
  shoot: じゃんけん
  you: あなた
  opponent: 相手

fr.yml

フランス語である。あいこを表す「Égalité」にどこか既視感があった。少し考えて、 フランス共和国の標語の「平等」と同じであることに気づいた。 なお、本書では、Egalité のように頭文字にアクサンテギュ(アクセント記号)がない形で印刷されている。 大文字の場合は記号をつけなくてもいいのだが、せっかくなのでここでは記号をつけている。

rps:
  win: Tu gagnes
  loose: Tu perds
  tie: Égalité
  rock: Caillou
  paper: Feuille
  scissors: Ciseaux
  shoot: On y va !
  you: Toi
  opponent: Adversaire

eo.yml

エスペラントである。じゃんけん決着のためのかけ言葉は Ek! としたが、 自信がない。

rps:
  win: Vi gajnas
  loose: Vi perdas
  tie: Egalforta
  rock: Ŝtono
  paper: Papero
  scissors: Tondilo
  shoot: Ek!
  you: Vi
  opponent: Adverso

これで、$ ruby rps_i18n.rb としてサーバーを起動する。 そして、ブラウザの URL で http://localhost:4567/rps を指定する。 ブラウザの言語のオプションで、表示をさせたい言語を一番最初に持っていけば、 その言語で表示される。

第5章 関数型プログラミング

再帰関数とメモ化について確認した。

再帰関数では速度が低下するというのは、 現在の 2.7.2 でも変わっていないようだ。

メモ化についての確認は一部にとどまる。配列のメモ化についてはその効果を確かめた。 なお、p.153 のメモ化のコード

def fib(n)
  @series || = []
  @series[n] || = fib(n-1) + fin(n-2)
end

は、このままでは動かない。まず、誤植がある。そして、n=0 と n= 1 の停止条件もない。 実際には p.152 のコード断片と見比べて、次のような書き方になる。

  def fib(n)
  @series || = []
  @series[n] || = (0..1).include?(n) ? n : fib(n-2) + fib(n-1)
end  

こんなことがあるから、やはりこの本は上級者向きなのだと思う。私は初心者であるから、 難しい。

全体を通して

コードの例は下記にある。
https://github.com/practicingruby/rbp

1章 テストでコードを駆動する

この章の題名は偶然俳句に近い。1章を「第1章」とすれば、「だいいっしょう テストでコードを くどうする」と読めるからだ。 それはともかく、最初の章なのに、読めない。読めるところを探すと、1.3 節ならば読めそうなことがわかった。

この章では、Test::Unit を使っているテストコードで、 must() メソッドなるものが頻出している。これは、本書の p.5 では、 activesupport ライブラリの test() メソッドに基づいたカスタム追加となっている。 私はこの must() メソッドを使おうとしたが、次のエラーが返ってくるだけで、結局使えなかった。

Traceback (most recent call last):
1: from test_volume_bad.rb:14:in `<main>'
test_volume_bad.rb:15:in `<class:VolumeTest>': undefined method `must' for VolumeTest:Class (NoMethodError)

本書に掲載されている例をみたが、メソッドの名前の代わりに「そうあってしかるべき」 である条件が英語の文法通りに並べられているのだった。だから、 わざわざ must メソッドを使わずとも(使う方法がわからなかったからだが)コメントとメソッド名を適切に組み合わせたら、 それで問題はない。

2章 美しい API を設計する

題だけでは、「うつくしい えーぴーあいを せっけいする」で、字余りだ。 それはともかく、私のような低レベルでこの章で役に立ったことは、 attr_read、attr_writer、attr_accessor を使おう、 ということぐらいだ。

ほかには、「ドッグフードを食べる」という言い回しが、 「ソフトウェア開発者や開発会社が、開発したソフトウェアを自社で実際に業務などで使うこと」 を意味するのが面白かった。

3章 動的な機能を使いこなす

動的な機能と聞いただけで、使いこなせない、勘弁してください、と叫んでしまいそうだ。 3.2.2 項では、method_missing() と send() を使ってメッセージを扱うことが主題である。 この send() というメソッドは、引数にシンボルまたは文字列をとって、 そのシンボルまたは文字列のメソッドを実行する、というメソッドである。 私はこのようなメソッドがわからなかったために、 8章のじゃんけんプログラムの書き直しに大変苦労したのだった。 send がどこで使われているかを見たければ、send をこのページで検索すればいい。 weapon_name の定義で出てくる。それはともかく、動的な機能は勘弁してください、である。

書誌情報

書 名Ruby ベストプラクティス
著 者Gregory Brown
監訳者高橋 征義
訳 者笹井 崇司
発行日2010 年 3 月 25 日 初版第1刷
発行所オライリー・ジャパン
発売元オーム社
定 価3200 円(税別)
サイズ
ISBN978-4-87311-445-3
その他越谷市南部図書室で借りて読む
NDC

まりんきょ学問所Rubyの浮き輪 > Ruby ベストプラクティス


MARUYAMA Satosi