tl;dr

かなり古い WEB+DB PRESS (2011 Vol.63) の連載 (Ruby わくわくナビ第 1 回) にて, ライブラリの使い方を知る (学ぶ) 為にテストコードを書くことで, 単にサンプルコードを書き捨てるよりも, 「繰り返して実行可能」, 「いつでも参照可能」な自分用のリファレンスを作ることが出来るというメリットがあると書かれていた.

特集1は「現場で役立つ実践ノウハウWeb開発の「べし」「べからず」~危険なコード,腐るテスト,不安定なインフラからの脱却~」プログラミング,テスト,データベース,インフラなどの分野別に,「べし」「べからず」を整理して解説しています。特集2は「[実録]pi...

gihyo.jp

ということで, 組み込みライブラリ (クラス) のメソッドについて, 学習テストを書いていきつつ, メソッドの使い方を学んでいきたいと思う.

尚, 上述の記事では, Ruby 1.9 に追加されたメソッド達について触れられていたが, 本記事では Object クラスをスーパークラスとする各サブクラス全部を対象としていきたいと考えている.

どのクラスのどのメソッドにするのか

出来るだけ, 多くのメソッド達に触れていきたいと思いつつ, どうやって学習するクラスのメソッドを決めようと悩んだ末に, 以下のようなランダムに組み込みライブラリのメソッドを返すコードを書いた.

#!/usr/bin/env ruby
# file name: gacha.rb

class Class
  def subclasses
    subclasses = []
    ObjectSpace.each_object(singleton_class) do |k|
        subclasses << k if k.superclass == self
    end
    subclasses
  end
end

c = Object.subclasses.sample
methods_list = c.instance_methods(false).sample(3)

puts "クラス: #{c}"
methods_list.each { |m| puts "メソッド: #{m}" }

名づけて, メソッドガチャ. 本来の「ガチャ」を引く, 「ガチャ」を回すという言葉の意味はちゃんと理解出来ていないんだけど, 「クジを引く」くらいの意味を込めてみた. で, このメソッドガチャを実行して, 出力されたメソッド (インスタンスメソッド) について学習していく. ちなみに, サブクラスの取得方法については, 以下の記事を参考にさせて頂いた. 感謝.

このガチャを回すと以下のように出力される.

$ ruby gacha.rb 
クラス: Dir
メソッド: close
メソッド: each
メソッド: tell

$ ruby gacha.rb 
クラス: MatchData
メソッド: ==
メソッド: offset
メソッド: begin

$ ruby gacha.rb 
クラス: Enumerator
メソッド: feed
メソッド: size
メソッド: rewind

なんか面白い.

進め方

本記事については, 以下のように進める.

1.ガチャを引く
2. 出力されたメソッドの中から 1 ~ 3 つ選んで, そのメソッドについてリファレンスを確認する
3. テストコードを書いて, 挙動を確認する

書いた学習コードは, 以下の Github リポジトリで管理する.

ruby-gakushu-test - 学習テスト!, 学習テスト!, 学習テスト!

github.com

尚, 本記事で扱う環境は以下の通り. テストを書く場合には, Rspec または minitest を利用して書いていく. 特に明記しない場合には多分 minitest を利用する.

$ ruby --version
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]

$ bundle exec rspec --version
RSpec 3.7
  - rspec-core 3.7.1
  - rspec-expectations 3.7.0
  - rspec-mocks 3.7.0
  - rspec-support 3.7.1

$ bundle exec irb --version
irb 0.9.6(09/06/30)

$ bundle exec irb
irb(main):001:0> require 'minitest/autorun'
=> true
irb(main):002:0> MiniTest::Unit::VERSION
=> "5.11.3"

ということで, 第一回目

ガチャを回す

$ ruby gacha.rb 
クラス: Time
メソッド: tv_sec
メソッド: gmt_offset
メソッド: utc_offset

この中から Time#tv_sec (Time#to_i)Time#utc_offset (Time#gmt_offset) について学習テストしていく.

Time#tv_sec とは

以下, ドキュメントから引用.

起算時からの経過秒数を整数で返します。
to_i -> Integer tv_sec -> Integer

