注目の記事

Chef、Vagrantに興味があるけどRubyをやったことない技術者が最低限知っておいた方がいい知識 まとめ

2013.09.04

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Chef Vagrant Ruby

最近、ChefやVagrantを勉強し始めたのですが、設定がRubyで記述されているため私は「たのしいRuby」を読んで勉強することにしました。 私は元々Rubyに興味があったのでいいのですが、Ruby知らないしそのために勉強するのは大変だから手を出すのをやめとこう、と思う人がいるかもしれないのでこれだけ知っていればある程度読めるのではないか、というポイントをまとめました。

まったくプログラミングをしたことない方には説明するのは無理なのでプログラミング経験があり
条件分岐、ループ、クラス、メソッド、文字列 が何か分かるくらいの方が対象です。if文、for文など他の言語でもあるようなものは省いていきます。
真面目にRubyを勉強したい方は下のアフィリエイトから「たのしいRuby」をポチって下さい!

まずはHello World

まずはHello World です。実際にやってみる必要はないと思いますが目を通しておいて下さい。
以下のようなソースを書きます。ファイル名はhello.rb にして下さい。

def hello name
  print "Hello World. #{name}\n"
end

# 定義したメソッドを呼び出す
hello "Hakamata"

rubyコマンドを叩くと上から実行されていきます。上から実行されるのでメソッドの定義は呼び出されている箇所よりも上にある必要があります。

ruby hello.rb

最初にhelloというメソッドを定義しています。引数はnameという名前の変数です。
2行目のprintメソッドは標準出力に文字列を出力する組み込み関数です。組み込み関数というのは自分で定義しなくても使えるメソッドのことです。
printメソッドの引数に文字列型のオブジェクトを渡します。標準出力にHello World という文字列と一緒に名前を出力していますが、
文字列の中に変数を埋め込みたい場合は #{変数名} で埋め込む事ができます。文末にセミコロンは必要ありません。
標準出力に文字列を出力する組み込み関数には他にもputs, p, ppなどがあります。
putsは改行文字を入れなくても改行してくれるのでここからはputsを使っていきます。

あとRubyは括弧を省略して書く事ができるので人によって書き方が違うと思います。 上記のソースは省略しないで書くと以下のようになります。

def hello(name)
  print("Hello World. #{name}\n")
end

# 定義したメソッドを呼び出す
hello("Hakamata")

条件分岐

プログラミング経験がある方であれば if文、case文は見れば何をやっているのか大体分かると思いますので、unless文を紹介します。
unless は if の逆で渡した条件がfalseだった場合に中の処理が実行されます。

unless 条件
  # 条件がfalseの場合に実行される処理
end

またRubyでは if や unless を以下のように式の最後に付ける事ができます。
この例でいうと変数aが1よりも大きい場合にHello World.が標準出力に出力されます。

a = 2
puts "Hello World." if a > 1

繰り返し

until文

プログラミング経験がある方であれば for文、while文は見れば何をやっているのか大体分かると思いますので、until文を紹介します。
until は while の逆で渡した条件がfalseだった場合に中の処理が実行されます。

until 条件
  # 条件がfalseの場合に繰り返して実行される処理
end

loopメソッド

次にloopメソッドを説明します。loopメソッドは組み込み関数になります。
loopメソッドは中でbreakしない限り終了しません。

loop do
  # 繰り返したい処理
end

count = 0
loop do
  break  if count == 3
  puts count
  count+=1
end

あとRubyは do や end を中括弧で書く事ができることも知っておいた方がいいです。上記のソースは中括弧で書くと以下のようになります。

count = 0
loop {
  break  if count == 3
  puts count
  count+=1
}

範囲オブジェクト

a..b や 1...10 のような表現を見る事があったらそれは範囲オブジェクトというものです。
for文で1つづつ要素を取り出して処理する事ができます。

a = 5
b = 10

for i in a..b
  puts i
end

.. と ... の違いは最後の値を含めるかどうかです。... の場合は含みません。
上記の例ではa..bをa...bに修正して実行すると10が出力されなくなります。

シンボル

Vagrantfileを生成して中を見てみると :chef-solo という表現がありますがこれはシンボルというものです。
大まかにいうとオブジェクトの識別に使う文字列といった感じでしょうか。主に以下のようにハッシュ(連想配列)のキー値に使われたりします。

person = {:name => "Hakamata", :age => 35, :address=> "Tokyo"}
person = {name: "Hakamata", age:35, address:"Tokyo"} #この書き方でも同じ意味

# ハッシュから値を取り出して標準出力に出力する
puts person[:name]

