Ruby関連ツール覚え書き

# pry

* irbの代替となる強力な対話型インタプリタ
* 主な機能
    * 確定した入力、評価結果の出力、入力履歴のシンタックスハイライト
    * クラスやモジュール、またはメソッドを定義しているソースコードを参照する
    * クラスやモジュール、またはメソッドのドキュメントを参照する
    * エディタを起動し、その場でメソッドの定義を書き換える
    * シェルコマンドを呼び出す
    * Gistと連携する
    * スコープの移動
    * プログラム実行時に任意の場所で実行を停止し、Pryの対話環境を起動する
    * プラグインによる拡張機能


## インストール

* pry
    * 本体
* pry-doc
    * Ruby本体のC言語による実装を参照する機能をPryに追加する
* プラグイン
    * pry-stack_explorer
        * Pryを起動した時点でのそれまでの呼び出しスタックを表示する
        * スタックが示すコンテキストを選択して移動できる
    * pry-byebug
        * コードをステップ実行する
        * Ruby2系で利用
            * Ruby1.9まではpry-debuggerを使う


``` shell:pryインストール
### Pryインストール
$ gem install pry pry-doc

### プラグインのインストール
$ gem install pry-stack_explorer pry-byebug
```

## pryを使ってみる

``` ruby:pryを使う
[1] pry(main)> 1+2
=> 3
[2] pry(main)> "ruby".upcase
=> "RUBY"

### fooというStringオブジェクトの作成
[1] pry(main)> foo = "foo"
=> "foo"

### オブジェクトのコンテキストスコープに移動
[2] pry(main)> cd foo

### 現在のスコープに定義されているメソッドや定数、変数を出力する
[3] pry("foo"):1> ls
Comparable#methods: <  <=  >  >=  between?
String#methods:
  %            chr             gsub       rindex       succ!
  *            clear           gsub!      rjust        sum
...
  chop         freeze          reverse    sub!         valid_encoding?
  chop!        getbyte         reverse!   succ
self.methods: __pry__
locals: _  __  _dir_  _ex_  _file_  _in_  _out_  _pry_

### "foo"というインスタンスに対してupcaseを実行している
[6] pry("foo"):1> upcase
=> "FOO"
```

## ヘルプを確認する

* help
    * 引数なしで一覧が表示される
    * 引数をつけるとそのコマンドの詳細が見える

## プログラム実行時に任意の場所で対話環境へ移行する

``` ruby:任意の場所で対話型環境へ移行
$ cat pry_test.rb
==============================
require "pry"

class Foo
  def initialize
    @bar = "bar"
  end

  def bar
    binding.pry  ### ここで対話型環境に移行
    @bar
  end
end

puts "bar is " + Foo.new.bar
==============================

$ ruby pry_test.rb
==============================
From: /Users/ftakao2007/work/ruby/tmp/pry_test.rb @ line 10 Foo#bar:

     9: def bar
 => 10:   binding.pry
    11:   @bar
    12: end

### 定義されているものの確認
[1] pry(#<Foo>)> ls
Foo#methods: bar  ### barメソッドが定義されている
instance variables: @bar   ### インスタンス変数 @bar が定義されている
locals: _  __  _dir_  _ex_  _file_  _in_  _out_  _pry_

### @barを評価。コンクラスタで代入された"bar"が入っている
[2] pry(#<Foo>)> @bar
=> "bar"

### 新しく文字列を代入
[3] pry(#<Foo>)> @bar = "new strign"
=> "new strign"

### barの戻り値が変化したため最終的な出力結果が変わる
[4] pry(#<Foo>)> exit
bar is new strign
==============================
```

## 主要なコマンド

* show-source
    * メソッドやクラス、モジュールの定義をPry上で直接確認できる
* show-doc
    * 引数で渡したメソッドやクラス、モジュールのドキュメントをPry上で確認できる
        * ソースコードにRDocやYARD形式で記述されたコメント
* edit
    * Pryからエディアタを起動する
        * エディタで保存した記述をPry上で評価できる
        * デフォルトだとnanoが起動する
        * nano以外を使う方法
            * Pry.config.editor = "(edita)" のように設定する
            * 環境変数EDITORに起動したいエディタを設定する
            *  ubuntuの場合は
* その他コマンドについて
    * helpかgithub上のWikiを見る

``` ruby:pryのコマンド
### 組み込みライブラリに定義されているSetクラスの確認
[1] pry(main)> require "set"
=> true

### Set#addの実装を確認
[2] pry(main)> show-source Set#add

From: /home/vagrant/.rbenv/versions/2.2.2/lib/ruby/2.2.0/set.rb @ line 289:
Owner: Set
Visibility: public
Number of lines: 4

def add(o)
  @hash[o] = true
  self
end

### Setクラスの実装を確認
[3] pry(main)> show-source Set
From: /home/vagrant/.rbenv/versions/2.2.2/lib/ruby/2.2.0/set.rb @ line 67:
Class name: Set
Number of lines: 482

class Set
  include Enumerable

### edit
[1] pry(main)> exit
==============================
module Foo
  def self.foo
    puts "foo"
  end
end

Foo.foo
==============================
foo  ### 保存した時に実行される
=> nil
```

## .pryrcファイル

* pryの設定ファイル。pry起動時に読み込まれる
    * 設定例
        * 利用するエディタをviにする
            * Pry.config.editor = "vi"

## プラグインの利用

### pry-stack_explorer

