【Ruby】【book】 「プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで」の学習記録

下記の「プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで」を使った学習記録のページです。
本ページのプログラムは「プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで」をもとにして作成したものも多数ありますが、大半は本の内容とは関係無いものです。


第1章 本書を読み進める前に

 1.1 イントロダクション
 1.2 本書の概要
 1.3 Rubyについて
 1.4 Rubyのインストール
 1.5 エディタ/ IDEについて
 1.6 Rubyを動かしてみる
 1.7 本書のサンプルコードがうまく動かない場合
 1.8 この章のまとめ

第2章 Rubyの基礎を理解する

 2.1 イントロダクション
 2.2 Rubyに関する基礎知識
 2.3 文字列
 2.4 数値
 2.5 真偽値と条件分岐
 2.6 メソッドの定義
 2.7 例題:FizzBuzzプログラムを作成する
 2.8 文字列についてもっと詳しく
 2.9 数値についてもっと詳しく
 2.10 真偽値と条件分岐についてもっと詳しく
 2.11 メソッド定義についてもっと詳しく
 2.12 その他の基礎知識
 2.13 この章のまとめ

第3章 テストを自動化する

 3.1 イントロダクション
 3.2 Minitestの基本
 3.3 FizzBuzzプログラムのテスト自動化
 3.4 この章のまとめ

第4章 配列や繰り返し処理を理解する

 4.1 イントロダクション
 4.2 配列
 4.3 ブロック
 4.4 ブロックを使う配列のメソッド
   map_collect.rb
   reject.rb
   select.rb
   find.rb
   inject.rb
   amp_colon.rb
 4.5 範囲(Range)
   range_array.rb
   range_nm.rb
   range_case.rb
   range_array_to_s.rb
   range_array_to_s.rb
   range_num_to_a.rb
 4.6 例題:RGB変換プログラムを作成する
   README.txt
   rgb.rb
   rgb_test.rb
   README.txt
   rgb.rb
   rgb_test.rb
 4.7 配列についてもっと詳しく
 4.8 ブロックについてもっと詳しく
 4.9 さまざまな繰り返し処理
 4.10 繰り返し処理用の制御構造
 4.11 この章のまとめ

第5章 ハッシュやシンボルを理解する

 5.1 イントロダクション
 5.2 ハッシュ
   hash_1.rb
   hash.rb
 5.3 シンボル
 5.4 続・ハッシュについて
 5.5 例題:長さの単位変換プログラムを作成する
 5.6 ハッシュについてもっと詳しく
 5.7 シンボルについてもっと詳しく
 5.8 この章のまとめ
   string_match.rb
   string_replace.rb
   string_gsub_join.rb

第6章 正規表現を理解する

 6.1 イントロダクション
 6.2 正規表現って何?
 6.3 Rubyにおける正規表現オブジェクト
   system_git.rb
 6.4 例題:Rubyのハッシュ記法を変換する
 6.5 正規表現オブジェクトについてもっと詳しく
 6.6 この章のまとめ

第7章 クラスの作成を理解する

 7.1 イントロダクション
 7.2 オブジェクト指向プログラミングの基礎知識
 7.3 クラスの定義
 7.4 例題:改札機プログラムの作成
 7.5 selfキーワード
 7.6 クラスの継承
 7.7 メソッドの公開レベル
 7.8 定数についてもっと詳しく
 7.9 さまざまな種類の変数
 7.10 クラス定義やRubyの言語仕様に関する高度な話題
 7.11 この章のまとめ

第8章 モジュールを理解する

 8.1 イントロダクション
 8.2 モジュールの概要
 8.3 モジュールのミックスイン(includeとextend)
 8.4 例題:deep_freezeメソッドの作成
 8.5 ミックスインについてもっと詳しく
 8.6 モジュールを利用した名前空間の作成
 8.7 関数や定数を提供するモジュールの作成
 8.8 状態を保持するモジュールの作成
 8.9 モジュールに関する高度な話題
 8.10 この章のまとめ

第9章 例外処理を理解する

 9.1 イントロダクション
 9.2 例外の捕捉
 9.3 意図的に例外を発生させる
 9.4 例外処理のベストプラクティス
 9.5 例題:正規表現チェッカープログラムの作成
 9.6 例外処理についてもっと詳しく
 9.7 この章のまとめ

第10章 yieldとProcを理解する

 10.1 イントロダクション
 10.2 ブロックを利用するメソッドの定義とyield
 10.3 Procオブジェクト
 10.4 例題:ワードシンセサイザーの作成
 10.5 Procオブジェクトについてもっと詳しく
 10.6 この章のまとめ