ハッシュのキーに文字列を使うこともできますが、シンボルを使った方が同じかどうかの比較が早いなどのメリットがあるそうです。
他の言語では文字列を使うところで代わりに使う感じなので、とりあえず文字列みたいなものと思っておけばいいのではないでしょうか。

キーワード引数

Rubyではメソッドの引数に名前を付けることができます。以下ではnameという引数に"Hakamata"という文字列を渡しています。
宣言部分の"Ruby"はデフォルト値です。引数に何も渡さなかった場合、"Ruby"という文字列を渡した場合と同じ処理になります。

def hello name:"Ruby"
  print "Hello World. #{name}\n"
end

# キーワード引数に代入
hello :name => "Hakamata"

ブロック付きメソッド

VagrantfileやChefのrecipeでよく使われている構文です。ブロック付きメソッドは一部の処理を呼び出す時点で定義することができます。
定義は以下になります。

def メソッド名 引数 
  yield ブロック変数 
end

呼び出し側は以下のようになります。ブロック内の処理はメソッド定義のyieldある個所で実行されます。
ブロック内で使う変数(ブロック変数)は定義側でyieldの引数に渡したものが入ってきます。

メソッド名 メソッドの引数の変数名 do |ブロック変数|
  # yieldがある箇所に入れたい処理
end

私の知っている範囲では他の言語にはあまりない機能だと思いましたが、強いて言えばJavaScriptのファンクションの引数に無名ファンクションを渡すのに近いような気がします。Vagrantfileに以下のような箇所がありますが、

config.vm.provision :chef_solo do |chef|
  chef.cookbooks_path = "cookbooks"
  chef.run_list = "recipe[centos]"
end

Javascriptに置き換えると大体以下のような意味だと思います。

provision("chef_solo", function (chef) {
  chef.cookbooks_path = "cookbooks";
  chef.run_list = "recipe[centos]";
});

Vagrantfileを読み込んでいるところの実装はどうなっているのかは分かりませんが、
以下のように引数で渡したシンボルによって処理が分かれているような感じではないでしょうか?
16行目のblock.jsは上記のJavaScriptのファイルです。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <script>
			function provision(symbol, method) {
				var chef = {};
				if(symbol == 'chef_solo') {
					# chef_soloの場合の処理
				} else if(symbol == 'chef_client') {
					# chef_clientの場合の処理
				}
				method(chef);
			}
		</script>
        <script src="block.js"></script>
	</head>
</html>

ブロックを使った繰り返し

timesメソッド

Rubyではブロック付きメソッドを使って繰り返し処理を行う事ができます。上で紹介したloopメソッドも実はブロック付きメソッドです。
まず最初はRubyのIntegerクラスで実装されているtimesメソッドです。繰り返したい回数を指定できます。

Integer型のオブジェクト.times do |変数|
  繰り返し行いたい処理
end

以下が例です。変数の部分には繰り返した回数が代入されます。

3.times do |i|
  # 3回だけ実行したい処理
  puts i
end

eachメソッド

次は配列オブジェクトで使えるeachメソッドです。
配列の要素を順番に取り出していきます。

配列オブジェクト.each do |変数|
  繰り返し行いたい処理
end

以下が例になります。変数の部分には取り出した配列の要素が代入されます。

animals = ["lion", "elephant", "giraffe"]
animals.each do |animal|
  puts animal
end

あとChef を使っていると以下のようなソースを見ることがあるかもしれませんが、%wは文字列の配列を定義する時に使います。
以下の例でいうと %w[make gcc openssl-devel] という部分は ["make", "gcc", "openssl-devel"] と同じ意味になります。

%w[make gcc openssl-devel].each do |name|
  package name do
    action :install
  end
end

その他メモ

require
引数に指定したRubyファイルをインポートします。requireの引数にはファイル名から拡張子を外したものを指定します。
include
引数に指定したモジュールの機能を使えるようにします。
ENVオブジェクト
環境変数を取得できるオブジェクトです。例えば ENV['SHELL'] で使っているシェルの名前を取得できます。

まとめ

この記事を書くにあたりどの程度まで書くか悩んだのですが、上記のポイントを抑えていれば大体の設定は読めるのではないでしょうか?
Chef、Vagrantはとても便利なツールなので、Rubyを知らない方もこの記事を読んで試してみて下さい。

設定にRubyを使うツールとしては他にもRoadworkerというAmazon Route53の管理ツールがあるので興味のある方はこちらもご覧下さい。
Route53をgitで管理する「Roadworker」を早速試してみました
Amazon Route53管理ツール Roadworkerの設定(Routefile)レシピ

Amazonでチェックする