pry
- irbの代替となる強力な対話型インタプリタ
- 主な機能
- 確定した入力、評価結果の出力、入力履歴のシンタックスハイライト
- クラスやモジュール、またはメソッドを定義しているソースコードを参照する
- クラスやモジュール、またはメソッドのドキュメントを参照する
- エディタを起動し、その場でメソッドの定義を書き換える
- シェルコマンドを呼び出す
- Gistと連携する
- スコープの移動
- プログラム実行時に任意の場所で実行を停止し、Pryの対話環境を起動する
- プラグインによる拡張機能
インストール
- pry
- 本体
- pry-doc
- Ruby本体のC言語による実装を参照する機能をPryに追加する
- プラグイン
- pry-stack_explorer
- Pryを起動した時点でのそれまでの呼び出しスタックを表示する
- スタックが示すコンテキストを選択して移動できる
- pry-byebug
- コードをステップ実行する
- Ruby2系で利用
- Ruby1.9まではpry-debuggerを使う
- pry-stack_explorer
pryインストール
### Pryインストール
$ gem install pry pry-doc
### プラグインのインストール
$ gem install pry-stack_explorer pry-byebug
pryを使ってみる
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
- 引数なしで一覧が表示される
- 引数をつけるとそのコマンドの詳細が見える
プログラム実行時に任意の場所で対話環境へ移行する
任意の場所で対話型環境へ移行
$ 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形式で記述されたコメント
- 引数で渡したメソッドやクラス、モジュールのドキュメントをPry上で確認できる
- edit
- Pryからエディアタを起動する
- エディタで保存した記述をPry上で評価できる
- デフォルトだとnanoが起動する
- nano以外を使う方法
- Pry.config.editor = "(edita)" のように設定する
- 環境変数EDITORに起動したいエディタを設定する
- ubuntuの場合は
- Pryからエディアタを起動する
- その他コマンドについて
- helpかgithub上のWikiを見る
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"
- 利用するエディタをviにする
- 設定例
プラグインの利用
pry-stack_explorer
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
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連携
インストール
### 必要なパッケージのインストール (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は
- ビルドの際に生成された一時ファイrを削除する
簡単な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
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
一日の食事記録を作成しました
==========================
ファイルタスク
ファイルタスク
### ファイルタスクを利用する場合。事前のファイルやディレクトリの作成が不要
$ 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
==========================
クリーンタスク
クリーンタスク
### クリーンタスクの追加
$ vim Rakefile3
==========================
require 'rake/clean'
CLEAN.include('mealss/*')
CLOBBER.INCLUDE('reports/*')
==========================
### 一時ファイルのファイルを削除
$ rake -f Rakefile3 clean
$ ls meals
==========================
(何もなし)
==========================
### 一時ファイルと成果物を削除
$ rake -f Rakefile3 clobber
$ ls reports
==========================
(何もなし)
==========================
ディレクトリタスク
- ビルド処理の過程で生成物を特定のディレクトリに格納したい場合などに使用する
- 対象ディレクトリが存在しないときは自動的に作成する
- 既に存在する場合はスキップする
- ブロックを渡す事はできない
- 作成したディレクトリを利用するタスクはtaskメソッドを用いて記述する
ディレクトリタスク
$ 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という拡張子をソースファイルとしてコンパイルするという用途で利用
- .oという拡張子を持つコンパイル後のオブジェクトファイルが必要なタスク
- メリット
- 直接ファイル名を記述せずに済む
ルールの例
### t.sourceが.cファイル、t.nameが.oファイル
rule '.o' => ['.c'] do |t|
sh "cc #{t.source} -c -o #{t.name}"
end
グループ化
- 複数のタスクをグループ化する
- namespaceメソッド
グループ化
$ 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
- 9番目
- v10.0.0での変更点
- いくつかのdeprecatedな機能が削除されている
- 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系で一番新しい
- -v : バージョンを指定する
- 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など
- gem install (gemパッケージ) : 依存するパッケージも含めてインストールする
- コマンド
- .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する
- bundle install --path vendor/bundle
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パッケージ名を指定するとインストールされている場所が表示される
- bundlerで管理されている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を起動する
- 引数でグループが指定可能
- Bundlerで管理しているgemパッケージを予め読み込んだ上でIRBを起動する
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
- コロン区切りで複数のグループを指定できる
- BUNDLE_WITHOUT: development:test
- binstubsオプション
- bundle exec無しにコマンドを実行できる
- Gemfileが存在するディレクトリと同じ階層にbinディレクトリを作成
- gemパッケージに付随するコマンドをbinディレクトリに配置する
- 別の解決方法としてコマンドエイリアスでalias be='bundle exec'とする事がよくある
一歩踏み込んだ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の処理系のみインストール指定できる
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にキャッシュされる
- gemパッケージをキャッシュに保存するコマンド
- bundle install --local
- vendor/cacheにあるものを利用してインストール
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
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
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で出なくなった
==========================
/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
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
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'
/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を使って雛形を作る
雛形を作成
### 雛形を作る
$ 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
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
サーバーの自動テストを実現するツール
インストールとテスト
$ 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」の通りだと動かなかったので編集個所をメモ書き。
### テスト用ディレクトリの削除
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
サンプル
Rspec
$ 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
================================================