``` ruby:pry-stack_explorer
$ cat multi_stack_sample.rb
==============================
require "pry"

module MultiStackSample
  def self.stackA
    stackB
  end

  def self.stackB
    binding.pry
    "stackB"
  end
end

MultiStackSample.stackA
==============================

$ ruby multi_stack_sample.rb
==============================
Frame number: 0/3  ### Frame numberという情報が追加されている

From: /home/vagrant/work/ruby/tmp/multi_stack_sample.rb @ line 10 MultiStackSample.stackB:

     8: def self.stackB
     9:   binding.pry
 => 10:   "stackB"
    11: end

[1] pry(MultiStackSample)> help
...
Pry-stack_explorer (v0.4.9.2) ### Pry-stack_explorerの情報が追加されている
  down               Go down to the callee's context.
  frame              Switch to a particular frame.
  show-stack         Show all frames
  up                 Go up to the caller's context.

### upとdownでスタックを移動できる
[1] pry(MultiStackSample)> up

Frame number: 1/3
Frame type: method

From: /Users/ftakao2007/work/ruby/tmp/multi_stack_sample.rb @ line 10 MultiStackSample.stackB:

     8: def self.stackB
     9:   binding.pry
 => 10:   "stackB"
    11: end

[2] pry(MultiStackSample)> up

Frame number: 2/3
Frame type: method

From: /Users/ftakao2007/work/ruby/tmp/multi_stack_sample.rb @ line 5 MultiStackSample.stackA:

    4: def self.stackA
 => 5:   stackB
    6: end

[3] pry(MultiStackSample)> up

Frame number: 3/3
Frame type: main

From: /Users/ftakao2007/work/ruby/tmp/multi_stack_sample.rb @ line 14 :

     9:     binding.pry
    10:     "stackB"
    11:   end
    12: end
    13:
 => 14: MultiStackSample.stackA

### frame番号を指定して移動
[4] pry(main)> frame 2

Frame number: 2/3
Frame type: method

From: /Users/ftakao2007/work/ruby/tmp/multi_stack_sample.rb @ line 5 MultiStackSample.stackA:

    4: def self.stackA
 => 5:   stackB
    6: end
==============================
```

### pry-byebug

``` ruby:pry-byebug
$ cat byebug_sample.rb
==============================
require "pry"

module DebuggerSample
  def self.conditional_print(obj, condition)
    if condition
      p obj
    end
  end
end

binding.pry
DebuggerSample.conditional_print([1,2,3], true)
==============================

$ ruby byebug_sample.rb
==============================
Frame number: 0/1

From: /Users/ftakao2007/work/ruby/tmp/byebug_sample.rb @ line 12 :

     7:     end
     8:   end
     9: end
    10:
    11: binding.pry
 => 12: DebuggerSample.conditional_print([1,2,3], true)

### Breakpointの設定
[1] pry(main)> break DebuggerSample.conditional_print

  Breakpoint 1: DebuggerSample.conditional_print (Enabled)

  4: def self.conditional_print(obj, condition)
5:   if condition
6:     p obj
7:   end
8: end

### Breakポイントで停止した、ふたたびpry起動
[2] pry(main)> continue

  Breakpoint 1. First hit

From: /Users/ftakao2007/work/ruby/tmp/byebug_sample.rb @ line 4 DebuggerSample.conditional_print:

 => 4: def self.conditional_print(obj, condition)
    5:   if condition
    6:     p obj
    7:   end
    8: end

### step実行
[2] pry(DebuggerSample):1> step

From: /Users/ftakao2007/work/ruby/tmp/byebug_sample.rb @ line 5 DebuggerSample.conditional_print:

    4: def self.conditional_print(obj, condition)
 => 5:   if condition
    6:     p obj
    7:   end
    8: end

[2] pry(DebuggerSample)> step

From: /Users/ftakao2007/work/ruby/tmp/byebug_sample.rb @ line 6 DebuggerSample.conditional_print:

    4: def self.conditional_print(obj, condition)
    5:   if condition
 => 6:     p obj
    7:   end
    8: end

[2] pry(DebuggerSample)> step
[1, 2, 3]
==============================
```

## Jupyter連携

インストール

