tips  >> 02.プログラミングなど  >> Ruby  >> Rubyめも.txt
[フレームで表示]

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] で取り出す   ※ 1番目のコマンドライン引数の添え字は 0 (ここはシェルスクリプトとかCと違う)


比較

    オブジェクトが同じかどうか判別
        equal? : オブジェクトIDが同じかどうか判別
        
        ※ objA.equal?(objB) のように書く
           objA.id == objB.id と同じ結果
        
    
    値が同じかどうか判別
        ==   : 通常の比較  ※文字列も == でOK
        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  # 複数指定可 (if文で orでつないだのと同じ)
            文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」のように * をつけておくと、配列が渡せる


呼び出し

    オブジェクト名.メソッド名(引数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                               # }
        
        ※「 => 変数」のようにしなくても $! で取り出せる
        ※ else句をつけておくと「例外が発生しなかった場合のみ」処理する
        
    例外を投げるときの書き方(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
        
        ※ ヒアドキュメント使わなくても文字列の途中で改行できるのですね
            str1 = "
                aaaa
                safdfq
                xx
            "

    式展開
        "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           : 前後の空白を取り除く
    
    ※ 例えば 97 → 'a' と表示したいときはどうする?
    

文字コード変換
    $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(サイズ, 初期値)

    ※変数をまとめて代入
        (name, return_type, value) = ['''''']

サイズ (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 を使う
    
    例1: iniファイルな形式の文字列からキーと値の組を取り出す
        line.toutf8.scan(/(.*)?= +(.*)/) {|key, val| 
            puts "#{key.strip} => #{val.strip}" 
        }
    
    例2: 1行のうちに複数回マッチしてもOK。関数の引数をパースするときとか
        "index1:int, index2:int".scan(/(\w+):(\w+)/) {|name, type|
            puts "  #{type} #{name}"
        }
        # => int index1
        #    int index2
  

※ただ文字列が含まれるか知りたい場合は
    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行を変数に入れる
            line.chomp!
            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

    ※ファイルテスト、ファイルがあるかないか、あるなし、有無、exists、存在チェック、-e、-d、-f


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 コマンド (RDocドキュメント)
        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 rubygems       # なければ rubygems をインストール
    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:分かったら書く
    Cygwinでは host の項目が localhost だと失敗することがあるんだとか。
    その場合は 127.0.0.1 を使用する。



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
    
    ※ 明示的に書くなら line = STDIN.gets



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. hpricot使うと次の警告が出る。

    /usr/lib/ruby/gems/1.8/gems/hpricot-0.6/lib/hpricot/builder.rb:11: warning: `&' interpreted as argument prefix
    
    しかしこれだけのために「#!/usr/bin/ruby -w」の「-w」を削りたくない。
    上の行だけ握りつぶすとか出来ないだろうか。

A.
    TODO:考える





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 を入れると継承したもの以外が一覧される


Q. 1文字目だけ大文字にしたい

A.
    "abcDef".capitalize    # => Abcdef
    

※ 2文字目以降を小文字にするのではなくそのまま残したい場合
    def capitalize2(str)
        after =  str[0..0].upcase
        if (str.length > 1) then
            after << str[1..(str.length - 1)]
        end
        return after
    end
    
    capitalize2("abcDef")  # => AbcDef


Q. ASCIIコードの数字から文字列に変換したい

A.
    puts 97.chr   # => a
    puts 98.chr   # => b


Q. 変数名にマッチする正規表現
A. \w  == [0-9a-zA-Z_]


Q. マーシャリングって?
A. オブジェクトをバイトストリームに変換してアプリケーションの外部に保存すること。
    Railsではセッションデータの管理に使っているんだとか
    TODO:コード例




rcairo

インストール
    (0) 前提
        cairo と ruby と rubygems がインストール済みであること

    (1) gem (ソースを落としてくれるだけらしい)
        gem install cairo
    
    (2) コンパイルとインストール
        cd /usr/lib/ruby/gems/1.8/gems/cairo-1.5.0
        
        ※ rake?
        
        ruby extconf.rb
        make
        make install
    
    (3) そうすると次のサイトで書いてるようなサンプルが動くようになる
        ※ /usr/lib/ruby/gems/1.8/gems/cairo-1.5.0/samples にもサンプルコードがある
        
        http://jp.rubyist.net/magazine/?0019-cairo


require
    require 'cairo'

初期化
    surface = Cairo::ImageSurface.new(Cairo::FORMAT_ARGB32, width, height)
    context = Cairo::Context.new(surface)

要素1つ描く基本的な流れ
    context.set_source_rgb(1, 0, 0)  # 色の設定(それぞれ0.0-1.0)
      # または context.set_source_rgb(Cairo::Color::RED) 
    context.xxxxx(xx, xx, xx, xx)    # rectangle とか arc とか
      # 角丸四角形はrcairoにはあるけどcairoにはない
    context.fill

描画
    surface.write_to_png("aaa.png")  # context.xxx で描いたものを描画

要素の種類
    context.rectangle(x, y, width, height)
    context.arc(x, y, radius, 0, length)     TODO:引数はあってるか後で確認



Gtk2

インストール
    yum install ruby-gtk*
    
    ※ 以下のモジュールが入る
        ruby-gtk2-devel
        ruby-gtkglext
        ruby-gtkhtml2
        ruby-gtkmozembed
        ruby-gtksourceview

Hello World
    http://ruby-gnome2.sourceforge.jp/ja/hiki.cgi?gtk2-tut-helloworld