第11章 Rubyのデバッグ技法を身につける

 11.1 イントロダクション
 11.2 バックトレースの読み方
 11.3 よく発生する例外クラスとその原因
 11.4 プログラムの途中経過を確認する
 11.5 汎用的なトラブルシューティング方法
 11.6 この章のまとめ

第12章 Rubyに関するその他のトピック

 12.1 イントロダクション
 12.2 日付や時刻の扱い
   dir_tmpdir.rb
   dir_mkdir.rb
 12.3 ファイルやディレクトリの扱い
   system_git.rb
 12.4 特定の形式のファイルを読み書きする
   csv_read.rb
   yaml_add.rb
   yaml_has_key.rb
   orig.yml
   yaml_print.rb
   yaml_walk.rb
   yaml_dump.rb
   yaml_tr.rb
 12.5 環境変数や起動時引数の取得
 12.6 eval,バッククオートリテラル,sendメソッド
 12.7 Rake
 12.8 gemとBundler
 12.9 この章のまとめ

付録 Ruby on Railsの習得に向けた予備知識

 A.1 イントロダクション
 A.2 Railsの独自拡張になっている機能を理解する
 A.3 フレームワークの変化の速さに追従する
 A.4 アプリケーション設計に関する知識
 A.5 Web技術に関する知識
 A.6 データベースに関する知識
 A.7 セキュリティに関する知識
 A.8 テストの自動化に関する知識
 A.9 GitやGitHubに関する知識
 A.10 サーバや運用に関する知識
 A.11 gemに関する知識と定期的なアップデート

 
 

4.4.1

 

map_collect.rb
#!/usr/bin/env ruby

# 各要素を 10倍した配列を作る
numbers = [1,2,3,4,5]
new_numbers = []
numbers.each { |n| new_numbers << n * 10 }
p new_numbers   #=> [10, 20, 30, 40, 50]

# map メソッドを使うとブロックの戻り値が配列の要素となる新しい配列が作成される
map_new_numbers = numbers.map { |n| n * 10 }
print map_new_numbers   #=> [10, 20, 30, 40, 50]

 
 

4.4.2

 

reject.rb
#!/usr/bin/env ruby
# select の反対
# - ブロックを評価して偽ならば配列を返す

numbers = [1,2,3,4,5]
odd_numbers = numbers.reject { |n| n.even? }

p odd_numbers #=> [1, 3, 5]

 

select.rb
#!/usr/bin/env ruby
# select:
# - エイリアスメソッド = find_all
# - ブロックを評価して真ならば配列を返す

numbers = [1,2,3,4,5]
even_numbers = numbers.select { |n| n.even? }

p even_numbers #=> [2, 4]

 
 

4.4.3

 

find.rb
#!/usr/bin/env ruby
# find:
# - エイリアスメソッド = detect
# - ブロックを評価して真ならば最初の要素を返す

numbers = [1,2,3,4,5]
even_number = numbers.find { |n| n.even? }

p even_number #=> 2

 
 

4.4.4

 

inject.rb
#!/usr/bin/env ruby
# inject:
# - エイリアスメソッド = reduce
# - 畳み込み演算
# 
# 1 回目: result = 0、n = 1 で、0 + 1 = 1。これが次の result に入る。
# 2 回目: result = 1、n = 2 で、1 + 2 = 3。この結果が次の result に入る。

numbers = [1,2,3,4]
sum = numbers.inject(0) { |result, n| result + n } # 0 は初期値
p sum #=> 10

mon_to_sat= ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
day_of_the_week = mon_to_sat.inject('Sun') { |result, s| result + s } # 'Sun' が初期値
p day_of_the_week #=> "SunMonTueWedThuFriSat"

 
 

4.4.5

 

amp_colon.rb
#!/usr/bin/env ruby
# &:
# 下記条件をすべて満たした場合のみ使用できる記法
# - ブロック引数が1個
# - ブロック内で呼び出すメソッドに引数が無い
# - ブロック内ではメソッドの呼び出しが1回のみ

p ['ruby', 'java', 'perl'].map { |s| s.upcase }
p ['ruby', 'java', 'perl'].map(&:upcase)
#=> ["RUBY", "JAVA", "PERL"]

p [1, 2, 3, 4, 5].select { |n| n.odd? }
p [1, 2, 3, 4, 5].select(&:odd?)
#=> [1, 3, 5]

 
 