* [Python周りのライブラリのインストール](http://qiita.com/ftakao2007/items/e7f951de98509929bdfb#python%E5%91%A8%E3%82%8A%E3%81%AE%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)
* [pyenv, pyenv-virtualenv環境構築](http://qiita.com/ftakao2007/items/d30c0fdd8b9561bc0f78)

``` shell
### 必要なパッケージのインストール (CentOs7)
$ sudo yum install libtool

### Ubuntu
$ sudo apt-get install libtool-bin

### gemパッケージのインストール
$ bundle init
$ vi Gemfile
================================================
# frozen_string_literal: true
source "https://rubygems.org"

gem "rbczmq"
gem "pry"
gem "iruby"
================================================
$ bundle install --path vendor/bundle

### irubyを起動
$ bundle exec iruby notebook --ip=(サーバのIPアドレス)
```

# Rake

* Rubyで記述されたビルドツール
    * Make、Ant、Mavenなどと同種のツール
    * Rubyで一連の処理を記述して実行できる
        * ビルドツール向けの言語内DSLとして定義されたメソッド群を活用
        * ビルドツールとしての記述のしやすさとRubyの表現力を両立
    * Ruby以外のプロジェクトにも利用可能

## Rakefile

* RakeはRakefileと呼ばれるファイルに一連の処理をまとめたタスクを定義する
* taskメソッド
    * 処理を定義する
        * タスク定義を一つの単位として処理を実行する
    * 引数としてシンボルを渡すとタスク名として扱われる
    * Hashを引数として渡す事ができる
        * キー : タスク名
        * 値 : そのタスクが依存しているタスク名
            * 値に配列を渡すこともできる
                * 依存しているタスクを複数記載できる
    * defaultタスク
        * 引数無くrakeが実行された時に実行されるタスク
* descメソッド
    * タスクに説明文をつける
        * rake -Tオプションで説明文を表示できる
* fileメソッド
    * ファイルタスクを定義する
        * 引数はファイル名を示す文字列を渡す
            * ファイルが存在する場合はそのファイルタスク行はスキップされる
        * 用途
            * タスクBがタスクAの成果物に依存しており、すでに成果物がある場合はタスクAをスキップする
        * メリット
            * ビルドに数分かかる処理は不要なファイルを作らずに済むので処理時間を短縮できる
* クリーンタスク
    * ビルドの際に生成された一時ファイrを削除する
        * require 'rake/clean'でライブラリを読み込む必要がある
        * 以下タスクが追加される
            * clean : 一時ファイルの削除
            * clobber : 一時ファイルと作成された実行ファイルの削除
        * CLEAN定理が定義される
            * cleanタスクはCLEAN定義に格納されたリストを削除対象とする
            * ファイルの追加方法
                * CLEAN.include(ファイル名)
            * clobberは
                


``` ruby:簡単なRakefile
### 簡単なRakefile
$ vi Rakefile
==========================
desc 'Task Description'  ### rake -Tで表示されるタスクの説明文を表示するメソッド
task :task_name do
 puts 'Rake task'
end
==========================

### 定義されているタスクの一覧を表示
$ rake -T
==========================
rake task_name  # Task Description
==========================

### 引数にタスク名を渡してタスクを実行する
$ rake task_name
==========================
Rake task
==========================
```

### もう少しビルドツールらしいRakefile

``` ruby:Rakefile2
### 必要なファイルやディレクトリを作成
$ mkdir meals
$ touch meals/breakfast.txt
$ touch meals/lunch.txt
$ touch meals/dinner.txt
$ mkdir reports

### Rakefileの作成
$ vi Rakefile2
==========================
# -*- coding: utf-8 -*-

TXT_DIR = "#{Dir.pwd}/meals/"
OUTPUT_DIR = "#{Dir.pwd}/reports/"

FILE_EXT = '.txt'
MEALS = [
  {name: 'breakfast', lavel: '朝食'},
  {name: 'lunch', lavel: '昼食'},
  {name: 'dinner', lavel: '夕食'}
]

task :default => :daily_report   ### デフォルトタスクの定義

desc '一日の食事記録を作成する'
task :daily_report => [:check, :report] do  ### タスクの定義。check,reportタスクに依存している
  puts '一日の食事記録を作成しました'
end

desc '食事記録ファイルの確認'
task :check do
  MEALS.each do |meal|
    file_name = meal[:name] + FILE_EXT
    full_path = TXT_DIR + file_name

    if File.exist?(full_path)
      puts "#{file_name} OK"
    else
      puts "#{file_name} がありません"
    end
  end
end

desc '食事レポート作成'
task :report do
  date_str = Time.now.strftime('%Y/%m/%d')
  output_str = "#{date_str}の食事記録\n\n"

  MEALS.each do |meal|
    file_name = meal[:name] + FILE_EXT
    full_path = TXT_DIR + file_name
    description = <<DESC
#{meal[:label]}
#{File.read(full_path, :encoding => Encoding::UTF_8)}
DESC

    output_str += description
  end

  output_file = OUTPUT_DIR + "report_#{Time.now.strftime('%Y%m%d')}.txt"
  File.open(output_file, 'w') {|f|
    f.write output_str
  }
end
==========================

### タスク一覧。-fはRakefileを指定している。
$ rake -f Rakefile2 -T
==========================
rake check         # 食事記録ファイルの確認
rake daily_report  # 一日の食事記録を作成する
rake report        # 食事レポート作成
==========================

### タスクの実行。 -tをつけると実行時のトレース情報を出力する
### 依存しているタスクcheckとreportが実行されているのが分かる
$ rake daily_report -t
==========================
** Invoke daily_report (first_time)
** Invoke check (first_time)
** Execute check
breakfast.txt OK
lunch.txt OK
dinner.txt OK
** Invoke report (first_time)
** Execute report
** Execute daily_report
一日の食事記録を作成しました
==========================

### 引数無しで実行するとdefaultタスクが実行される
$ rake -f Rakefile2
==========================
breakfast.txt OK
lunch.txt OK
dinner.txt OK
一日の食事記録を作成しました
==========================
```

## ファイルタスク

``` ruby:ファイルタスク
### ファイルタスクを利用する場合。事前のファイルやディレクトリの作成が不要
$ vi Rakefile3
==========================
# -*- coding: utf-8 -*-

TXT_DIR = "#{Dir.pwd}/meals/"
OUTPUT_DIR = "#{Dir.pwd}/reports/"
OUTPUT_FILE = OUTPUT_DIR + "report_#{Time.now.strftime('%Y%m%d')}.txt" ### 出力するファイルの設定

FILE_EXT = '.txt'
MEALS = [
  {name: 'breakfast', lavel: '朝食'},
  {name: 'lunch', lavel: '昼食'},
  {name: 'dinner', lavel: '夕食'}
]
TXT_FILES = MEALS.map {|m| TXT_DIR + m[:name] + FILE_EXT} ### 読み込むファイルの設定

task :default => :daily_report

desc '一日の食事記録を作成する'
task :daily_report => [:check, :report] do
  puts '一日の食事記録を作成しました'
end

desc '食事記録ファイルの確認'
task :check => TXT_FILES do
end

### ファイルが無ければ作成する
TXT_FILES.each do |file_name|
  file file_name do
    puts "#{file_name} OK"
    puts "#{file_name} がありません"
    sh "touch #{file_name}"
  end
end

desc '食事レポート作成'
task :report do
  date_str = Time.now.strftime('%Y/%m/%d')
  output_str = "#{date_str}の食事記録\n\n"

  MEALS.each do |meal|
    file_name = meal[:name] + FILE_EXT
    full_path = TXT_DIR + file_name
    description = <<DESC
#{meal[:label]}
#{File.read(full_path, :encoding => Encoding::UTF_8)}
DESC

    output_str += description
  end

  output_file = OUTPUT_DIR + "report_#{Time.now.strftime('%Y%m%d')}.txt"
  File.open(output_file, 'w') {|f|
    f.write output_str
  }
end
==========================

### ファイルがあるとき
$ rake -f Rakefile3 -t
==========================
** Invoke default (first_time)
** Invoke daily_report (first_time)
** Invoke check (first_time)
** Invoke /Users/ftakao2007/work/ruby/tmp/meals/breakfast.txt (first_time, not_needed)
** Invoke /Users/ftakao2007/work/ruby/tmp/meals/lunch.txt (first_time, not_needed)
** Invoke /Users/ftakao2007/work/ruby/tmp/meals/dinner.txt (first_time, not_needed)
** Execute check
** Invoke report (first_time)
** Execute report
** Execute daily_report
一日の食事記録を作成しました
** Execute default
==========================

### ファイルが無い時
$ rake -f Rakefile3 -t
==========================
** Invoke default (first_time)
** Invoke daily_report (first_time)
** Invoke check (first_time)
** Invoke /Users/ftakao2007/work/ruby/tmp/meals/breakfast.txt (first_time)
** Execute /Users/ftakao2007/work/ruby/tmp/meals/breakfast.txt
/Users/ftakao2007/work/ruby/tmp/meals/breakfast.txt OK
/Users/ftakao2007/work/ruby/tmp/meals/breakfast.txt がありません
touch /Users/ftakao2007/work/ruby/tmp/meals/breakfast.txt
** Invoke /Users/ftakao2007/work/ruby/tmp/meals/lunch.txt (first_time)
** Execute /Users/ftakao2007/work/ruby/tmp/meals/lunch.txt
/Users/ftakao2007/work/ruby/tmp/meals/lunch.txt OK
/Users/ftakao2007/work/ruby/tmp/meals/lunch.txt がありません
touch /Users/ftakao2007/work/ruby/tmp/meals/lunch.txt
** Invoke /Users/ftakao2007/work/ruby/tmp/meals/dinner.txt (first_time)
** Execute /Users/ftakao2007/work/ruby/tmp/meals/dinner.txt
/Users/ftakao2007/work/ruby/tmp/meals/dinner.txt OK
/Users/ftakao2007/work/ruby/tmp/meals/dinner.txt がありません
touch /Users/ftakao2007/work/ruby/tmp/meals/dinner.txt
** Execute check
** Invoke report (first_time)
** Execute report
** Execute daily_report
一日の食事記録を作成しました
** Execute default
==========================
```

## クリーンタスク

``` ruby:クリーンタスク
### クリーンタスクの追加
$ vim Rakefile3
==========================
require 'rake/clean'

CLEAN.include('mealss/*')
CLOBBER.INCLUDE('reports/*')
==========================

### 一時ファイルのファイルを削除
$ rake -f Rakefile3 clean
$ ls meals
==========================
(何もなし)
==========================

### 一時ファイルと成果物を削除
$ rake -f Rakefile3 clobber
$ ls reports
==========================
(何もなし)
==========================
```

## ディレクトリタスク

* ビルド処理の過程で生成物を特定のディレクトリに格納したい場合などに使用する
    * 対象ディレクトリが存在しないときは自動的に作成する
    * 既に存在する場合はスキップする
    * ブロックを渡す事はできない
        * 作成したディレクトリを利用するタスクはtaskメソッドを用いて記述する

``` ruby:ディレクトリタスク
$ vi Rakefile4
==========================
BACKUP_DIR = "backups"

directory BACKUP_DIR
desc "レポートをバックアップする"
task :backup => BACKUP_DIR do
  sh "cp reports/* backups/"
end
==========================

$ rake -f Rakefile4 backup -t
==========================
** Invoke backup (first_time)
** Invoke backups (first_time, not_needed)
** Execute backup
cp reports/* backups/
==========================
```

## ルール

* 特定のパターン名を持ったファイルに依存するタスクを動的に定義する仕組み
* タスクの実行途中で必要になるファイルについて生成方法を定義する
* ruleメソッド
    * rake -Tで一覧には表示されない
    * ブロックでFileTaskオブジェクトを受け取る事ができる
    * FileTaskオブジェクトのメソッド
        * name : タスクの親となるファイル名を返す
        * source : ルールの依存先になるファイルを返す
        * sources : ルールの依存先になるファイルの配列を返す
        * prerequisites : タスクの依存先になるタスク名の配列を返す
* 使用例
    * .oという拡張子を持つコンパイル後のオブジェクトファイルが必要なタスク
        * 自動的に.cという拡張子をソースファイルとしてコンパイルするという用途で利用
* メリット
    * 直接ファイル名を記述せずに済む


``` ruby:ルールの例
### t.sourceが.cファイル、t.nameが.oファイル
rule '.o' => ['.c'] do |t|
  sh "cc #{t.source} -c -o #{t.name}"
end
```

## グループ化

* 複数のタスクをグループ化する
    * namespaceメソッド

``` ruby:グループ化
$ vi Rakefile5
==========================
# coding: utf-8
namespace :check do
  desc 'ログファイルのチェック'
  task :log do
  end

  desc 'yamlファイルのチェック'
  task :yml do
  end
end
==========================

### check:hoge の形式でタスクが表示される
$ rake -f Rakefile5 -T
==========================
rake check:log  # ログファイルのチェック
rake check:yml  # yamlファイルのチェック
==========================

### namespace自体を入れ子にする
$ vi Rakefile6
==========================
# coding: utf-8
namespace :make do
   namespace :report do
    desc '日時レポートの作成'
    task :daily_report do
    end

    desc '週次レポートの作成'
    task :weekly_report do
    end
  end

  namespace :history do
    desc 'メニュー履歴作成'
    task :menu do
    end

   desc '接種カロリー履歴作成'
   task :calorie do
   end
  end
end
==========================

### make:hoge:fuga の形式でタスクが表示される
$ rake -f Rakefile6 -T
==========================
rake make:history:calorie       # 接種カロリー履歴作成
rake make:history:menu          # メニュー履歴作成
rake make:report:daily_report   # 日時レポートの作成
rake make:report:weekly_report  # 週次レポートの作成
==========================
```

## Rakeのバージョンについて

* 表記方法
    * 9番目
        * v0.9.0
    * 10番目
        * v10.0.0
            * 一つ目の数字をカウントアップする形式になった
* v10.0.0での変更点
    * いくつかのdeprecatedな機能が削除されている
        * deprecatedな機能を利用していないならば大きな問題は無い

# RubyGems

* RubyGemsとは
    * Rubyにおけるパッケージ管理システム
    * Ruby1.9から標準ライブラリに含まれるようになった
    * 用途
        * ライブラリのインストール、アップデート、アンインストール
        * パッケージ同士の依存関係の解決
        * 環境内に含まれるライブラリの検索
    * gem
        * RubyGemsの略称
        * コンテキストやさす対象によって意味が異なる
            * gemパッケージ : RubyGemsでインストール可能なライブラリやコマンドをまとめたもの
            * gemコマンド : gemパッケージを操作するためのコマンド

## gemコマンド

* コマンド体系
    * gemの後に続けてサブコマンドを指定する
    * コマンド
        * gem help : ヘルプを表示する
        * gem (gemコマンド) example : (gemコマンド)の利用例が表示される
* インストールと確認
    * コマンド
        * gem install (gemパッケージ) : 依存するパッケージも含めてインストールする
            * -v : バージョンを指定する
                * gem install machanize -v '< 2.2'    # 2.2より一つ古い
                * gem install machanize -v '>= 2.2'   # 2.2かそれより新しい
                * gem install machanize -v '~> 0.8'   # 0.X系で一番新しい
                * gem install machanize -v '~> 0.8.0' # 0.8系で一番新しい
        * gem list : システムにインストールされているgemパッケージのリストを表示する
            * --remote : サーバ側のリストを取得する
            * --pre : RC(Release Candidate:リリース候補)版のインストール
        * gem search : パッケージを探す
            * 例
                * gem search mechanize --remote : meahanizeと名のつくパッケージを検索する
        * gem query : 正規表現で検索する
            * 例
                * gem query -r --name-matches='^active*'
        * gem update : パッケージをアップデートする
            * 引数にgemパッケージを指定するとそのgemパッケージと依存するgemパッケージが最新版にアップデートされる
            * gem update --system : RubyGems自身をアップデートする
        * gem uninstall : gemパッケージをアンインストールする
            * 依存するパッケージのアンインストールは行われない
            * -v バージョンを指定
            * -a 全バージョン
            * 例
                * gem uninstall mechanize -v '2.2.1'
                * gem uninstall mechanize -a
        * gem cleanup : 最新のバージョンを残して古いバージョンのgemパッケージを削除する
            * -d (--dryrun) : ドライラン
        * gem prinstine : gemパッケージをインストール時の状態に戻す
            * gemパッケージを直接編集して動作を変更した場合など
            * 例
                * gem pristine pry ### pryを初期状態に戻す
                * gem pristine pry -v 0.9.8.4  ### バージョンを指定
                * gem pristine --all  ### 全てのパッケージを初期状態に戻す
        * gem server : RDocを参照するためのローカルサーバをたてる
            * コマンドを実行後、表示されるアドレスにアクセスするとドキュメントを参照できる
            * --no-rdoc --no-ri
                * ドキュメント生成を省略する
                * 本番環境等ドキュメントが必要ない
                * YARDというドキュメントツールが主流になってきている
        * gem environment : RubyGemsの固有情報の確認
            * バージョンやconfigなど
* .gemrc
    * 毎回決まって付与するオプションを指定する
        * 例
        * gem: --no-rdoc --no-ri # インストール時にrdoc,riを生成しない
        * :verbose: true # 詳細メッセージの表示

# Bundler

* アプリケーションで利用するgemパッケージを定義し、依存関係を解決するための仕組み
    * アプリケーション米にgemパッケージの依存関係を閉じ込める
    * 特定のアプリケーションのみが利用するgemパッケージを簡単に管理できる


## Bundlerの特徴

### Gemfile

* Gemfile
    * アプリケーションで利用するgemパッケージが列挙されている
* 利用の流れ
    * インストール
        * gem install bundler
    * bundler initでGemfile作成
    * 使用するgemパッケージを列挙する
    * bundler installを実施
* Gemfile.lock
    * bundler install後に作成される
    * gemパッケージの依存関係を解決した結果が書き出される
    * バージョンまで細かく書かれている
        * バージョンを固定できる
            * 複数人で開発しても同じバージョンのgemパッケージで開発できる
* 明示的に依存関係を再度更新してインストール
    * bundler update
* gemパッケージをインストールするディレクトリを指定
    * bundle install --path vendor/bundle
        * アプリケーションで使用するgemパッケージをアプリケーション内に閉じ込められる
        * 一度pathオプションを指定すると、移行はそのpathの場所にインストールを行う
    * 注意
        * この状態でgem listを実行してもインストールしたgemは表示されない
        * 表示する方法
            * bundle exec gem list
            * bundle execはbundle installでインストールしたディレクトリに対してパスを解決してコマンドを実行する
            * bundle list見ることが多い
        * 普通にrequireして使用することもできない
        * requireする方法
            * Rubyプログラム中でBundle.requireを実行する
            * Bundle.requireはライブラリの読み込みパスを解決する
            * Gemfileに記述されているgemパッケージを自動でrequireする


``` ruby:bundler
### bundler init実行直後
==========================
# A sample Gemfile
source "https://rubygems.org"   ### gemパッケージの参照先

# gem "rails"
==========================

### tapp 1.5.0を使用
==========================
# A sample Gemfile
source "https://rubygems.org"

gem "tapp", '1.5.0'
==========================

### bundle install実行後に作成されるGemfile.lock
==========================
GEM
  remote: https://rubygems.org/
  specs:
    tapp (1.5.0)
      thor
    thor (0.19.1)

PLATFORMS
  ruby

DEPENDENCIES
  tapp

BUNDLED WITH
   1.10.6
==========================

### gem listの実行
$ gem list
==========================
*** LOCAL GEMS ***

bigdecimal (1.2.6)
binding_of_caller (0.7.2)
bundler (1.10.6)
...
tapp (1.5.0)   ### tapがインストールされている
test-unit (3.0.8)
yard (0.8.7.6)
==========================

### --pathを指定したgemをプログラム中で使う
==========================
require "bundler"

Bundler.require
self.tapp
==========================
```

## Bundlerでの様々な確認方法

* bundle list
    * bundlerで管理されているgemパッケージを依存関係も含めて一覧する
        * bundler showのエイリアスになっている
    * 引数にgemパッケージ名を指定するとインストールされている場所が表示される
* bundle check
    * Gemfileに含まれているgemパッケージと依存するgemパッケージがインストールされているかチェックする
    * 一つでもパッケージが足りないとエラーになる
    * Gemfileを参照する順番
        * まずカレントディレクトリ、なければ上の階層を見る
        * --gemfileオプションで離れた階層のGemfileも指定できる
    * --dry-run
        * Gemfile.lockを更新せずに実行できる
* bundle outdated
    * インストールされているgemパッケージとrubygems.orgで公開されている最新バージョンとを比較する
    * 中身の比較は行わない
        * changelogを参照するかyard diffで増減を比較する
    * --pre
        * プレリリース版も含めて比較する
* bundle open
    * 引数に指定したgemパッケージがインストールされているディレクトリをエディタで開く
* bundle console
    * Bundlerで管理しているgemパッケージを予め読み込んだ上でIRBを起動する
        * 引数でグループが指定可能


``` ruby:Bundlerでの確認方法
### bundle list
$ bundle list
==========================
Gems included by the bundle:
  * bundler (1.8.3)
  * tapp (1.5.0)
  * thor (0.19.1)
==========================

### gemパッケージがインストールされている場所を表示
$ bundle list tapp
==========================
/Users/ftakao2007/work/ruby/tmp/vendor/bundle/ruby/2.2.0/gems/tapp-1.5.0
==========================

### 依存するgemパッケージがインストールされている事を確認
$ bundle check
==========================
The Gemfile's dependencies are satisfied
==========================

### チェックするGemfileのパスを指定
$ bundle check --gemfile ./Gemfile

### ローカルとrubygems.orgのパッケージのバージョンを比較
$ bundle outdated

### プレリリース版も含めて比較
$ bundle outdated --pre

### パッケージのディレクトリをエディタで開く
$ bundle open tapp
==========================
" ============================================================================
" Netrw Directory Listing                                        (netrw v140)
"   /Users/ftakao2007/work/ruby/tmp/vendor/bundle/ruby/2.2.0/gems/tapp-1.5.0
"   Sorted by      name
"   Sort sequence: [\/]$,\<core\%(\.\d\+\)\=\>,\.h$,\.c$,\.cpp$,*,\.o$,\.obj$,\.info$,\.swp$,\.bak$,\~$
"   Quick Help: <F1>:help  -:go up dir  D:delete  R:rename  s:sort-by  x:exec
" ============================================================================
../
bin/
...
==========================

### Bundlerで管理しているgemパッケージを読み込んだ上でirbを起動
$ bundle console
==========================
irb(main):001:0>
==========================
```

## 一歩踏み込んだBundlerの使い方

* gemパッケージのグループ化
    * 開発用途のみで利用するgemパッケージをグループ化するなどで使える
* withoutオプション
    * 特定のグループのgemパッケージを除いてbundle installする
    * .bundle/configに記述してもよい
        * BUNDLE_WITHOUT: development:test
            * コロン区切りで複数のグループを指定できる
* binstubsオプション
    * bundle exec無しにコマンドを実行できる
    * Gemfileが存在するディレクトリと同じ階層にbinディレクトリを作成
        * gemパッケージに付随するコマンドをbinディレクトリに配置する
    * 別の解決方法としてコマンドエイリアスでalias be='bundle exec'とする事がよくある

``` shell:一歩踏み込んだBundlerの使いかた
### gemパッケージのグループ化の例
$ cat Gemfile
==========================
source "https://rubygems.org"

gem 'sinatra'

group :development do
  gem 'awesome_print'
end

group :test do
  gem 'rspec'
end
==========================

### 環境を複数指定
==========================
group :development, :test do
  gem 'pry'
end
==========================

### developmentとtestグループを除いてインストール
$ bundle install --without development test
```

* その他gemファイルの使い方
    * 取得先でgitリポジトリを指定可能
    * ローカルファイルを取得元に設定できる
    * Bundler.require
        * Gemfileに記述したgemパッケージをまとめてrequireする
        * :require => falseとすると対象外になる
        * :require => '(requireさせたいライブラリ)'で指定したもののみrequireできる
        * :platform => :(プラットフォーム名)で特定のRubyの処理系のみインストール指定できる


``` shell:gemファイルの使い方
### 取得先にgitリポジトリを指定
==========================
gem 'pry', :git => 'https://github.com/pry/pry.git'
==========================

### githubの場合はURLを省略できる(アカウント名/リポジトリ名)
==========================
gem 'pry', :github => 'pry/pry'
==========================

### ブランチ、タグ、コミットハッシュ指定
==========================
# ブランチ
gem 'rails', :git => 'https://github.com/rails/rails.git', :branch => '3-2-stable'

# タグ
gem 'rails', :git => 'https://github.com/rails/rails.git', :tag => 'v3.2.9.rc3'

# コミットハッシュ
gem 'rails', :git => 'https://github.com/rails/rails.git', :ref => '2d4068dbeff7313ab332538b3934cfbf10445104'
==========================

### ローカルファイルを指定
==========================
gem 'yard', :path => '/home/vagrant/gem/yard'
==========================

### Bundler.requireの制御
==========================
# requireしない
gem 'tapp', :require => false

# 特定のライブラリのみ
gem 'activesupport', :require => 'acrive_support/core_ext/numeric'

# 特定のRuby処理系のときのみインストール
gem 'nokogiri', :platform => :jruby
==========================
```

## bundle package

* ローカルにキャッシュしてあるgemパッケージからインストールする
* bundle package
    * gemパッケージをキャッシュに保存するコマンド
        * インターネットに接続する事ができない場合でもインストールできる
    * vendor/cacheにキャッシュされる
* bundle install --local
    * vendor/cacheにあるものを利用してインストール

``` shell:bundle_package
### キャッシュする
$ bundle package
==========================
Using thor 0.19.1
Using tapp 1.5.0
Using bundler 1.8.3
Bundle complete! 1 Gemfile dependency, 3 gems now installed.
Bundled gems are installed into ./vendor/bundle.
Updating files in vendor/cache
  * thor-0.19.1.gem
  * tapp-1.5.0.gem
==========================

### キャッシュされた
$ ls vendor/cache/
==========================
tapp-1.5.0.gem	thor-0.19.1.gem
==========================

### いったんインストール済みパッケージを削除
$ rm -r vendor/cache/

### キャッシュからインストール
$ bundle install --local
==========================
Using thor 0.19.1
Using tapp 1.5.0
Using bundler 1.8.3
Bundle complete! 1 Gemfile dependency, 3 gems now installed.
Bundled gems are installed into ./vendor/bundle.
==========================

### インストールされた
$ bundle check
==========================
The Gemfile's dependencies are satisfied
==========================
```

## bundle installエラー対応

### mysql2

``` shell
checking for mysql_query() in -lmysqlclient... no
-----
mysql client is missing. You may need to 'apt-get install libmysqlclient-dev' or 'yum install mysql-devel', and try again.
-----
*** extconf.rb failed ***
```

* `$ sudo apt-get install libmysqlclient-dev`

### rmagic

``` shell
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

    current directory: /var/share/jorurigw_oss/vendor/bundle/ruby/2.4.0/gems/rmagick-2.15.4/ext/RMagick
/usr/local/rbenv/versions/2.4.1/bin/ruby -r ./siteconf20170520-26193-wm99vc.rb extconf.rb
checking for gcc... yes
checking for Magick-config... no
```
* ` sudo apt-get install libmagickwand-dev imagemagick`


* stdint.hのところでエラー
    * rmagickのバージョンを変えてみる
    * 2.13.2 -> 2.16.0で出なくなった

``` shell
==========================
/usr/local/rbenv/versions/2.1.5/bin/ruby extconf.rb
--with-opt-dir=/var/share/pref_t-kushima_jp-joruri-gw/vendor/bundle/ruby/2.1.0/gems/rmagick-2.13.2
checking for Ruby version >= 1.8.5... yes
checking for gcc... yes
checking for Magick-config... yes
checking for ImageMagick version >= 6.4.9... yes
checking for HDRI disabled version of ImageMagick... yes
checking for stdint.h... *** extconf.rb failed ***
==========================
```

### ruby-ldap

* ubuntu
    * `$ sudo apt-get install libldap-dev`

``` shell
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

    /usr/local/rbenv/versions/2.1.5/bin/ruby extconf.rb
--with-openldap2
checking for ldap.h... no
checking for lber.h... no
checking for ldap_ssl.h... no
*** extconf.rb failed ***
```

* ubuntu
    * `$ sudo apt-get install libsasl2-dev`

``` shell
saslconn.c:19:23: fatal error: sasl/sasl.h: そのようなファイルやディレクトリはありません
compilation terminated.
Makefile:237: ターゲット 'saslconn.o' のレシピで失敗しました
make: *** [saslconn.o] エラー 1

make failed, exit code 2
```

### bundle exec rails s エラー対応

* mysql2のバージョンを上げた
    * 0.3.16 -> 0.3.18以上
    * ` gem 'mysql2', '~> 0.3.18'`

``` shell
/usr/local/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/bundler-1.14.6/lib/bundler/runtime.rb:94:in `rescue in block (2 levels) in require': There was an error while trying to load the gem 'mysql2'. (Bundler::GemRequireError)
Gem Load Error is: uninitialized constant Mysql2::Client::SECURE_CONNECTION
Backtrace for gem load error is:
...
```

# gemパッケージ作成

* 様々な雛形の作成の仕方がある
    * ここではBundlerを使って雛形を作る

## 雛形を作成

``` shell
### 雛形を作る
$ bundle gem gem_sample_pruby
==========================
Creating gem 'gem_sample_pruby'...
      create  gem_sample_pruby/Gemfile
      create  gem_sample_pruby/.gitignore
      create  gem_sample_pruby/lib/gem_sample_pruby.rb
      create  gem_sample_pruby/lib/gem_sample_pruby/version.rb
      create  gem_sample_pruby/gem_sample_pruby.gemspec
      create  gem_sample_pruby/Rakefile
      create  gem_sample_pruby/README.md
      create  gem_sample_pruby/bin/console
      create  gem_sample_pruby/bin/setup
      create  gem_sample_pruby/CODE_OF_CONDUCT.md
      create  gem_sample_pruby/LICENSE.txt
      create  gem_sample_pruby/.travis.yml
      create  gem_sample_pruby/.rspec
      create  gem_sample_pruby/spec/spec_helper.rb
      create  gem_sample_pruby/spec/gem_sample_pruby_spec.rb
Initializing git repo in /Users/200135/work/ruby/gem/gem_sample_pruby
==========================
```


# rbenv

## rbenvインストール
システムにインストールする。rubyは2.2.2。

### CentOS6.5

``` bash:rbenv_install
### 必要なパッケージをインストール 
$ sudo yum install -y gcc gcc-c++ make patch unzip bzip2-devel zlib-devel openssl-devel gdbm-devel ncurses-devel freetype-devel readline-devel sqlite-devel libxml2-devel libxslt-devel libffi-devel libjpeg-turbo-devel libpng-devel libyaml-devel

$ sudo visudo
==================================
### rbenvでsudoを使ってもpathが通るように追加
Defaults env_keep += "PATH RBENV_ROOT"

### secure_pathが設定されている場合は先頭にrbenvのPATHを追加
Defaults    secure_path = /usr/local/rbenv/bin:/usr/local/rbenv/shims:/sbin:/bin:/usr/sbin:/usr/bin
==================================

### rbenv関連パッケージをclone
$ cd /usr/local
$ sudo git clone git://github.com/sstephenson/rbenv.git rbenv
$ sudo git clone git://github.com/sstephenson/ruby-build.git rbenv/plugins/ruby-build
 
### ruby-buildのインストール
$ cd rbenv/plugins/ruby-build/
$ sudo ./install.sh
 
### rbenvが使う環境変数を設定
$ sudo vi /etc/profile.d/rbenv.sh
==================================
export RBENV_ROOT="/usr/local/rbenv"
export PATH="/usr/local/rbenv/bin:$PATH"
eval "$(rbenv init -)"
==================================
$ sudo source /etc/profile.d/rbenv.sh
 
### ruby2.2.2をインストールし、デフォルトで使うように設定
$ sudo rbenv install -l
$ sudo rbenv install 2.2.2
$ sudo rbenv global 2.2.2
$ sudo rbenv rehash

### パッケージのインストール
$ sudo gem install bundler -q --no-rdoc --no-ri
$ sudo gem install pry pry-doc
```

# Serverspec
サーバーの自動テストを実現するツール

## インストールとテスト

``` shell
$ cd ~
$ mkdir serverspec
$ cd serverspec

### Gemfileの作成
$ vi Gemfile
================================================
source 'https://rubygems.org'

gem 'serverspec'
gem 'rake'
gem 'highline'
================================================

### vendor/bundle配下にgemをインストール
$ bundle install --path=vendor/bundle

### 初期設定
$ bundle exec serverspec-init
================================================
Select OS type:

  1) UN*X
  2) Windows

