Rubyめも
基本
全般
文の区切りに ; は不要
真偽値
false : nil, false:
true : それ以外(0も1もtrue)
→ なので、
"abcdef" .index("abc" ) は 0 なので true
"abcdef" .index("bcd" ) は 1 なので true
"abcdef" .index("ccc" ) は nil なので false
print, puts, p, pp
print : 文字列表示
puts : print + 改行
p : オブジェクトの内容を表示(数値と文字列の区別が付く) +改行
改行やタブなどの文字をそのまま(\n, \t と)表示する
pp : 見やすい形式に (require 'pp' しとく必要あり) ※ PHPでいう var_dumpみたいな
gets
ファイルから1行ずつ取り出す
include
例) include Math と冒頭に書けば
Math::sin(xxxx) と書くところを sin(xxxx) と書ける。
コメント
# シェルと同じ
require
別のファイル(ライブラリ)を読み込む
require xxxxx[.rb]
型変換 (キャストとかparseIntとか)
文字列を数字として
文字列.to_i
文字コードから文字へ
文字コード.chr
文字コードを取り出す
?文字
"文字" [0]
コマンドライン引数
ARGV[x] で取り出す
比較
オブジェクトが同じかどうか判別
equal? : オブジェクトIDが同じかどうか判別
※ objA.equal?(objB) のように書く
objA.id == objB.id と同じ結果
値が同じかどうか判別
== : 通常の比較
eql? : 厳密に比較(1.0 と 1 を区別したり)
文字列が空かどうか
empty?
オブジェクトのクラス名は? (typeof, type_of ではない)
obj1.class # obj1 のクラス名は?
obj1.instance_of?(String) # obj1 は Stringクラスか
obj1.is_a?(String) # obj1 は Stringの子クラスか
変数
[A-Z] (大文字)で始まるもの : 定数
[a-z_] (小文字)で始まるもの : ローカル変数
$ で始まるもの : グローバル変数
@ で始まるもの : インスタンス変数
@@ で始まるもの : クラス変数
演算子
** : べき乗
が使える
* : 文字列に対して使うと「繰り返し」になる (Perl でいう x と同じ)
制御文
※ if, case...when の then
while, for, until の do は省略可能
if .. then .. else .. end
if 条件1 then
処理1
elsif 条件2
処理2
else
処理2
end
# ・ end に注意
# ・ 条件の部分は 0 でも通る
# ・ 通らないのは false, nil のとき
unless
unless 条件1 then
偽の場合に実行
end
case (switchではない)
case オブジェクト名
when 値1 then
文1
when 値2, 値3, 値4 then # 複数指定可
文2
...
when /^TODO
else
文n
end
※ break とか要らない
※ when の条件は、型でもいい。正規表現の場合は =~ と同じ比較をする
→ 内部で === という演算子を使用している
case オブジェクト名
when String then
xxx
when Numeric then
yyy
else
zzz
end
while .. [ do ] ... end
while 条件 do
処理
end
until .. [ do ] ... end
until 条件 do
偽の場合に処理
end
times
# 100回繰り返す (中で変数 i を使わない場合は |i| は不要)
100.times {|i|
print i, "回目の繰り返しです。\n"
}
※ これは Integerクラスのメソッド
for ... in ... [ do ] ... end
for 変数 in オブジェクト do
処理
end
※ 整数の配列の場合
for i in 1..5
処理
end
each (Enumurable#each)
# 配列の場合
配列.each {| 変数 |
処理
}
# ハッシュの場合
ハッシュ.each {|key, value|
処理
}
※ each の代わりに Enumurable#collect を使用すると、
処理した結果が配列として得られる
※ Enumurable のメソッドには 他に sort がある
loop
loop {
# 無限ループ
asdas
if xxx then
break
end
}
繰り返しの制御
1つ分のループを抜けるとき
(Ruby) (Cでいう)
break break
next continue
redo
入れ子になったループを抜けるとき(gotoの使い方と同じ?)
catch (:ラベル名) {
loop {
loop {
if xxx
throw :ラベル名, val #
end
}
}
}
メソッド(関数、function、sub、プロシージャ)
定義
インスタンスメソッド
def メソッド名(引数1, 引数2, ...)
処理
return 値1 # 戻り値が無い場合は不要
end
クラスメソッド
def self.メソッド名(引数1, 引数2, ...)
処理
return 値1 # 戻り値が無い場合は不要
end
※ 引数の部分を「*引数3」のように * をつけておくと、配列が渡せる
呼び出し
j
オブジェクト名.メソッド名(引数1, 引数2, ...)
※ クラスメソッドの場合
オブジェクト名::メソッド名 とも書ける
クラス
定義
class クラス名
Version = "1.0" # 定数
@@count = 0 # クラス変数
# コンストラクタ
def initialize(引数1=デフォルト値, 引数2, ...)
@name = asfa # インスタンス変数
aaa
end
# インスタンスメソッド
def xxx
xxxx
end
# クラスメソッド
def クラス名.yyy
yyyy
end
end
メソッドのアクセス指定子
方法:書き方が2通りある
i) メソッドの定義の後に「public :メソッド名」 のように書く。
(指定したメソッドが public になる)
ii) メソッド定義の前に 引数を指定せずに 「private」のように書く。
(メソッド1個ではなく、この行以降に定義されたメソッドが全部 private になる)
種類
public(デフォルト), private, protected
アクセサを自分で書く場合の書き方(参考)
# 定義
class クラス名
:
def name
return @name
end
def name=(value) # name= という名のメソッド (!)
@name = value
end
end
# 呼び出し時
print インスタンス.name # 参照
インスタンス.name = "xxx" # 更新
アクセサを自動生成?する場合の書き方
attr_reader :変数名 # 参照
attr_writer :変数名 # 更新
attr_accessor :変数名 # 参照と更新両方
attr_accessible :変数名 # 代入を許す
attr_protected :変数名 # 代入禁止
→ これを クラス定義の直下(メソッドと同列)に書く
既に定義されているオブジェクト(Stringとか)にメソッドを追加することもできる
→ レシーバは self を使用する
例)
class String
def count_word
ary = self.split(/\s+/)
return ary.size
end
end
継承
class 子クラス < 親クラス
def メソッド名
xxxxxxxxx
super() # 親クラスではなく、
# 親クラスの同名のメソッドが呼び出される
end
end
モジュール
定義
module モジュール名
def メソッド名
end
module_function :メソッド名
end
呼び出し
モジュール名::メソッド名
例外処理
構文
begin # try {
xxxxx #
rescue Exception1 => 変数 # } catch (Exception1 変数) {
xxxxx #
rescue Exception2 => 変数 # } catch (Exception2 変数) {
xxxxx # ...
rescue # } catch (Exception 変数) {
xxxxx #
ensure # } finally {
xxxxx #
end # }
※「 => 変数」のようにしなくても $! で取り出せる
例外を投げるときの書き方(4通り)
raise 例外クラス, メッセージ
raise 例外クラス
raise メッセージ # RuntimeError が raise される
raise
※ raise のときだけ rascue 節の内外で処理が違う
rescue節の外 : RuntimeErrorを発生させる
rescue節の中 : 最後に発生した例外をもう一度発生させる ( == raise $!)
例外オブジェクト($!)のメソッド
class : 種類(クラス名)
message : メッセージ
backtrace : 位置情報
自動的にセットされる情報
$! : 最後に発生した例外(の例外オブジェクト)
$@ : 最後に発生した例外の位置に関する情報 ( == $!.backtrace)
定義済みのクラス
一覧
Numeric
Integer
Fixnum : 普通の整数
Bignum : 大きな整数
Float
String
Hash
Array
Regexp
IO
File
Time
Integer
n.times {|i| ... } : n 回繰り返し
from.upto(to) {|i| ... } : from から to まで繰り返し(加算)
from.downto(to) {|i| ... } : from から to まで繰り返し(減算)
from.step(to, step) {|i| ... } : from から to まで繰り返し(stepずつ)
n.abs : 絶対値
Float
f = 3.4
f.ceil : 繰り上げ
f.truncate : 切り捨て
f.round : 四捨五入
String
代入
%Q, %q
str1 = %Q { 文字列のなかに "xxx" とか 'xxx' があるときに使う }
ヒアドキュメント
str1 <<-EOF
asdfa
asfs
as
EOF
※ <<- と EOF の間にスペースを含めない
※ 空白が無視されるのは EOF 行のみ
※ 式展開やエスケープさせたくない場合はシングルクオートで囲う
str1 = <<'EOF'
xx
xxx
EOF
※ 文字列をつなげたいときはなんか変に見えるかもしれないが気にしない
html << <<-HTML
</table>
HTML
式展開
"aaa #{xxx} bbb" : xxx 部分を Rubyの式として実行し、置き換える
文字列の長さを得る
str1.length # アルファベット
str1.size # 〃
str1.split(//e).length # 日本語(EUC-JP)
str1.split(//s).length # 日本語(Shift_JIS)
str1.split(//).length # $KCODEの設定で
文字列を分割する
ary1 = str1.split(/正規表現/) # 区切り文字で区切られてる場合
ary1 = str1.unpack("a10a30a20" ) # バイト数を指定して区切る場合
連結
str1 << str2
str1.concat(str2)
その他 : 基本的に配列(Array)と一緒
str1.chop! : 後ろ1文字を削る
str1.chomp! : 改行がある場合に削る
str1.index(str2) : 見つからなかった場合は nil。1文字目で見つかったら 0。
str1.rindex(str2)
str1.slice
str1.delete(str2)
str1.reverse
str1.upcase ※ toupper ではない
str1.downcase ※ tolower ではない
str1.swapcase
str1.capitalize
str1.tr("abc" , "ABC" )
str1.strip : 前後の空白を取り除く
文字コード変換
$KCODE = 'UTF8' # or EUC, SJIS, NONE ※ JIS(iso-2022-jp)は無いらしい
require 'kconv'
入力文字を自動判別する場合
str2 = str1.tojis
str2 = str1.toeuc
str2 = str1.tosjis
str2 = str1.toutf8
入力文字列の文字コードを明示的に指定する場合
str2 = str1.kconv(Kconv::SJIS, Kconv::EUC) # EUC→SJIS(逆に思いがちなので注意)
※ 他の書き方
str2 = Kconv::kconv(str1, Kconv::SJIS, Kconv::EUC) # Kconv の場合
str2 = NKF::nkf("-Es" , str1) # NKF の場合
succ
「次の」文字列を返すメソッド
p "aa" .succ # => "ab"
p "99" .succ # => "100"
p "a9" .succ # => "b0"
p "Az" .succ # => "Ba"
p "zz" .succ # => "aaa"
p "-9" .succ # => "-10"
p "9" .succ # => "10"
p "09" .succ # => "10"
p "1.9.9" .succ # => # "2.0.0"
※使わないような気がするけど面白い
Array : 配列
初期化
ary1 = ["xxx" , "yyy" , "zzz" ]
ary2 = %w( xxx yyy zzz )
ary3 = []
ary4 = Array.new(サイズ, 初期値)
サイズ (length とか count ではない)
ary1.size
取り出すとき
1つ分の要素を取り出す
ary1[0] : 0番目の要素を取り出し
ary1[-2] : 後ろから2番目の要素を取り出し
配列を取り出す
ary1[3..5] : 3番目から5番目まで取り出し
ary1[3, 2] : 3番目から2つ取り出し
ary1.indexes(3, 5, 8) : 飛び飛びに取り出せる
集合として(配列を)取り出す
ary1 & ary2 : 共通集合
ary1 | ary2 : 和集合
ary1 - ary2 : 集合の差
ary1 + ary2 : ただ足すだけ(同じ要素があれば重複する) (== concat)
キューとスタック
先頭 末尾
要素を加える unshift push (a << item という書き方も可)
要素を取り除く shift pop
要素を参照する first last
その他配列操作
ary2 = ary1.compact : nil の要素を取り除く
ary1.compact! : 〃 (元の配列を書き換える)
ary1.delete(xxx) : xxx の「値の」要素を取り除く
ary1.delete_at(n) : n番目の要素を取り除く
ary1.delete_if {|item| 条件 } : 条件が真のものを削除
ary1.reject! {|item| 条件 } : 〃
ary1.concat(ary2) : ary1の後ろにary2を足す
ary1.slice(n..m) : nからmまで取り出す
ary1.slice!(n..m) : 取り出した分を削除(JavaScript, ActionScriptでいうsplice)
ary1.uniq! : 重複を削除
ary1.fill(値, 3..5) : 埋める
ary1.flatten! : 入れ子になった配列を平坦化
ary1.collect {|item| 処理 } : 配列の中身をまとめて変換
ary1.map {|item| 処理 } : 〃
ary1.reverse! : 逆順にする
ary1.sort! : sortする
ary1.index(elem) : 要素が何番目なのかを取得 (at() とか indexOf(), index_of() ではない
※ 無かったら -1 ではなく nil
※ "!" が付いているものは 破壊的(元の配列を書き換える)メソッドで
付けられるがついていないものは、新しい配列を返す
ソート順を定義する
# sort の評価式を定義したいとき
ary1.sort {|a, b|
a <=> b
}
※ キーが一致するものは削除されてしまう?ので注意
sort | uniq した状態に・・?
→ そんなことは無いみたい
オブジェクトの配列を uniq したいとき
uniq の重複判定に使われる eql? メソッドをオーバーライドすればいい
hash メソッドもオーバーライドする必要あり
※ マニュアルより
A.eql?(B) が成立する時は必ず A.hash == B.hash も成立しなければいけません。
eql?を再定義した時には必ずこちらも合わせて再定義してください。
※ こんな感じ?
class Test1
def eql?(other)
@key_for_uniq == other.key_for_uniq
end
def hash
# ?
end
end
→ hash の再定義の方法が分からない
あるか無いかチェック (exists, 存在チェック)
ary1.index(elem) を使う
配列のコピー
浅いコピー
ary2 = ary1.dup
深いコピー
ary2 = Marshal.load(Marchal.dump(ary1))
Hash : ハッシュ
初期化
(1) {} を使用する
h1 = {
"normal" => "+0" ,
"small" => "-1" ,
"big" => "+1"
}
h2 = {}
(2) Hash.new を使用する
h1 = Hash.new(0) # 新しくキーが作られるときの初期値を 0 に
h2 = Hash.new # 指定しなければ nil がデフォルト
※ 初期値は、出てきた単語をカウントするときなんかに使うといい
値を取り出す
(1) [] を使う
h["name1" ]
h[:name1] # <= このようにシンボルを使うのが一般的みたい
(2) fetch を使う
h.fetch("name1" )
h.fetch("name1" , "ない" ) # ない場合のデフォルトを別で指定したい場合
値を設定する
(1) = を使う
h["name1" ] = "aaaa"
(2) store を使う
h.store("name1" , "aaaa" )
あるか無いかチェック (exists, 存在チェック)
キー : h.has_key?(key)
値 : h.has_value?(value)
※ h[key].nil? だと以下の区別がつかないので has_key? を使う
・キーがあるけど nil
・キーがないので nil
削除
h.delete("name1" )
クリア
h.clear
キーでソート、値でソート
値でソートする
ary1 = hash1.sort {|a, b|
a[1] <=> b[1]
}
キーでソートする
ary1 = hash1.sort {|a, b|
a[1] <=> b[1]
}
でも戻り値は配列になる
※ 次のように取り出されるので、a[1], b[1] を比較する
a[0] : key
a[1] : value
※ なので中身を見るのはこんな感じ
ary1.each {|a|
puts "#{a[0]} => #{a[1]}"
}
※ 文字列でも数値でも <=> を使う
全部の要素について処理
hash1.map {|key, val| 処理 }
逆引きハッシュ
h.invert
配列として取り出す
hash1.to_a : 2次元配列として取り出せる
hash1.keys : キーのみの1次元配列
hash1.values : 値のみの1次元配列
Regexp : 正規表現
書き方
/xxx/
%r{xxx} # "/" をパターンに含める場合などに使用
pattern = Regexp.new("xxx" )
pattern = Regexp.new(Regexp.quote("xx*x" )) # メタ文字をエスケープ
マッチ(match)したか判別 : =~ ※ ~= は誤り
マッチした位置を返す。無い場合は nil
(true とか false とかじゃないことに注意)
/xyz/ =~ "abc lmn xyz mno" # この場合は 8 が返る
pattern =~ "xxasdfa fzzaasd"
オプション
/i : 大文字小文字区別しない
/m : "." が改行文字にもマッチ
/s : SJIS としてマッチ
/e : EUC 〃
/u : UTF-8 〃
/n : 文字コードを意識しない
/x : 正規表現内の空白と # の後ろの文字を無視する
後方参照
$1, $2, ... : ()内の文字列
$`, $&, $' : マッチの前、マッチした部分、マッチの後 を取り出す
置き換え時の参照 (呼び方忘れた)
\1, \2 を使う
str.gsub(/([0-9]+) coins/, '\1 points' )
置き換え(置換、replace)
sub : 最初にマッチした部分のみ置き換え
gsub : マッチする部分全て
scan : 置き換えないで取り出すだけ
(1) 全て同じ文字列で置き換え
gsub(/パターン/, "置き換え文字列" )
(2) ブロックを使って処理する場合
gsub(/パターン/) {|matched|
xxxxx
}
※マッチした部分全部について処理したいときとか
scan を使う
例: iniファイルな形式の文字列からキーと値の組を取り出す
line.toutf8.scan(/(.*)?= +(.*)/) {|key, val|
puts "#{key.strip} => #{val.strip}"
}
※ただ文字列が含まれるか知りたい場合は
str.count('xxx' )
str.count('xxx' , 'yyy' , ...)
を使う
IO クラス
標準入出力
組み込み定数 グローバル変数
標準入力 STDIN $stdin
標準出力 STDOUT $stdout
標準エラー出力 STDERR $stderr
例)$stdout.print "xxxxx\n"
メソッド
入力
read : 全行を文字列として取り出し
read(size) : サイズを指定して取り出し
readlines : 全行を配列として取り出し
gets : 1行ずつ取り出し
each : 〃
getc : 1バイトずつ取り出し
each_byte : 〃
ungetc : 1バイト
出力
io.puts(str1, str2, ...) : 文字列に改行文字を補って出力
io.print(str1, str2, ...) : 改行文字を補わず出力
io.printf : printf
io.write(str) : 文字列を出力。書き込んだバイト数が返る
io.putc(ch) : 文字を出力
<< : str で指定した文字を出力
例) io << "xx " << "asf " << "ddd "
ファイルポインタ
io.lineno : 行番号
io.lineno= : 行を変更
io.pos : ファイルポインタの位置
io.pos= : 位置を指定
io.rewind : ファイルの先頭へ移動
io.seek(offset, IO::SEEK_SET) : 頭から数えて offset ぶん移動
io.seek(offset, IO::SEEK_CUR) : 現在位置 〃
io.seek(offset, IO::SEEK_END) : 末尾 〃
io.truncate(size) : ファイルサイズを size に切り詰める
その他
io.binmode : バイナリモードにする(改行を変換しない)
アスキーモード?にするというメソッドはない
io.flush : バッファをフラッシュ
io.sync : true にセットすると 書き込みのたびにフラッシュされる
io.popen("command" , mode) : パイプから受け取り
io.open("|command" , mode) : 〃
File : ファイル (IO の子クラス)
open/close
open
file = open(filename, mode)
file = File.open(filename, mode)
モード : r, r+, w, w+, a, a+
close
file.close
※ ブロックにして開くと close を省略できる
open(filename) {|file|
while line = file.gets
:
end
}
ファイルを読む
全行読む : read
(i) 通常
file = open(filename) # ファイルを開く
text = file.read # 文字列全部を変数に入れる
print text
file.close # ファイル閉じる
(ii) ブロックを使って1行でも書ける
data = open(filename) {|file| file.read }
1行ずつ読む : gets, each
open(filename)
while line = file.gets # 1行を変数に入れる
print
end
file.close
ファイルに書き込む
file.puts("xxx" ) ?
メソッド
File.rename(before, after) : 名前変更、移動
File.unlink(filename) : 削除
# File.delete(filename) : 削除
File.chmod(0777, path) : 属性変更
File.chown(owner, group, path) : 所有者変更
File.basename(path[, suffix]) : ディレクトリ名抜きでファイル名を取得
suffix に拡張子がある場合、拡張子なしのファイル名を取得
例) File.basename("C:/tmp/aa.txt" , ".*" )
File.dirname(path) : ディレクトリ名
File.extname(path) : 拡張子
File.split(path) : ディレクトリ名, ファイル名 の(サイズ2の)配列
例) dir, base = split(path)
File.join(dir, base) : ファイル/パス名をくっつける
File.expand_path(path) : 相対パス→絶対パス
File.fnmatch("a*.txt" ) : ファイル名のパターンマッチ
ファイル操作関連のライブラリ
fileutils.rb (1.6までは ftool.rb)
require 'fileutils'
FileUtils.cp(before, after) : コピー
FileUtils.cp_r(before, after) : コピー(再帰)
FileUtils.mv(before, after) : 名前変更、移動
(ファイルシステムやドライブをまたがって移動できる。rename では出来ない)
(複数指定可:配列形式でファイルを指定できる)
FileUtils.chdir(path[, :verbose=>true]) : ディレクトリ移動
(移動結果を表示できる)
FileUtils.rm(path[, :force=>true]) : 削除
(複数指定可)
FileUtils.rm_r(path[, :force=>true]) : 削除(再帰)
FileUtils.mkdir_p(path[, :mode=>0755]) : mkdir -p path
(複数指定可)
※ verbose が true のときは処理内容を表示
noop が true のときは実際には操作を行なわない
# require 'ftools'
# File.copy(before, after) : コピー
# File.move(before, after) : 名前変更、移動
# (ファイルシステムやドライブをまたがって移動できる。rename では出来ない)
# File.compare(file1, file2) : 一致してれば true
# File.install(from, to[, mode]) : コピーして属性をセット
# File.makedirs(path) : mkdir -p /aaa/bbb/cc な感じ
find.rb : ディレクトリ以下を再帰的に処理する
Find::find : ディレクトリ以下を再帰的に処理
Find::prune : find で現在処理中のディレクトリ以下の処理を飛ばす
require 'find'
Find::find("/xxx/yyy/" ) {|path|
if FileTest::directory?(path)
dirname, basename = File::split(path)
if basename = "CVS"
Find::prune
end
puts path
end
}
tempfile.rb
require 'tempfile'
tempfile.new(basename[, tempdir])
tempfile.close
tempfile.path
File::Stat クラス
初期化
# Etcモジュールが必要
require 'etc'
include Etc
stat = File.stat(path) # ファイル/ディレクトリの属性を取得
メソッド
ctime
mtime, atime, blocks, blksize, size, rdev, gid, uid, nlink
mode, ino, dev
※ File.ctime(path) のように使用できる
FileTestモジュール
FileTest.exist?(path) : あるかどうか
FileTest.file?(path) : ファイルかどうか
FileTest.directory?(path) : ディレクトリかどうか
FileTest.owned?(path)
FileTest.grpowned?(path)
FileTest.readable?(path)
FileTest.writable?(path)
FileTest.executable?(path)
FileTest.size(path)
FileTest.size?(path) : サイズ0以上なら true
FileTest.zero?(path) : サイズ0なら true
Dir : ディレクトリ操作
メソッド
Dir.pwd : カレントディレクトリを取得
Dir.chdir : ディレクトリを移動
Dir.rmdir : 空ディレクトリを削除
Dir.glob("*" ) : ファイルの一覧を取得。 ** で再帰的に処理できる
{aaa.html,bbb.jpg,cc.txt} という書き方も出来る
例) ディレクトリ以下を再帰的に処理
Dir.glob("/xxx/**/ *.txt").each {|filename|
xxxx
}
※ こういうのもある
# open/close, リスト出力 (あまり使わない)
dir = Dir.open
dir.each{|name|
p name
}
dir.close
※ 同じようなのが pathname でも・・
require 'pathname'
Pathname.glob('/xxx/xxx/data_*.txt' )
Pathname
children
relative_path_from(base_directory)
require 'find'
Find.find(self.to_s) {|f|
yield Pathname.new(f)
}
※ yield
ブロック内で、そのブロックを再帰的に呼びたいときに使う(?
TCPSocket
require 'socket'
s = TCPSocket.new("localhost" , 80)
s.print "GET /xxx/aaa.html HTTP/1.0\r\n\r\n" # GETメソッド送信
print s.read # 受け取る
s.close
※ 接続できなかったときの例外 : Errno::ECONNREFUSED
Time
取得
t = Time.new # 現在時刻
t = Time.local(y, m, d, h, m s) # ローカルタイムとして指定
t = Time.utc(y, m, d, h, m, s) # 協定世界時
メソッド
取得する系
t.year, month, day, hour, min, sec, to_i, wday, mday, yday, zone
その他
t.strftime(%Y/%m/%d %H:%M:%S") : 日付文字列を整形
文字列 → 配列(年、月、日、時、分、秒、タイムゾーン、曜日)
require 'parsedate'
ParseDate.parsedate(str, complete = false)
文字列 → Timeオブジェクト
require 'time'
Time.parse(date, now=Time.now)
文字列 → Dateオブジェクト
require 'date'
Date.parse(date)
日付を書式化 (format)
t.strftime('%Y%m%d %H%M%S' )
HTTPの日付
t.httpdate()
# Time.httpdate(date)
5日前の日付
date= Date.today
date -= 5
p date.to_s
5日前の日付と時刻
date= DateTime.now
date -= 5
p date.to_s
5分前とかは?
parsedate モジュール
require 'parsedate'
d = ParseDate::parsedate(str)
# ↑ 年, 月, 日, 時, 分, 秒, タイムゾーン, 曜日 の順に返す
date ライブラリ
require 'date'
Date.new(2000).leap? # うるう年かどうか判定する
csv ライブラリ
require 'csv'
CSV.parse("data.csv" ) {
p row.to_a
}
CSV.generate("out.csv" , ?,) {|writer|
writer << ["aaa" , "bbb" , "ccc" , "ddd" ]
writer << ["eee" , "fff" , "ggg,GGG" , "hhh" ]
}
cgi ライブラリ
CGI.pretty : HTMLの見た目を整形する
コマンドラインオプション (コマンドライン引数、ワンライナー)
-c : 文法チェックのみ行なう
-w : 警告モード
-s : 引数でスクリプトにスイッチを与える
ruby -s sample.rb -aaa -bbb とやると $aaa, $bbb が true になる
-d : デバッグモード ($DEBUG が true に設定される)
-e 'command' : command をスクリプトとして実行する
-a : オートスプリットモード
入力行を自動的に split して、結果を配列 $F に格納する
-F: : 区切りパターンを : にする
-i[extension] : 入力ファイルと同じファイルに書き込む
extension を指定すると、元ファイルのバックアップを作成する。
例) ruby -i.bak -np -e 'xxxxx' a.txt
-Idirectory : $LOAD_PATH の追加
-l : 行末処理を指定
-n : プログラムを 次のループの内側で実行する
while gets()
:
end
-p : nオプションと一緒に使うと、ループの最後に $_ を自動的に出力する
-rlibrary : 実行前にライブラリを読み込む
※ perl で perl -pe とするところを ruby -npe とする
-T[level] : 汚染度チェックの指定(?)
-K[kcode] : 文字コードを指定(-Ks, -Ke -Ku)
擬似変数
__FILE__ : 実行中のプログラムのファイル名
__LINE__ : 実行中の行番号
変数名とかの命名規則
変数名は "_" で区切るのが一般的らしい (ここがJavaと違う)
his_name
でもクラス名は単語の先頭を大文字にする
HisFamily
その他ユーティリティ
※使ってないので記述が不確か
ri コマンド
コマンドベースでAPIのリファレンスが見れる
gem_server
実行すると http://localhost:8808/ でAPIリファレンスが見れるようになる
rake appdoc コマンド
Javaでいう javadoc コマンド
→ コマンドにより、doc/app ディレクトリにドキュメントが生成される。
Excelを使う
よく使う(かもしれない定数)
xlTop = -4160
xlLeft = -4131
xlCenter = -4108
xlLeft = 7
xlEdgeTop = 8
xlEdgeBottom = 9
xlEdgeRight = 10
xlInsideVertical = 11
xlInsideHorizontal = 12
xlContinuous = 1
xlThin = 2
起動
require 'win32ole'
# Excel起動
excel = WIN32OLE.new('Excel.Application' )
excel.visible = true
# ファイル開く場合
book1 = excel.Workbooks.Open("filename.xls" )
# 新規作成の場合
book2 = excel.Workbooks.add()
sheet2 = book2.Worksheets("Sheet1" ) # ワークシート選択
# 閉じる
book1.Close()
VBAな話
書式設定(例)
# セルの書式設定
sheet2.Cells.VerticalAlignment = xlTop
sheet2.Cells.HorizontalAlignment = xlLeft
sheet2.Cells.WrapText = true
sheet2.Cells.Font.Size = 9
# 範囲を指定して書式設定
sheet2.Range("A1:D1" ).HorizontalAlignment = xlCenter
sheet2.Range("A1:D1" ).Interior.ColorIndex = 36
# 列を指定して書式設定
sheet2.cells("A:A" ).ColumnWidth = 20
# ウィンドウ枠の固定
sheet2.Range("A2" ).Select
excel.ActiveWindow.FreezePanes = true
# セルに書き込む
sheet2.cells(1, 3).Value = 'あー'
# 印刷設定
sheet2.PageSetup.Zoom = false
sheet2.PageSetup.FitToPagesWide = 1
sheet2.PageSetup.FitToPagesTall = false
sheet2.PageSetup.PrintTitleRows = "$1:$1"
ループ
# 全てのシートについて○○
book1.Worksheets.each {|sheet1|
row = 1
# データなくなるまでループ
while (sheet1.cells(row, 3).Value != nil) do
sheet1.cells(row, 4).Value = 'うー'
row = row + 1
end
}
罫線の設定例
range = sheet2.Range("A1:D" + rowout.to_s)
for side in [xlEdgeTop, xlEdgeBottom, xlEdgeRight, xlInsideVertical, xlInsideHorizontal] do
range.Borders(side).LineStyle = xlContinuous
range.Borders(side).Weight = xlThin
range.Borders(side).ColorIndex = 16
end
XMLのパース
準備
require 'rexml/document'
doc = REXML::Document.new(File.open("xxx.xml" ))
取り出したい要素が1つのとき
xpath = '/aaa/bbb[@id="b001" ]/ccc'
key = 'name'
val = doc.elements[xpath].attributes[key]
puts val
→ 「c001」
取り出したい要素が複数のとき
xpath = '/aaa/bbb[@id="b001" ]/ccc'
key = 'name'
REXML::XPath.match(doc, xpath).each {|e|
val = e.attributes[key]
puts val
}
→ 「c001 \n c002」
※ 次のようなXMLを取り出すとした
<aaa>
<bbb id="b001" >
<ccc name="c001" />
<ccc name="c002" />
</bbb>
<bbb id="b002" >
<ccc name="c003" />
<ccc name="c004" />
</bbb>
</aaa>
メール送信
#!/usr/bin/ruby -w
require 'net/http'
require 'net/smtp'
require 'kconv'
# メール送る
mail_from = 'xxxx@xxx.xx.com'
mail_to = 'xxxx@xxx.xx.com'
smtp_server = 'xxx.xxx.xxx.xx'
message = <<EOM
From: <#{mail_from}>
To: <#{mail_to}>
Subject: たいとる
ほんぶん
ほんぶん
EOM
message = message.tojis
Net::SMTP.start(smtp_server, 25) {|smtp|
smtp.send_mail message, mail_from, mail_to
}
puts "送りました"
YAMLについて
形式について注意
・行頭はTab不可
読み取るとき
require 'yaml'
conf = YAML.load_file(fname)
p conf["xxx" ]["mmm" ]
書き込むとき
PStoreの書き方と同じ。つまり transaction で囲む
require 'yaml/store'
db = YAML::Store.new("sample.yml" )
db.transaction do
db["hoge" ] = {1 => 100, "bar" => 101}
end
→ もともと登録されているデータは消されない
(つまり同じYAMLファイルを使って、db["hoge" ] はこのアプリで、
db["moge" ] は別のアプリで更新、とかやっても大丈夫そう)
※ PStoreの例
require 'pstore'
db = PStore.new("/tmp/foo" )
db.transaction do
p db.roots
ary = db["root" ] = [1,2,3,4]
ary[0] = [1,1.5]
end
db.transaction do
p db["root" ]
end
YAML形式の文字列を取り出したいとき
str1 = YAML.dump(obj1)
デバッグモード
ruby -r debug スクリプト名.rb
list x x行目±5行のソースを表示
where 現在位置の表示
quit
break x x行目にブレークポイントをしかける
break
delete ブレークポイントを全部削除
delete x x行目のブレークポイントを削除
watch 条件式 条件式を満たしたら一時停止させる「ウォッチポイント」をしかける
step 1行ずつ進める
cont ブレークポイントまで進める
finish ブロックの外へ?
var local ローカル変数の一覧
var global グローバル変数の一覧
p 変数名 変数の内容を表示
p メソッド名 メソッドの戻り値を表示
引数の処理 (OptionParser, optparse)
require 'optparse'
opt = OptionParser.new
opt.on('-a' ) {|v| v1 = v } # -a が指定された時に v は true
opt.on('-b filename' ) {|v| v2 = v } # v は指定した引数 (-b の直後の文字列)
opt.on('-c [message]' ) {|v| v3 = v } # 省略可能な引数
opt.on('-d, --del' ) {|v| v4 = v } # --xxx 形式も受け付ける
opt.on('--echo' ) {|v| v5 = v } # --xxx 形式も受け付ける
opt.on('--[no-]file' ) {|v| v6 = v } # --no-file と指定すると v は false
opt.on('--group[=gid]' ) {|v| v7 = v } # --xxx 形式も引数を指定可能
opt.on('-i' , 'あああ' ) {|v| v8 = v } # --help で出るメッセージを指定
begin
opt.parse!(ARGV) # ARGVを解釈
# ※ "!" をつけるとARGVから "-x" は取り除かれる)
rescue OptionParser::InvalidOption => e
puts e.message # 「invalid option: -t」のように出る
end
※ v1, v2, ... と書いてるところはハッシュにしよう
Logger
require 'logger'
# 起動
log = Logger.new(STDOUT) # 標準出力に出す場合
log = Logger.new(STDERR) # 標準エラー出力に
log = Logger.new('test.log' ) # ファイルに
log = Logger.new('test.log' , 3) # rotateする場合 (→ test.log.1〜3)
log = Logger.new('test.log' , 'daily' ) # 日次 : test.log.20070725
log = Logger.new('test.log' , 'weekly' ) # 週次 ※ 何曜日に新しいファイル?
log = Logger.new('test.log' , 'monthly' ) # 月次 ※ 何日?
# 設定
log.progname = 'xxxx' # プログラム名
log.level = Logger::WARN # どこまで出すか設定 (FATAL, ERROR, WARN, INFO, DEBUG)
# 書き込む
log.fatal('hello' )
log.error('hello' )
log.warn('hello' )
log.info('hello' )
log.debug('hello' )
# ブロック呼び出しも出来る
# → デバッグのときだけ行いたい処理とか入れられるということ?
log.debug {
# 何か処理
....
# 何か処理
...
"message"
}
※ syslog に出したい場合は Logger ではなく Syslogモジュールを使う
Syslog.alert(message)
※ Windowsのイベントログに出したい場合は win32-eventlog を使用する
Webオートメーション
(PerlでいうLWPみたいなの, UserAgentとかSimpleとか)
どんなライブラリがあるか
標準で使えるもの
URI : URIを扱うもの
open-uri : 簡単なリクエストを扱うときに使う (LWP::Simpleにあたる)
Net::HTTP : もう少し細かくリクエストを処理したいときに使う (LWP::UserAgent にあたる)
ruby gems で手に入るもの
scrAPI : HTMLの解析(スクレイピングというらしい)をするためのもの
http://d.hatena.ne.jp/secondlife/20060922/1158923779
Hpricot : HTMLの解析をするためのもの
http://tam.qmix.org/wiki/Hpricot.html
WWW::Mechanize : ブラウザで操作するはずのところを自動化するためのもの
※ Mechanize は Hpricot を使用している
※ Hpricot + WWW::Mechanize の組み合わせがオススメ!らしい
http://mono.kmc.gr.jp/~yhara/d/?date=20070205 #p01
open-uri
Simple
ruby -ropen-uri -e 'open("http://www.ruby-lang.org/ja/" ).read.display'
Net::HTTP
(1) Net::HTTP
require 'net/http'
Net::HTTP.version_1_2
Net::HTTP.start('www.ruby-lang.org' , 80) {|http|
response = http.get('/ja/' )
puts response.body
}
(2) Net::HTTP + URI
uri = URI('http://homepage2.nifty.com/takaaki024/' )
Net::HTTP.version_1_2
Net::HTTP.start(uri.host, uri.port) {|http|
response = http.get(uri.path)
puts response.body
}
フォームデータ送る場合
http.post('/xxx/' , "param1=xxxx" )
フォームデータ作る
query_string = query_hash.map{|key, value|
"#{URI.encode(key)}=#{URI.encode(value)}"
}.join("&" )
Basic認証したい
req = Net::HTTP::Get.new("/path/to/the/site/" )
req.basic_auth("ユーザ名" , "パスワード" )
↑
これを HTTP.start する前に呼ぶ
リダイレクトに対応:次のような再帰的なメソッドを使う
def fetch(uri_str, limit = 10)
return nil if limit == 0
response = Net::HTTP.get_response(URI.parse(uri_str))
case response
when Net::HTTPSuccess
response
when Net::HTTPRedirection
fetch(URI.join(uri_str, response['Location' ]).to_s, limit - 1)
else
response.error!
end
end
end
responseオブジェクトが成功したか失敗したかは、型で判定できる
HTTPResponse
HTTPSuccess
HTTPOK
HTTPRedirection
HTTPFound
HTTPNotModified ← これ使って更新チェックができる?
HTTPClientError
HTTPUnauthorized
HTTPForbidden
HTTPNotFound
HTTPRequestTimeOut
HTTPServerError
HTTPInternalServerError
※ HTTPNotModified は、If-Modified-Sinceヘッダを利用した時に
更新されていない場合に返るレスポンス
更新されたかどうかチェック
uri = URI('http://homepage2.nifty.com/takaaki024/' )
now = Time.new
old = Time.local(now.year, now.month - 1, now.day, 0, 0, 0)
Net::HTTP.start(uri.host, uri.port) {|http|
response = http.get(uri.path, {'If-modified-Since' => old.httpdate})
case response
when Net::HTTPSuccess
p '1ヶ月以内に更新されてる!'
when Net::HTTPNotModified
p 'ここ1ヶ月更新されてないよー'
else
p 'みつからないか転送されてる'
end
}
Responseオブジェクトについて
response.body : HTMLの内容を取得
response.each : ヘッダ(header)のキー/値の組み合わせを取れる
response.each {|k, v|
puts "#{k} => #{v}"
}
→ 出力は 例えばこんな感じ:
last-modified => Fri, 03 Aug 2007 16:03:41 GMT
content-type => text/html
date => Sat, 04 Aug 2007 00:55:20 GMT
server => Zeus/3.3
content-length => 2683
→ いっこいっこ取り出したい場合:
response["content-type" ]
URI
URIの文字列をパースする
uri = URI.parse('http://xxxx.x.xxxx.xxx/?xxx=3&mmm=4' )
uri = URI('http://xxxx.x.xxxx.xxx/?xxx=3&mmm=4' ) ※ どっちも同じ
uri = URI.join('http://xxxx.x.xxxx.xxx/' , '/?xxx=3&mmm=4' )
→ 次の要素に分解される
uri.scheme
uri.host
uri.port
uri.path
uri.query
uri.fragment
文字列からURIを抜き出す
uri_re = URI.regexp(['http' , 'https' , 'ftp' ])
# ※このメソッドは正規表現を書いてくれるだけ
str.scan(uri_re) do
p URI.parse($&)
end
※ extractというのもある
p URI.extract(str, ["http" , "ftp])
URIデコード
$KCODE = 'UTF8'
URI.decode('xxxx' )
URI.unescape('xxxx' ) ※どっちも同じ
URIの全体(コンストラクタで指定したのと同じ文字列)
URI.to_s
※ / や : もエンコードしたい場合は次のようにやるらしい
URI.escape(URLString, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]" ))
※ URI::PATTERN::UNRESERVED は、「-_.!~*'()a-zA-Z\\d」
※ % は含まれない
Hpricot
インストール
yum install ruby-devel # なければ ruby-devel をインストール
gem install hpricot
require
require 'rubygems'
require 'hpricot'
書き方
# 初期化
doc = Hpricot(str_html)
# 要素を検索
elements = doc.search("head/title" ).inner_text.to_s
#
elements.each {|elem|
メソッド
(1) CSSセレクタ形式でマッチしたElementのリストを返す
search("xxx" ) # 引数はシンボルまたは文字列
/"xxx" # これもメソッド名。 search, / のどっちを使っても良い
(2) CSSセレクタ形式でマッチした最初のElementを返す
search("xxx" ).first
at("xxx" )
CSSセレクタで指定する文字列の例
table : table タグの要素
div#xxx : div タグで id が xxx の要素
div.xxx : div タグで class が xxx の要素
table#list td : table タグで id が xxx の要素 の中の td タグ
a[@href^='xxx' ] : a タグで href が xxx の要素
a[text()*='new' ] : a タグで 囲まれた文字列 に xxx を含む要素
select[@name^='xxx' ] : select name='xxx'
※ option selected はどうやって選ぶのだろう
→ Mechanizeで
型名
doc = Hpricot(xx) で取れるのは Hpricot::Doc クラス
elements = doc.search(xx) で取れるのは Hpricot::Elements クラス(Arrayを継承)
elem = elements[0] で取れるのは Hpricot::Elem クラス
Elementsクラスの
Elemクラスのメソッド
attributes : 属性一覧
["xxx" ] : 属性 xxx の値を得る
name : タグ名
parent : 親要素
containers : 子要素(タグのみ)
children : 子要素(テキストやコメントも含む)
css_path : その要素のCSSパス
previous_sibling, next_sibling : 隣の要素(タグのみ)
previous_node, next_node : 隣の要素(テキストやコメントも含む)
each_child{|elem| ..} : 子要素について繰り返す
inner_html : 内部のHTML
inner_text : 内部のテキスト
to_html : そのタグも含めたHTML
to_original_html : そのタグも含めたHTML(なるべく元のHTMLと同じように出力する)
to_plain_text : 読みやすいテキストに整形する
例
# 本文のテキスト部分を取ってくる
p doc.to_plain_text.kconv
# 全部の div 要素の中身を表示
(doc/:div).each {|div|
p div.inner_html.kconv(Kconv::UTF8, Kconv::EUC)
puts "------------------------------"
}
※ 情報元
http://mono.kmc.gr.jp/~yhara/d/?date=20070205
WWW::Mechanize
インストール
gem install mechanize
require
require 'rubygems'
require 'mechanize'
初期化
agent = WWW::Mechanize.new
page1 = agent.get('http://cms.blog.livedoor.com/cms/' )
メソッドとか
Mechanize
title : ページのタイトル
header : レスポンスヘッダ
root : Hpricotオブジェクト
links -> Links ※違ったっけ
forms -> Forms
form('xxx' ) -> name='xxx' である最初のform
links.text('xxx' ) -> 文字列が 'xxx' であるリンク
submit(form1) -> form1 を Submit
submit(form1, button1) -> form1 の button1 を押す ※戻り値は次のページ
basic_auth(user, password)
open_timeout = 5 タイムアウト
read_timeout = 5 -> 単位は秒?
Page
header()
uri() : そのページのURI。uri.to_s を使ってログイン成功とか失敗とかの判別に
Link
text : <a>タグに囲われたテキスト
href : リンク先
node : hpricotオブジェクト
click : クリック
Form
name
method
action
fields -> Fields
buttons -> Buttons
checkboxes -> CheckBoxes
selectlist -> SelectList
submit ※戻り値は次のページ。見つからなかったときは空文字、のようだ
List (Fields, Buttons, CheckBoxesの親クラス。Arrayのサブクラス)
name('xxx' ) -> name が xxx な要素の配列を返す
※ あとは配列と同じ
Field
name
value
Button
name
value
CheckBox
checked : boolean
name
value
SelectList
name
value
selected_optons : "option selected" な配列
Option
select_list : 親というかselectタグ
selected : boolean
text
value
例1
require 'rubygems'
require 'mechanize'
# 接続
agent = WWW::Mechanize.new
page1 = agent.get('http://cms.blog.livedoor.com/cms/' )
# リンクの一覧
page1.links.each {|link|
puts "#{link.href} : #{link.text.toutf8}"
}
# フォームの一覧
page1.forms.each {|form|
puts form.name # => loginForm
puts form.method # => POST
puts form.action # => ./index
# フォーム内のフィールド一覧
form.fields.each {|field|
puts field.name # => livedoor_id
puts field.value.toutf8 # => ""
}
}
# フォーム 'loginForm' のフィールド 'livedoor_id' の値を 'takaaki' にする
puts page1.form('loginForm' )['livedoor_id' ] = 'takaaki'
# 「次の20件」と書いてある <A>タグの、リンク先
puts analyze_page.links.text("\n次の20件" .toeuc).href
例2 : select, option selected → なんか無理やりっぽいけど
edit_page = agent.get(blog_entry.link)
edit_page.form('ArticleForm' ).fields.each {|field|
if field.is_a?(WWW::Mechanize::SelectList)
if field.name == 'category_id1'
field.selected_options.each {|option|
puts option.text.toutf8
puts option.value.toutf8
}
end
end
}
Q. 「Broken Pipe」とかのエラーで止まってしまう
Errno::EPIPE
Broken pipe
/usr/lib/ruby/1.8/net/protocol.rb:175:in `write'
/usr/lib/ruby/1.8/net/protocol.rb:175:in `write0'
A. 次のように入れておくと良いらしい
trap("PIPE" , "IGNORE" )
※ $SIG{PIPE} = 'IGNORE' ; というのはPerlの書き方?
Mechanize日本語リファレンス
http://mono.kmc.gr.jp/~yhara/w/?Ruby-WWW-Mechanize
ERB, eRuby
※ ERb は昔の名前。ERB が新しい名前。
※ ERB は eRubyのRubyによる実装。Cによる実装はeruby
HTMLだけじゃなくてメールテンプレートみたいなものにも使えそう?
決まり事
<% ... %> : 式を実行するだけ
<%= ... %> : 式の結果を表示
<%- ... %> : 直前の(行頭からの)空白文字を削除
<% ... -%> : 直後の改行を出力しない
使い方
require 'erb'
str = 'hello'
erb = ERB.new("value = <%= str %>" )
puts erb.result(binding) # ← この binding ってなに
FTP
require 'net/ftp'
# まずローカルで移動しておく
Dir.chdir(dirname_l)
# 接続
ftp = Net::FTP.open(host, user, passwd)
# デフォルトは PASVモードはOFFなので
ftp.passive = true
# debug_mode を有効にしておくと転送するときのログが表示される
# ftp.debug_mode = true
# BINARYモードがデフォルトだけど明示的に書いておく
ftp.binary = true
# ディレクトリの移動と get
ftp.chdir(dirname_r)
ftp.getbinaryfile(filename_r, filename_l)
# 切断
ftp.close
メソッド
putする
ftp.put(file_l, file_r)
ftp.putbinaryfile(file_l, file_r)
ftp.puttextfile(file_l, file_r)
getする
ftp.get(file_r, file_l)
ftp.putbinaryfile(file_r, file_l)
ftp.puttextfile(file_r, file_l)
※ ファイル名のところはパス指定も可
('/home/takaaki/a.txt' とか 'test/uuu/b.txt' とか)
ディレクトリ作る
ftp.mkdir(dirname)
ディレクトリ内のファイルとディレクトリ一覧
ftp.nlst('test' ) # => ["test/a.txt" , "test/b.txt" , "test/dir1" ]
# ※ 順番はアルファベット順や
# ファイル/フォルダ毎にソートされてたりはしない
フォルダがあるかないか確認
begin
ftp.nlst('aaaa' )
puts "ある"
rescue Net::FTPTempError
puts "ない"
end
※
フォルダが無いときに(nlstメソッドで)発生するのは FTPTempError
ファイルが無いときに(get メソッドで)発生するのは FTPPermError
名前解決/DNS
require 'resolv'
begin
host = Resolv.getname(ip)
rescue
host = "-----" # 取れないときは例外吐くよ
end
ActiveRecord (ActionRecordは誤り)
Railsで使われている O/Rマッピングのライブラリ「ActiveRecord?」は
単体でも(Railsアプリの中じゃなくてもという意味)使えます。
DBの参照/更新をするためのコードが とても短く書けるので、
テストデータ投入とかバッチ処理を作るのに気軽に作れて良いかもしれません。
例1:参照するだけ
#!/usr/bin/ruby
require 'rubygems'
require 'active_record'
# DB接続
ActiveRecord::Base.establish_connection(
:adapter => "mysql" ,
:host => "localhost" ,
:username => "xxxxxx" ,
:password => "xxxxxxx" ,
:database => "xxxxxx" ,
:socket => "/var/lib/mysql/mysql.sock" ,
:encoding => "utf8"
)
# users テーブルにアクセスするためのクラスを宣言
class User < ActiveRecord::Base
end
# 全てのレコードについてループ
User.find(:all).each {|user|
# 各レコードのフィールド(*)にアクセス
# * この例では usesrテーブルの nameフィールド
p user.name
}
例2: insertする
# :
# users テーブル
class User < ActiveRecord::Base
end
# insertする
User.create(
:name => "takaaki" ,
:nickname => "jakoten" ,
:profile => "hello world!"
)
例3: 多対多の関連
# :
# products テーブル
class Product < ActiveRecord::Base
has_and_belongs_to_many :customers, :join_table => 'orders'
end
# customers テーブル
class Customer < ActiveRecord::Base
has_and_belongs_to_many :products, :join_table => 'orders'
end
# 全員が全部の商品を買ったことにする
Customer.find(:all).each {|user|
Product.find(:all).each {|product|
user.products << product
# user.save ← 要る?
}
}
※ ActiveRecordの詳しい使い方はRailsの方のメモ参照
※ Windows環境(cygwin)では「socket」には何を指定すれば良い? TODO:分かったら書く
Thread
スレッド起動
(1) Thread::new
t = Thread.new {
// 何か処理
}
※ 特に require とか要らない
(2) Thread::start
(3) Thread::fork
※ どれも書き方同じ
スレッドを一時停止 (スレッド内で呼ぶ)
Thread.stop
→ 外から t.run が呼ばれるまで待機
スレッドを再開させる (スレッド外で呼ぶ)
t.run
スレッドを終了させる (スレッド内で呼ぶ)
t.exit
Thread.exit
スレッドを終了させる (スレッド外で呼ぶ)
Thread::kill(t)
スレッドが終了されるまで待機する (スレッド外で呼ぶ)
t.join
ただスリープしたいとき ※ 単位は「秒」
sleep 5
スレッド一覧を取得する (配列として取れる)
Thread::list
現在のスレッド(カレントスレッド)を取得する
Thread::current
スレッド間通信
# キューを使う
q = Queue.new
# 送る側
q.push(line)
# 受ける側
while (line = q.pop)
p line
end
競合の回避
require 'thread' # つけなかったときはエラーが出たり出なかったりするのは何でだろう
@locker = Mutex.new
# 複数のスレッドから同時に実行されたくない部分を
# synchronizeブロックで囲う
@locker.synchronize do
xxxx
xxxx
end
サンプル
#!/usr/bin/ruby -w
susumu = false
puts "ここはスレッドの外(起動前)"
t = Thread.new do # スレッド起動
spc = " ---------------------------->"
puts "#{spc} スレッド起動" #
puts "#{spc} 3秒待つ"
sleep 3 # ただ停止したいとき
puts "#{spc} 3秒待った"
if (!susumu)
puts "#{spc} t.run が実行されるまで待つ"
Thread.stop # カレントスレッドの動作を停止
puts "#{spc} t.run が実行された模様" # (runで再開できる)
end
puts "#{spc} 更に2秒待つ"
sleep 2
puts "#{spc} 更に2秒待った"
puts "#{spc} スレッド終了"
end
# ← この時点で既にスレッド起動された状態
puts "ここはスレッドの外(起動後)"
puts "スレッドの外で5秒待つ"
sleep 5
puts "スレッドの外で5秒待った"
t.run # 止めていたスレッドの再開
puts "スレッドが終了するのを待つ"
t.join # 終了されるまで待つ
puts "スレッドが終了した模様"
# ↑ これ書かないと、そのまま進んでいって
# プログラム終了時点でスレッドも終了されるようだ)
puts "終了"
出力はこうなる
ここはスレッドの外(起動前)
----------------------------> スレッド起動
----------------------------> 3秒待つ
ここはスレッドの外(起動後)
スレッドの外で5秒待つ
----------------------------> 3秒待った
----------------------------> t.run が実行されるまで待つ
スレッドの外で5秒待った
----------------------------> t.run が実行された模様
----------------------------> 更に2秒待つ
スレッドが終了するのを待つ
----------------------------> 更に2秒待った
----------------------------> スレッド終了
スレッドが終了した模様
終了
jcode
Stringで日本語を扱えるようにするもの
※ jcode.pl みたいに日本語変換するためのものではない
使用例
#!/usr/bin/ruby -w
$KCODE = 'UTF8'
require 'jcode'
nihongo = 'にほんごとAlphabet'
nihongo.tr!('a-zA-Z' , 'a-zA-Z' )
p nihongo # => にほんごとAlphabe
nihongo.tr!('あ-ん' , 'ア-ン' )
p nihongo # => ニホンゴトAlphabet
再定義されたメソッド
chop
delete
squeeze
succ
tr
tr_s
追加のメソッド (trとかは再定義のメソッド)
each_char
jlength
jsize
squeeze
p "112233445566778899" .squeeze # => "123456789"
RSS
RSS Maker
例
#!/usr/bin/ruby -w
require 'rss'
rss = RSS::Maker.make('1.0' ) do |maker|
# RSSファイル1つに対して1回だけ設定する項目
maker.channel.about = 'http://homepage2.nifty.com/takaaki024/tips/index.rdf'
maker.channel.title = 'たいとる'
maker.channel.description = 'サイトの説明をここに書く(ここにはHTMLタグは入れないらしい)'
maker.channel.link = 'http://homepage2.nifty.com/takaaki024/tips/main.html'
maker.channel.date = Time.now
maker.image.title = 'takaakiですこんにちは'
maker.image.url = 'http://blog.livedoor.jp/takaaki_bb/profile.jpg'
# 更新日の新しい順に並べ替えるオプション
maker.items.do_sort = true
# 記事1個に対して1回設定する項目(10件公開したければ10個書く)
maker.items.new_item do |item|
item.link = 'http://homepage2.nifty.com/takaaki024/tips/computers/ms/ime.html'
item.title = 'IME(辞書)メモ (記事のtitleをここに書く)'
item.description = '記事のdescriptionをここに書く(本文のサマリ)'
item.dc_subject = '記事のsubjectをここに書く(絞って表示するための「カテゴリ」)'
item.content_encoded = 'ここにCDATAで囲んだ本文のHTML(content_encoded)を書く(でもエスケープされてしまうのでうまくいかない・・)'
item.date = Time.parse('2007/08/4 0:00' )
end
# * 記事2つ目
maker.items.new_item do |item|
item.link = 'http://homepage2.nifty.com/takaaki024/tips/computers/ms/excel.html'
item.title = 'Excelメモ (記事のtitleをここに書く)'
item.description = '記事のdescriptionをここに書く'
item.dc_subject = '記事のsubjectをここに書く'
item.content_encoded = 'ここにCDATAで囲んだ本文のHTML(content_encoded)を書く(でもエスケープされてしまうのでうまくいかない・・)'
item.date = Time.parse('2007/08/2 9:14' )
end
end
# 書き出す
puts rss.to_s
その他Q&A形式
Q.
シンボル(次のようなハッシュのキーの書き方)をどうして使うのか分からない
{ :age => 18, :height => 180 }
次のように書く場合との違いは何なんだろう
{ 'age' => 18, 'height' => 180 }
A.
rubyでのシンボルとは、いわゆるCでいう列挙型(enum)
内部処理では整数として扱い「処理を高速」に
ソース上は文字列として扱い「読みやすく」するための構文
ということのようだ。
※ http://q.hatena.ne.jp/1157468923
→ ということはハッシュのキーに関しては全てシンボルで書くと良さそうだ
Q.
MD5とかSHA1とか
A.
require 'digest/sha1'
puts Digest::SHA1.hexdigest("takaaki" )
require 'digest/md5'
puts Digest::MD5.hexdigest("takaaki" )
※ MD5使ってファイルの比較をする関数を作ってみる
# ファイル比較用関数
def same_files(filename1, filename2)
file_md5 = {}
[filename1, filename2].each {|filename|
open(filename) {|file|
file_md5[filename] = Digest::MD5.hexdigest(file.read)
}
}
return (file_md5[filename1] == file_md5[filename2])
end
Q. Windowsのイベントログが読みたい
A. win32-eventlog
http://raa.ruby-lang.org/project/win32-eventlog/
Q. JSONをパースしたい
A.
JSON形式で書かれたものは YAML形式で読めるらしい
→ YAML.load(str) とか YAML.load_file(fname) を使う
Q. 特定の文字列をURIでコードしたい
A.
ruby -r "uri" -e "puts URI.decode('%E3%82%AD%E3%83%BC%E3%83%AF%E3%83%BC%E3%83%89' )"
Q. シェルでいう `date +%Y%m%d
A.
require 'date'
puts Time.now.strftime('%Y%m%d_%H%M%S' )
Q. 改行で区切って配列にする
A.
file = open(... )
lines = file.read.gsub(/\r/m, '' ).split(/\n+/m)
Q. 「do ... end」 と 「 { ... } 」は、どういう場合に置き換え可能?
A. 結合強度が違うだけらしい
do ... endは、メソッド形式(f(a))にもコマンド形式(f a)にも結合するが、
{ ... }は、メソッド形式か、引数を記述してない形式にのみ結合する
→ あとは好み??
※ でもwhileは置き換え出来ないような
while (xx = mm) do
xxxx
end
Q. 標準入力を受け取る
A.
while (line = gets) do
puts "Hello #{line}"
end
Q. 「 ||= 」 これなに
A. nil だったときに代入を行なう演算子。
初期値を代入したいときにつかうといいらしい。
確認してみる
hash1 = {}
hash1["ohayou" ] = 3
hash1["konbanha" ] = 10 # キー "konnnichiha" を定義していない
["ohayou" , "konnnichiha" , "konbanha" ].each {|aisatsu|
hash1[aisatsu] ||= 0
hash1[aisatsu] += 1
}
hash1.each {|k, v|
puts "#{k} => #{v}"
}
出てきた件数をカウントするときとかに便利?
before:
count = {}
if (count[name].nil?)
count[name] = 1
else
count[name] += 1
end
after
count = {}
count[name] ||= 0
count[name] += 1
→ 6行が 3行に!
と思ったけどこういう場合は初期値を0にするのが正解
count = Hash.new(0)
count[name] += 1
※ 2行
Q. 自分のホスト名取得
A.
require 'socket'
Socket.gethostname
Q. 型チェック
A.
# is_a を使う?
if he.is_a(Man)
p "hello."
end
# case .. when を使う?
case he
when Man
p "hello."
when Alien
p "behind you!"
else
p "unknown type #{he.class}"
end
※ 型名を表示させたいときは
p obj1.class
Q. gem install hpricot したら、次のエラーが出た
Building native extensions. This could take a while...
ERROR: While executing gem ... (Gem::Installer::ExtensionBuildError)
ERROR: Failed to build gem native extension.
ruby extconf.rb install hpricot
can't find header files for ruby.
Gem files will remain installed in /usr/lib/ruby/gems/1.8/gems/hpricot-0.6 for inspection.
Results logged to /usr/lib/ruby/gems/1.8/gems/hpricot-0.6/ext/hpricot_scan/gem_make.out
A. ruby-devel が必要
yum install ruby-devel
Q. 乱数を生成したい (random, ランダム)
A.
val = rand # 0 〜 1未満
val = rand(100) # 0 〜 100未満
Q. printf, sprintf で全角文字がズレる
A. 無理やり解決した例
→ jcode ライブラリで printf も再定義されてたらなあ
#!/usr/bin/ruby -w
require 'jcode'
$KCODE = 'UTF8'
name_table = [
['東京都狛江市' , '28度' , '晴れ' ],
['Moscow' , '3度' , '雪' ],
['NY' , '24度' , '雨' ],
]
# マルチバイト文字が何文字含まれるかをカウントする
def count_mb(str)
count = 0
str.split(//).each {|c|
count += 1 if (c.jsize != c.size)
}
count
end
# 普通に使うとずれる
name_table.each {|row|
printf("%-20s | %6s | %-8s" , row[0], row[1], row[2])
puts
}
# ずれないように調整する
name_table.each {|row|
printf("%-#{20 + count_mb(row[0])}s | %#{6 + count_mb(row[1])}s | %-#{8 + count_mb(row[2])}s" , row[0], row[1], row[2])
puts
}
Q. 標準出力がバッファリングされてしまう。flush したい。
A. STDOUT.flush を使う
print "input here --> "
STDOUT.flush
msg = gets
Q. VisualBasic(VB)とかでいう trim() のような前後の空白を取り除くメソッドは無いのか
A.
分からないからこう書いてる
str.gsub!(/^\s+|\s+$/, '' )
→ と思ったら
str.strip
でいいみたい。
Q. rubygems をCygwinにインストールしたい
A.
# 落としてきて解凍
wget http://rubyforge.org/frs/download.php/20989/rubygems-0.9.4.tgz
tar zxvf rubygems-0.9.4.tgz
cd rubygems-0.9.4
# インストール
ruby setup.rb
# 確認
gem --version
※ Fedora Core の場合は yum install rubygems でOK
Q. ActiveRecord + Windows版 MySQL が CygwinのRubyから実行できない。
※ 設定項目「socket」が・・・
(未整理)
A. 解決策不明。
Windows版Rubyからは大丈夫なので、実行したいファイルが xxxx.rb だとしたら
/cygdrive/c/ruby/bin/ruby xxxx.rb と実行すればとりあえずは実行できたり。
でもこの方法だとソケット通信でサーバからのデータが受信できてないように
見えるのはどうしてだろう。
Q. という訳なので 実行環境を判定して、Cygwinだったらxxxの処理、、という感じに処理を分けたい。
A. 定数 RUBY_PLATFORM を使うといい
i386-mswin32
i386-linux
i386-cygwin
といった値が返ってくる。
Q. クラスの情報を調べたい (リフレクション, reflection)
A.
とりあえず色々
pp obj1
クラス名
obj1.class
メソッドの一覧
obj1.private_methods
obj1.protected_methods
obj1.public_methods
※ 第1引数に false を入れると継承したもの以外が一覧される