4.5

 

range.rb
#!/usr/bin/env ruby

# 範囲オブジェクト
range = 'a'..'e'
range.each { |n|
  puts n
}

range = 1..5  # 1 <= x <= 5
p range.include?(1)     #=> true
p range.include?(4.9)   #=> true
p range.include?(5)     #=> true
p range.include?(6)     #=> false

range = 1...5 # 1 <= x < 5
p range.include?(1)     #=> true
p range.include?(4.9)   #=> true
p range.include?(5)     #=> false
p range.include?(6)     #=> false

 
 

4.5.1

 

range_array.rb
#!/usr/bin/env ruby

# 配列における範囲オブジェクト

a = [1, 2, 3, 4, 5]

# 2番目から4番目までの要素を取得する
p a[1..3]   #=> [2, 3, 4]

# 2番目から4番目までの文字を取得する
a = 'abcdef'
p a[1..3]   #=> "bcd"

 
 

4.5.2

 

range_nm.rb
#!/usr/bin/env ruby
# n以上 m以下、n以上 m未満
# 不等号を使う場合
def liquid?(temperature)
  0 <= temperature && temperature < 100
end

p liquid?(-1)   #=> false
p liquid?(0)    #=> true  
p liquid?(99)   #=> true  
p liquid?(100)  #=> false 

def liquid_range?(temperature)
  (0...100).include?(temperature)
end

p liquid_range?(-1)   #=> false 
p liquid_range?(0)    #=> true  
p liquid_range?(99)   #=> true  
p liquid_range?(100)  #=> false 

 
 

4.5.3

 

range_case.rb
#!/usr/bin/env ruby
# case文で範囲オブジェクトを使う
# 料金表
def charge(age)
  case age
  when 0..5
    # 0〜5歳までの場合
    0
  when 6..12
    # 6〜12歳までの場合
    300
  when 13..18
    # 13〜18歳までの場合
    600
  else
    # 19歳〜の場合
    1000
  end
end

p charge(3)   #=> 0    
p charge(12)  #=> 300  
p charge(16)  #=> 600  
p charge(25)  #=> 1000 

 
 

4.5.4

 

range_array_to_s.rb
#!/usr/bin/env ruby

# 値が連続する配列における範囲オブジェクト
p (1..5).to_a           #=> [1, 2, 3, 4, 5] 
p (1...5).to_a          #=> [1, 2, 3, 4]    
 
p ('a'..'e').to_a       #=> ["a", "b", "c", "d", "e"] 
p ('a'...'e').to_a      #=> ["a", "b", "c", "d"]      
 
p ('bad'..'bag').to_a   #=> ["bad", "bae", "baf", "bag"] 
p ('bad'...'bag').to_a  #=> ["bad", "bae", "baf"]        

 
 

4.5.5

 

range_array_to_s.rb
#!/usr/bin/env ruby

# 値が連続する配列における範囲オブジェクト
p (1..5).to_a           #=> [1, 2, 3, 4, 5] 
p (1...5).to_a          #=> [1, 2, 3, 4]    
 
p ('a'..'e').to_a       #=> ["a", "b", "c", "d", "e"] 
p ('a'...'e').to_a      #=> ["a", "b", "c", "d"]      
 
p ('bad'..'bag').to_a   #=> ["bad", "bae", "baf", "bag"] 
p ('bad'...'bag').to_a  #=> ["bad", "bae", "baf"]        

 

range_num_to_a.rb
#!/usr/bin/env ruby

# 範囲オブジェクト
numbers = (1..4).to_a
sum = 0
numbers.each { |n| sum += n }
p sum   #=> 10

 
 

4.6

 
 

4.6.1

 

README.txt
.
├── README.txt
├── lib
│   └── rgb.rb
└── test
    └── rgb_test.rb

 

rgb.rb
#!/usr/bin/env ruby
#

def to_hex(r, g, b)
  '#' +
    r.to_s(16).rjust(2, '0') +
    g.to_s(16).rjust(2, '0') +
    b.to_s(16).rjust(2, '0') 
end

 

rgb_test.rb
#!/usr/bin/env ruby

require 'minitest/autorun'
require './lib/rgb'

class RgbTest < Minitest::Test
  def test_to_hex
    assert_equal '#000000', to_hex(0,0,0)
    assert_equal '#ffffff', to_hex(255,255,255)
    assert_equal '#043c78', to_hex(4,60,120)
  end
end

 
 