Select number: 1

Select a backend type:

  1) SSH
  2) Exec (local)

Select number: 1

Vagrant instance y/n: n
Input target host name: 127.0.0.1
 + spec/
 + spec/127.0.0.1/
 + spec/127.0.0.1/sample_spec.rb
 + spec/spec_helper.rb
 + Rakefile
 + .rspec
================================================

$ rm spec/127.0.0.1/sample_spec.rb
$ vi spec/127.0.0.1/ssh_port_spec.rb
================================================
require 'spec_helper'
set :request_pty, true

describe port(22) do
  it { should be_listening }
end
================================================

$ bundle exec rake spec:127.0.0.1
```

## roleを使えるようにしたい

本家の「[How to use host specific properties](http://serverspec.org/advanced_tips.html)」の通りだと動かなかったので編集個所をメモ書き。

``` shell
### テスト用ディレクトリの削除
rm -rf spec/127.0.0.1

### Rakeファイルの編集
$ vi Rakefile
$ cat Rakefile
================================================
require 'rake'
require 'rspec/core/rake_task'
require 'yaml'   ### ここ追加

properties = YAML.load_file('properties.yml')  ### ここ追加

desc "Run serverspec to all hosts"
task :spec => 'spec:all'  ### serverspecをspecに変更

namespace :spec do  ### serverspecをspecに変更
  task :all => properties.keys.map {|key| 'spec:' + key.split('.')[0] }  ### serverspecをspecに変更
  properties.keys.each do |key|
    desc "Run serverspec to #{key}"
    RSpec::Core::RakeTask.new(key.to_sym) do |t|  ### ここのsplitなんとかを消す
      ENV['TARGET_HOST'] = key
      t.pattern = 'spec/{' + properties[key][:roles].join(',') + '}/*_spec.rb'
    end
  end