上記の通り, to_i メソッドと同義で, レシーバで指定した Time オブジェクトの整数値を返す.

Time#tv_sec の 学習テスト

以下のように書いた.

require 'minitest/autorun'
require "minitest/reporters"
Minitest::Reporters.use! [Minitest::Reporters::SpecReporter.new]

class GakushuTest < Minitest::Test
  def test_time_tv_sec
    assert_equal Time.new(2017, 11, 12, 1, 2, 3).tv_sec.class, Integer
  end

  def test_time_to_i
    assert_equal Time.new(2017, 11, 12, 1, 2, 3).to_i.class, Integer
  end
en

Time.new で Time オブジェクトを生成して, .tv_sec 及び to_i の結果が整数値であることを確認している.

$ bundle exec ruby 2018/05/06.rb 
Started with run options --seed 62380

GakushuTest
  test_time_tv_sec                                                PASS (0.00s)
  test_time_to_i                                                  PASS (0.00s)

Finished in 0.00092s
2 tests, 2 assertions, 0 failures, 0 errors, 0 skips

irb でも確認してみる.

irb(main):005:0> Time.new(2017, 11, 12, 1, 2, 3).tv_sec.class
=> Integer
irb(main):006:0> Time.new(2017, 11, 12, 1, 2, 3).tv_sec
=> 1510416123
irb(main):007:0> Time.new(2017, 11, 12, 1, 2, 3).to_i.class
=> Integer
irb(main):008:0> Time.new(2017, 11, 12, 1, 2, 3).to_i
=> 1510416123

フムフム.

Time#utc_offset とは

以下, ドキュメントより引用.

協定世界時との時差を秒を単位とする数値として返します。 地方時が協定世界時よりも進んでいる場合(アジア、オーストラリアなど) には正の値、遅れている場合(アメリカなど)には負の値になります。
utc_offset -> Integer gmt_offset -> Integer gmtoff -> Integer

協定世界時 (UTC) と地方時 (例えば, 日本標準時) の差分を秒で返す. gmt_offcetgmtoff でも同様の結果を返す.

Time#utc_offset の学習テスト

日本標準時は協定世界時 (UTC) に 9 時間 (60 * 60 * 9 = 32400) 加算した時刻となる為, Time#utc_offset の結果は 32400 となることを期待するので, 以下のようなテストコードを書いた.

require 'minitest/autorun'
require "minitest/reporters"
Minitest::Reporters.use! [Minitest::Reporters::SpecReporter.new]

class GakushuTest < Minitest::Test
  def test_time_zone
    assert_equal Time.now.zone, 'JST'
  end

  def test_time_utc_offset
    assert_equal Time.now.utc_offset, 32400
  end

  def test_time_gmt_offset
    assert_equal Time.now.gmt_offset, 32400
  end

  def test_time_gmtoff
    assert_equal Time.now.gmtoff, 32400
  end
end

テストを走らせると以下のように出力される. ちなみに, 任意のテストメソッドだけ実行したい場合には -n オプションを利用する. また -n オプションは正規表現が利用可能なのが嬉しい.

$ bundle exec ruby 2018/05/06.rb -n test_time_zone -n /off/
Started with run options -n test_time_zone -n /off/ --seed 50955

GakushuTest
  test_time_utc_offset                                            PASS (0.00s)
  test_time_gmt_offset                                            PASS (0.00s)
  test_time_gmtoff                                                PASS (0.00s)

Finished in 0.00091s
3 tests, 3 assertions, 0 failures, 0 errors, 0 skips

irb でも確認する.

irb(main):001:0> Time.now
=> 2018-05-06 14:00:47 +0900
irb(main):002:0> Time.now.zone
=> "JST"
irb(main):003:0> Time.now.utc_offset
=> 32400
irb(main):004:0> Time.now.gmt_offset
=> 32400
irb(main):005:0> Time.now.gmtoff
=> 32400

フムフム.

以上

いつまで続くかわからないけど, 多くのメソッドとの出会いを楽しみにしている.

元記事はこちら

Ruby の組み込みライブラリ (クラス) の「学習テスト」を書いて, 出来るだけ多くのメソッドと出会いたい (1)