4.6.2-1

 

README.txt
.
├── README.txt
├── lib
│   └── rgb.rb
└── test
    └── rgb_test.rb

 

rgb.rb
#!/usr/bin/env ruby
# rjust メソッドを使って、右寄せ、かつ、2桁(左記は0埋め)、にする
# 以下のように書いても同じである
def to_hex(r, g, b)
  # 引数 r,g,b を配列に格納して使う
  hex = '#'
  [r, g, b].each do |n|
    hex += n.to_s(16).rjust(2, '0')
  end
  hex
end

 

rgb_test.rb
#!/usr/bin/env ruby

require 'minitest/autorun'
require './lib/rgb'

class RgbTest < Minitest::Test
  def test_to_hex
    assert_equal '#000000', to_hex(0,0,0)
    assert_equal '#ffffff', to_hex(255,255,255)
    assert_equal '#043c78', to_hex(4,60,120)
  end
end

 
 

4.6.2-2

 

README.txt
.
├── README.txt
├── lib
│   └── rgb.rb
└── test
    └── rgb_test.rb

 

rgb.rb
#!/usr/bin/env ruby
# inject, と rjust メソッドを使って、右寄せ、かつ、2桁(左記は0埋め)、にする
def to_hex(r, g, b)
  # 引数 r,g,b を配列に格納して使う
  [r, g, b].inject('#') do |hex, n|
    hex + n.to_s(16).rjust(2, '0')
  end
end

 

rgb_test.rb
#!/usr/bin/env ruby

require 'minitest/autorun'
require './lib/rgb'

class RgbTest < Minitest::Test
  def test_to_hex
    assert_equal '#000000', to_hex(0,0,0)
    assert_equal '#ffffff', to_hex(255,255,255)
    assert_equal '#043c78', to_hex(4,60,120)
  end
end

 
 

4.6.3

 

README.txt
.
├── README.txt
├── lib
│   └── rgb.rb
└── test
    └── rgb_test.rb

 

rgb.rb
#!/usr/bin/env ruby
# inject, と rjust メソッドを使って、右寄せ、かつ、2桁(左記は0埋め)、にする
def to_hex(r, g, b)
  # 引数 r,g,b を配列に格納して使う
  [r, g, b].inject('#') do |hex, n|
    hex + n.to_s(16).rjust(2, '0')
  end
end

def to_ints(hex)
  [0,0,0]
end

 

rgb_test.rb
#!/usr/bin/env ruby

require 'minitest/autorun'
require './lib/rgb'

class RgbTest < Minitest::Test
  def test_to_hex
    assert_equal '#000000', to_hex(0,0,0)
    assert_equal '#ffffff', to_hex(255,255,255)
    assert_equal '#043c78', to_hex(4,60,120)
  end
  def test_to_ints
    assert_equal [0,0,0], to_ints('#000000')
  end
end

 
 

5.2.1

 

hash_1.rb
#!/usr/bin/env ruby
#
# 空のハッシュを作る

h = { 'japan' => 'yen', 'us' => 'dopllar', "india" => 'rupeeh' }

p h   #=> {"japan"=>"yen", "us"=>"dopllar", "india"=>"rupeeh"}

 

hash.rb
#!/usr/bin/env ruby
#
# 空のハッシュを作る

h = { 'japan' => 'yen', 'us' => 'dopllar', "india" => 'rupeeh' }

p h   #=> {"japan"=>"yen", "us"=>"dopllar", "india"=>"rupeeh"}

 
 

5.8.1

 

string_match.rb
#!/usr/bin/env ruby
require  'fileutils'

list = [
          '/lib/systemd/libsystemd-shared-237.so',
          '/lib/systemd/network/',
          '/lib/systemd/resolv.conf',
          '/lib/systemd/systemd-initctl',
          '/lib/systemd/systemd-remount-fs',
          '/lib/systemd/systemd-sysv-install'
       ]

def my_split( list )
    list.each { |path|
        if m = path.match(%r@/(.*)/systemd/systemd-(.*)@)
            p m[2]
        end
    }
end

my_split( list )

#=> "initctl"
#   "remount-fs"
#   "sysv-install"

 

string_replace.rb
#!/usr/bin/env ruby
#
# /lib/systemd/system-initctl を /etc/systemd/tmp/system-initctl にリネームする
#
require  'fileutils'