end
================================================

### spec/spec_helper.rbの編集
$ vi spec/spec_helper.rb
$ cat spec/spec_helper.rb
================================================
require 'serverspec'
require 'net/ssh'
require 'yaml'  ### ここ追加

properties = YAML.load_file('properties.yml')  ### ここ追加

set :backend, :ssh

if ENV['ASK_SUDO_PASSWORD']
  begin
    require 'highline/import'
  rescue LoadError
    fail "highline is not available. Try installing it."
  end
  set :sudo_password, ask("Enter sudo password: ") { |q| q.echo = false }
else
  set :sudo_password, ENV['SUDO_PASSWORD']
end

host = ENV['TARGET_HOST']
set_property properties[host]  ### ここ追加

options = Net::SSH::Config.for(host)

options[:user] ||= Etc.getlogin

set :host,        options[:host_name] || host
set :ssh_options, options

# Disable sudo
# set :disable_sudo, true


# Set environment variables
# set :env, :LANG => 'C', :LC_MESSAGES => 'C'

# Set PATH
# set :path, '/sbin:/usr/local/sbin:$PATH'
================================================

### テストが通らないときserverspec自体がエラー終了することの対応(いいのかな。。)
### serverspec 2.24.3 からは以下やらなくてもエラーでなくなったみたいです
$ cp -ip /home/${USER}/serverspec/vendor/bundle/ruby/2.2.0/gems/serverspec-2.24.2/lib/serverspec.rb
$ vi /home/${USER}/serverspec/vendor/bundle/ruby/2.2.0/gems/serverspec-2.24.2/lib/serverspec.rb
$ diff -u /tmp/serverspec.rb /home/${USER}/serverspec/vendor/bundle/ruby/2.2.0/gems/serverspec-2.24.2/lib/serverspec.rb
================================================
--- /tmp/serverspec.rb  2015-11-12 20:23:09.535833632 +0900
+++ /home/${USER}/serverspec/vendor/bundle/ruby/2.2.0/gems/serverspec-2.24.2/lib/serverspec.rb  2015-11-12 20:19:54.723834767 +0900
@@ -43,7 +43,7 @@
         begin
           lines = []
           lines << "On host `#{host}'" if host