systemd_list = [
          '/lib/systemd/libsystemd-shared-237.so',
          '/lib/systemd/network/',
          '/lib/systemd/resolv.conf',
          '/lib/systemd/systemd-initctl',
          '/lib/systemd/systemd-remount-fs',
          '/lib/systemd/systemd-sysv-install'
       ]


def my_rename!( list )

    list.each { |path|
        # 入力データである systemd_list の object_id は呼び出し元と同じである
        # p path.object_id          # object_id 確認処理

        # 新たに作成された object_id であり systemd_list とは別物である
        dname = File.dirname( path ) 
        bname = File.basename( path )

        # 新規パスを作成する
        dname.gsub!(/^\/lib/, '/etc')
        newpath = File.join(dname, '/tmp/', bname)

        # 入力データである systemd_list に対して置換処理をする (破壊)
        path.gsub!(/(.*)/, newpath)
    }
end

# systemd_list.each{ |l| p l.object_id }    # object_id 確認処理

# 破壊前の状態を表示する
p systemd_list.each { |s| p s }

my_rename!( systemd_list )

# 破壊後の状態を表示する
p systemd_list.each { |s| p s }

 

string_gsub_join.rb
#!/usr/bin/env ruby
#
# /lib/systemd/system-initctl を /etc/systemd/tmp/system-initctl にリネームする
#
require  'fileutils'

list = [
          '/lib/systemd/libsystemd-shared-237.so',
          '/lib/systemd/network/',
          '/lib/systemd/resolv.conf',
          '/lib/systemd/systemd-initctl',
          '/lib/systemd/systemd-remount-fs',
          '/lib/systemd/systemd-sysv-install'
       ]


def my_rename( list )

    list.each { |path|
        dname = File.dirname( path )
        bname = File.basename( path )

        dname.gsub!(/^\/lib/, '/etc')
        p File.join(dname, '/tmp/', bname)
    }
end


my_rename( list )

 
 

6.3.1

 

system_git.rb
#!/usr/bin/env ruby
#
# 下記の操作をするのみ
# ---------------------------
# 1. /tmp/ 以下に作業ディレクトリを作成する
# ---------------------------
# % mkdir /tmp/clone_test
# % test -d /tmp/clone_test
# % export now="`date "+%Y.%m.%d-%H.%M.%S`"
# % mkdir /tmp/clone_test/test.XXXX.${now}
#
# ---------------------------
# 2. git clone してくる
# ---------------------------
# % cd /tmp/clone_test/test.XXXX.${now}
# % git clone http://github.com/tswicegood/Dolt
# % cd Dolt
# % ls -A .
# % cd $CLONED_DIRNAME/Dolt
# % test -f README.markdown
#
# ---------------------------
# 3. 所定のファイルを変更する
# ---------------------------
# % test ! -f README.markdown && exit 1
# % export ver=(sed -n 's/As of v\(.*\),\(.*\)/\1/p')
# % echo $ver > README.markdown

require   'fileutils'
require   'tmpdir'

GIT_REPOSITORY = "http://github.com/tswicegood/Dolt"
CLONED_DIRNAME = "/tmp/clone_test"

FileUtils.remove_entry_secure(CLONED_DIRNAME) if Dir.exist?(CLONED_DIRNAME)

# mkdir /tmp/clone_test
begin
  Dir.mkdir(CLONED_DIRNAME, 0755)
rescue => e
  STDERR.puts "Error: could not mkdir <#{CLONED_DIRNAME}>" 
  exit 1
end

# test -d /tmp/clone_test
if Dir.exist?(CLONED_DIRNAME)
  puts "OK: mkdir #{CLONED_DIRNAME}" 
else
  STDERR.puts "Error: could not mkdir <#{CLONED_DIRNAME}>" 
end

# 日時から一意性文字列を作る
# now=`date "+%Y.%m.%d-%H.%M.%S"`
now = Time.now.strftime("%Y.%m.%d-%H.%M.%S")

# 一時ディレクトリにプレフィクスとサフィックスを付与する
# mkdir /tmp/clone_test/test.XXXX.2018.04.01-18.00.01
# cd /tmp/clone_test/test.XXXX.2018.04.01-18.00.01
dir = Dir.mktmpdir( ['test.', '.'+now], CLONED_DIRNAME )
begin
  Dir.chdir(dir)
  puts Dir.getwd 
ensure
  puts "Debug: FileUtils.remove_entry_secure(#{dir})"
  # FileUtils.remove_entry_secure(dir)  # 後始末
end

# git clone http://github.com/tswicegood/Dolt
# cd Dolt
# ls -A .
begin
  system('git', 'clone', GIT_REPOSITORY)
rescue => e
  STDERR.puts "Error: system('git', 'clone', #{GIT_REPOSITORY})"
  exit 1
end

# cd $CLONED_DIRNAME/Dolt
# test -f README.markdown
begin
  path = File.join(dir, 'Dolt')
  Dir.chdir(path)
  puts "DEBUG: #{Dir.getwd()}"
rescue => e
  STDERR.puts "Error: At #{Dir.getwd} by "
  STDERR.puts e.message
  exit 1
end

# test ! -f README.markdown && exit 1
exit 1 unless File.exist?('README.markdown')
puts "---------------------------------------"

#「As of v0.3」といった行が出現したら、次の操作をする
# 1. +1 加算して 1.3 とする
# 2.「1.3」のみを README.markdown に書き出す 
# 3. README.markdown の中身が「1.3」のみとなるようにする

version = 0

File.open( 'README.markdown', "r+" ) { |f|
  line = f.read()
  line.match(%r@^As of v(.+?),(.+?)@) { |m|
    version = m[1].to_f + 1
  }
}

begin
  f = File.open( 'README.markdown', "w" )
  f.write(version)
rescue => e
  STDERR.puts e.message
ensure
  puts "DEBUG: f.close"
  f.close
end

File.open( 'README.markdown', "r" ) { |f|
  puts f.read()
}

 
 

12.2.1

 

dir_tmpdir.rb
#!/usr/bin/env ruby
#
# 一時ディレクトリを作る

require   'fileutils'
require   'tmpdir'

CLONED_DIRNAME = "/tmp/clone_test"

FileUtils.remove_entry_secure(CLONED_DIRNAME) if Dir.exist?(CLONED_DIRNAME)

begin
  Dir.mkdir(CLONED_DIRNAME, 0755)
rescue => e
  STDERR.puts "Error: could not mkdir <#{CLONED_DIRNAME}>" 
  exit 1
end

if Dir.exist?(CLONED_DIRNAME)
  puts "OK: mkdir #{CLONED_DIRNAME}" 
else
  STDERR.puts "Error: could not mkdir <#{CLONED_DIRNAME}>" 
end

now = Time.now.strftime("%Y.%m.%d-%H.%M.%S")

dir = Dir.mktmpdir( ['test.', '.'+now], CLONED_DIRNAME )
begin
  Dir.chdir(dir)
  puts Dir.getwd  #=> /tmp/clone_test/test.20180520-3966-1p9r8az.2018.05.20-23.15.10
ensure
  puts "Debug: FileUtils.remove_entry_secure(#{dir})"
  FileUtils.remove_entry_secure(dir)
end

 

dir_mkdir.rb
#!/usr/bin/env ruby
#
# mkdir 相当の実現方法

require   'fileutils'

CLONED_DIRNAME = "/tmp/clone_test"

FileUtils.remove_entry_secure(CLONED_DIRNAME) if Dir.exist?(CLONED_DIRNAME)

begin
  Dir.mkdir(CLONED_DIRNAME, 0755)
rescue => e
  STDERR.puts "Error: could not mkdir <#{CLONED_DIRNAME}>" 
  exit 1
end

if Dir.exist?(CLONED_DIRNAME)
  puts "OK: mkdir #{CLONED_DIRNAME}" 
else
  STDERR.puts "Error: could not mkdir <#{CLONED_DIRNAME}>" 
end

 
 

12.3.2

 

system_git.rb
#!/usr/bin/env ruby
#
# 下記の操作をするのみ
# ---------------------------
# 1. /tmp/ 以下に作業ディレクトリを作成する
# ---------------------------
# % mkdir /tmp/clone_test
# % test -d /tmp/clone_test
# % export now="`date "+%Y.%m.%d-%H.%M.%S`"
# % mkdir /tmp/clone_test/test.XXXX.${now}
#
# ---------------------------
# 2. git clone してくる
# ---------------------------
# % cd /tmp/clone_test/test.XXXX.${now}
# % git clone http://github.com/tswicegood/Dolt
# % cd Dolt
# % ls -A .
# % cd $CLONED_DIRNAME/Dolt
# % test -f README.markdown
#
# ---------------------------
# 3. 所定のファイルを変更する
# ---------------------------
# % test ! -f README.markdown && exit 1
# % export ver=(sed -n 's/As of v\(.*\),\(.*\)/\1/p')
# % echo $ver > README.markdown

require   'fileutils'
require   'tmpdir'