-          lines << failure_slash_error_line unless (description == failure_slash_error_line)
+          #lines << failure_slash_error_line unless (description == failure_slash_error_line)
           lines << "#{exception_class_name}:" unless exception_class_name =~ /RSpec/
           encoded_string(exception.message.to_s).split("\n").each do |line|
             lines << "  #{line}"
================================================

### properties.ymlの作成
$ vi properties.yml
$ cat properties.yml
================================================
127.0.0.1:
  :roles:
    - base
  :listen_port: 22
================================================

### baseディレクトリとサンプルファイルの作成
$ mkdir spec/base
$ vi spec/base/ssh_port_spec.rb
$ cat spec/base/ssh_port_spec.rb
================================================
require 'spec_helper'
set :request_pty, true

describe port(property[:listen_port]) do
  it { should be_listening }
end
================================================

### コマンドの確認
$ rake -T
================================================
rake spec            # Run serverspec to all hosts
rake spec:127.0.0.1  # Run serverspec to 127.0.0.1
================================================

### 実行
$ bundle exec rake spec:127.0.0.1
```

## サンプル

* [githubに上げてる個人的に作っているファイル](https://github.com/ftakao2007/spec)

# Rspec

``` shell
$ vi Gemfile
================================================
...
gem 'rspec'
...
================================================

$ bundle install --path vendor/bundle
$ mkdir -p spec/models/webmail
$ vi spec/mail_spec.rb
================================================
require './spec/models/webmail/mail'

RSpec.describe Mail do
  describe "#score" do
    it "returns 0 for an all gutter mail" do
      mail = Mail.new
      20.times { mail.roll(0) }
      expect(mail.score).to eq(0)
    end
  end
end
================================================

$ vi spec/models/webmail/mail.rb
================================================
class Mail
  def roll(pins)
  end

  def score
    0
  end
end
================================================

$ bundle exec rspec spec/mail_spec.rb --color --format doc
================================================
Mail
  #score
    returns 0 for an all gutter mail

Finished in 0.00108 seconds (files took 0.05672 seconds to load)
1 example, 0 failures
================================================
```