GIT_REPOSITORY = "http://github.com/tswicegood/Dolt"
CLONED_DIRNAME = "/tmp/clone_test"

FileUtils.remove_entry_secure(CLONED_DIRNAME) if Dir.exist?(CLONED_DIRNAME)

# mkdir /tmp/clone_test
begin
  Dir.mkdir(CLONED_DIRNAME, 0755)
rescue => e
  STDERR.puts "Error: could not mkdir <#{CLONED_DIRNAME}>" 
  exit 1
end

# test -d /tmp/clone_test
if Dir.exist?(CLONED_DIRNAME)
  puts "OK: mkdir #{CLONED_DIRNAME}" 
else
  STDERR.puts "Error: could not mkdir <#{CLONED_DIRNAME}>" 
end

# 日時から一意性文字列を作る
# now=`date "+%Y.%m.%d-%H.%M.%S"`
now = Time.now.strftime("%Y.%m.%d-%H.%M.%S")

# 一時ディレクトリにプレフィクスとサフィックスを付与する
# mkdir /tmp/clone_test/test.XXXX.2018.04.01-18.00.01
# cd /tmp/clone_test/test.XXXX.2018.04.01-18.00.01
dir = Dir.mktmpdir( ['test.', '.'+now], CLONED_DIRNAME )
begin
  Dir.chdir(dir)
  puts Dir.getwd 
ensure
  puts "Debug: FileUtils.remove_entry_secure(#{dir})"
  # FileUtils.remove_entry_secure(dir)  # 後始末
end

# git clone http://github.com/tswicegood/Dolt
# cd Dolt
# ls -A .
begin
  system('git', 'clone', GIT_REPOSITORY)
rescue => e
  STDERR.puts "Error: system('git', 'clone', #{GIT_REPOSITORY})"
  exit 1
end

# cd $CLONED_DIRNAME/Dolt
# test -f README.markdown
begin
  path = File.join(dir, 'Dolt')
  Dir.chdir(path)
  puts "DEBUG: #{Dir.getwd()}"
rescue => e
  STDERR.puts "Error: At #{Dir.getwd} by "
  STDERR.puts e.message
  exit 1
end

# test ! -f README.markdown && exit 1
exit 1 unless File.exist?('README.markdown')
puts "---------------------------------------"

#「As of v0.3」といった行が出現したら、次の操作をする
# 1. +1 加算して 1.3 とする
# 2.「1.3」のみを README.markdown に書き出す 
# 3. README.markdown の中身が「1.3」のみとなるようにする

version = 0

File.open( 'README.markdown', "r+" ) { |f|
  line = f.read()
  line.match(%r@^As of v(.+?),(.+?)@) { |m|
    version = m[1].to_f + 1
  }
}

begin
  f = File.open( 'README.markdown', "w" )
  f.write(version)
rescue => e
  STDERR.puts e.message
ensure
  puts "DEBUG: f.close"
  f.close
end

File.open( 'README.markdown', "r" ) { |f|
  puts f.read()
}

 
 

12.4.1

 

csv_read.rb
#!/usr/bin/env ruby

require 'csv'
require 'fileutils'
 	
File.open("meibo.csv", mode = "w") {|f|
  f.write("mike,3\n")
  f.write("tora,1\n")
  f.write("hachi,10\n")
}

CSV.foreach("meibo.csv") { |row|
    STDOUT.printf("[OUTPUT] #{row[0]},#{row[1]}\n")
}

 
 

12.4.3

 

yaml_add.rb
#!/usr/bin/env ruby
#
# YAML に新たな要素を追加する
#
# <orig.yml>
# alice:
#     name: 'Alice'
#     email: 'alice@example.com'
#     age: 20

require 'yaml'
require 'fileutils'
require 'tempfile'

def yaml_add()

  orig = YAML.load_file('orig.yml')
  #p orig
  p orig['alice']           #=> {"name"=>"Alice", "email"=>"alice@example.com", "age"=>20} 
  p orig['alice']['name']   #=> "Alice"
  p orig['alice']['email']  #=> "alice@example.com"
  p orig['alice']['age']    #=> 20

  # 新規要素を追加する
  orig['alice']['gender'] = :female

  orig
end

# 変更後の YAML オブジェクトを受け取る
changed = yaml_add()

p changed['alice']           #=> {"name"=>"Alice", "email"=>"alice@example.com", "age"=>20, "gender"=>:female}
p changed['alice']['name']   #=> "Alice"
p changed['alice']['email']  #=> "alice@example.com"
p changed['alice']['age']    #=> 20
p changed['alice']['gender'] #=> :female

 

yaml_has_key.rb
#!/usr/bin/env ruby
#
# ハッシュを指定して YAML の内容を変える。
# exmaple.com -> gmail.com
#
# このとき、キーが存在するかチェックをする。
#
# [orig.yaml] の内容は以下である
# alice:
#     name: 'Alice'
#     email: 'alice@example.com'
#     age: 20

require 'yaml'
require 'fileutils'
require 'tempfile'

yaml = YAML.load_file('orig.yml')

if yaml.has_key?('alice')
  p yaml['alice']           #=> {"name"=>"Alice", "email"=>"alice@gmail.com", "age"=>20} 
end

if yaml['alice'].has_key?('name')
  p yaml['alice']['name']   #=> "Alice"
end

if yaml['alice'].has_key?('email')
  p yaml['alice']['email']  #=> "alice@example.com" 
  yaml['alice']['email'].gsub!('example.com', 'gmail.com')
  p yaml['alice']['email']  #=> "alice@gmail.com" 
end

if yaml['alice'].has_key?('age')
  p yaml['alice']['age']    #=> 20
end

 

orig.yml
alice:
    name: 'Alice'
    email: 'alice@example.com'
    age: 20

 

yaml_print.rb
#!/usr/bin/env ruby
#
# YAML の内容を表示する
#
# <orig.yml>
# alice:
#     name: 'Alice'
#     email: 'alice@example.com'
#     age: 20

require 'yaml'
require 'fileutils'
require 'tempfile'

def yaml_print()

  orig = YAML.load_file('orig.yml')

  #p orig

  p orig['alice']           #=> {"name"=>"Alice", "email"=>"alice@example.com", "age"=>20} 
  p orig['alice']['name']   #=> "Alice"                                                    
  p orig['alice']['email']  #=> "alice@example.com"                                        
  p orig['alice']['age']    #=> 20                                                         

end

yaml_print()

 

yaml_walk.rb
#!/usr/bin/env ruby
#
# YAML の内容を表示する。
# このとき、YAML の構成が変化しても対応できるようにする
#
# <orig.yml>
# alice:
#     name: 'Alice'
#     email: 'alice@example.com'
#     age: 20
#
# 参考にしたサイト
# https://teratail.com/questions/68365

require 'yaml'
require 'fileutils'
require 'tempfile'

def yaml_walk( y, parent = nil )
    y.each { |key, val|
        label = parent.nil? ? "#{key}" : "#{parent}#{key}"
        if val.is_a?(Hash)
            yaml_walk(val, label)
        else
            puts "#{label}#{val} です"
        end
    }
end

yaml = YAML.load_file('orig.yml')

p yaml_walk(yaml)

 

yaml_dump.rb
#!/usr/bin/env ruby
#
# YAML の内容をダンプしてファイルに書き出す

require 'yaml'
require 'fileutils'
require 'tempfile'

def yaml_dump()
  orig = YAML.load_file('orig.yml')
  #p orig

  # orig.yml の内容を一時ファイルに書き出す
  tmpf = Tempfile.create("")
  buff = YAML.dump(orig, tmpf)
  tmpf.close

  # コピーしたファイルの中身を表示する (デバッグ)
  File.open(tmpf){ |f|
    line = f.read
    STDOUT.write line
  }
end

yaml_dump()

 

yaml_tr.rb
#!/usr/bin/env ruby
#
# Hash を無視して YAML 全体を書き変える
# exmaple.com -> gmail.com

require 'yaml'
require 'fileutils'
require 'tempfile'

def yaml_tr(a,b)

  orig = YAML.load_file('orig.yml')
  #p orig

  # orig.yml の内容を一時ファイルに書き出す
  tmpf = Tempfile.create("")
  buff = YAML.dump(orig, tmpf)
  tmpf.close

  # 一時ファイルの中身を置換しつつ, その都度 YAML 形式として読み込む
  buff = File.open( tmpf, "r" ){ |f|
    f.read().gsub!(a,b)
  }

  YAML.load(buff)
end

y = yaml_tr('example.com', 'gmail.com')

p y['alice']           #=> {"name"=>"Alice", "email"=>"alice@gmail.com", "age"=>20} 
p y['alice']['name']   #=> "Alice"                                                    
p y['alice']['email']  #=> "alice@gmail.com"                                        
p y['alice']['age']    #=> 20                                                         

 
 

12.4.4