第1回RHG読書会を開催しました

4月 8th, 2009 admin

RHG(Ruby Hacking Guide)と聞くと、どうしても「銀河ヒッチハイク・ガイド」を思い出してしまう私ですが、5日(日)にCSNagoyaとしてRHG読書会を開催しました。

Rubyのソースコードを読むという、CSNagoyaなのにカジュアルでもなんでもない内容ということもあって、また参加者1人の自習時代に戻る覚悟もしていましたが結局10人以上が集まる会になりました。

個人的な目標

僕は大きなコードを読んだことがないというのと、Rubyが好きなのだけど、いまいちRuby会議などで堂々とできない自分にサヨナラするのが目的なので
・ソースコードの読み方の作法とコツを習得
・Rubyistを名乗る
というのを目標にして参加しました。

まとめ

・Rubyのオブジェクトは構造体で、扱うときはVALUE型(unsigned long)のポインタ経由で扱う(unsigned longをポインタにキャストする)
・すべてのオブジェクトがstruct RBasic型のメンバbasicをもっていて、こいつが自分の構造体の型や各種フラグ、そしてクラスオブジェクトが格納されている
・VALUEがvoid*でないのは、小さな整数、シンボル、true、false、nil、QundefのときはVALUEに直接値を入れるため
・上記のような値と、オブジェクトのポインタとの値が重複しないように、整数は奇数の値、オブジェクトは4の倍数、シンボルは奇数ではなく4の倍数でもない数字にする、などという工夫がされている
・インスタンス変数を始めからもっているオブジェクトとそうでないのがある。これはメモリの無駄を省くため(RStringにインスタンス変数をセットして検証してみたい)
・struct RArrayはリストでは無い、以前にRubyでリストを自前で実装したら、Array.pushとかより高速に動作するようになって驚いたけど、そういうことらしい。

感想

C言語で書かれたオープンソースのコードを読むのは恥ずかしながら初めてだったので、まずソース(最初はruby.h)を開いただけでわくわくしました。難しいかと思われた内容も、本の解説がとても丁寧なおかげで今のところなんとかついていけているかなという感じです。
そして、いままでRailsでなんとなく使っていたシンボルの意味がよくわかったり、以前作ったリストが高速だった意味などが、それこそソースレベルで理解できてすごく気持ちがよかったです。

Rubyの歴史というのは10年程度で、それが長いのか短いのかわかりませんが、それでも10年以上もMatzを始めとする日本のスーパーハカーの人達が磨きに磨いたコードを読むというのは意外にもエキサイティングであり、贅沢なことなんだということを体感できました。こんなものが簡単に入手できてしまうオープンソースの文化に感謝をしつつ、次回の以降の勉強会にも取り組んでいきたいと思います。

動的計画法(1) ナップサック問題

1月 22nd, 2009 admin

あと2問で終わりとなった「アルゴリズムとデータ構造シリーズ」です。今回は第11章「動的計画法」のナップサック問題です。

動的計画法

深さ優先探索や幅優先探索などの分割統治法(トップダウン的問題解決法)では指数的に計算量が増えてしまうような問題というのがあります。(本書では例として「43ペンスのお釣りを出すときに考えられる小銭の組合せ」という問題があげられていました)そのような問題を解決するひとつの方法としてあるのが動的計画法というもので、

全体をいくつかの小問題に分割して、先に一括して小問題の解をすべて得て、最終的にそれらを組み合わせて問題の解決に役立てる。

だそうです。いつもは自分の言葉で説明するのですが、このアルゴリズムは説明しにくい。理解してないということかもしれません・・・

コード

ナップサック問題というのを解く問題です

RUBY:
  1. class Product
  2.   attr_accessor :size, :value
  3.  
  4.   def initialize(size,value)
  5.     @size = size
  6.     @value = value
  7.   end
  8. end
  9.  
  10. products = [
  11.             Product.new(2,2),
  12.             Product.new(3,4),
  13.             Product.new(5,7),
  14.             Product.new(6,10),
  15.             Product.new(9,14)
  16.            ]
  17.  
  18. NAP_SIZE = 16
  19. NAP_SIZE_RANGE = 1..NAP_SIZE
  20. nap_value = Array.new.fill(0,0..NAP_SIZE)
  21.  
  22. printf("nap size            ");
  23. NAP_SIZE_RANGE.each do |j|
  24.   printf(" %2d",j)
  25. end
  26. puts "\n"
  27.  
  28. 0..products.size.times do |i|
  29.   NAP_SIZE_RANGE.each do |j|
  30.  
  31.     if j - products[i].size>= 0
  32.       new_value = (nap_value[j - products[i].size] || 0) + products[i].value
  33.     else
  34.       new_value = 0
  35.     end
  36.  
  37.     nap_value[j] = new_value if new_value> nap_value[j]
  38.   end
  39.  
  40.   printf("use product until #%d ", i+1);
  41.   NAP_SIZE_RANGE.each do |j|
  42.     printf("%2d ",nap_value[j])
  43.   end
  44.   puts "\n"
  45. end

まとめ

これを使うべきときに思い出せるかどうか不安。

※このシリーズは私がRubyでアルゴリズムとデータ構造を基礎から学ぶ記録です。全体の目次はこちらへ。

文字列探索(1)

10月 25th, 2008 admin

久しぶりに「アルゴリズムとデータ構造シリーズ」です。今回は第9章「浮動小数点数と数値計算」です。

文字列探索

このアルゴリズムについては説明は必要ないでしょうね。文字列の探索はコンピュータがビジネスに欠かせなくなってしまった今日では非常に重要になっています。
第2章のあたりでサーチについては学びましたが、効率の良いバイナリサーチは文字列探索の場合はつかえません。なぜならバイナリサーチはあらかじめサーチ対象をソートしておく必要があったからです。
では、どのような方法があるのでしょうか。この章は3つぐらいコードが必要で正直面倒ですが順番にやっていきたいと思います。

まず今回はもっとも単純ですが、単純がゆえにバグもおこりにくいということから最も使われているアルゴリズムからです。ようするに最初から順番に調べていくという方法です。
[ruby]
def simple_search(text,pattern)
text.length.times do |n|
print " text:" + text + "\npattern:"
print " " * n
print pattern

flag = true
pattern.length.times do |i|
flag = false if (pattern[i] != text[n + i]) Read the rest of this entry »

Rails+Capistrano+Apache2.2+Mongrel_clusterのメモ

10月 17th, 2008 admin

まるまる2日間かかった。いろいろはまりすぎ。

前提

debian etch
mod_rails(passenger)はメモリを食いすぎて使えなかった
subversionを使っている
デプロイは自動化したい

OpenVZのVEの設定

まず最初に仮想マシンの容量が足りなくてRailsが起動しない。

vzctl set 2000 --privvmpages 2G --save #メモリ上限を2Gにする
vzctl set 2000 --diskspace 10G --save

インストール

サーバになにも入っていないのでどんどん入れる

aptitude install apache

aptitude install ruby
aptitude install rubygems

aptitude install mysql-server
aptitude install libmysqlclient-dev
aptitude install subvesion

gem install rails
gem install mysql
gem install gettext
gem install mongrel
gem install mongrel_cluster
gem install capistrano

#今回のアプリ用のもの
gem install ferret
gem install image_science
gem install amazon-ecs

$ unzip FreeImage3100.zip
$ cd FreeImage
$ make

Apache2.2の設定

このへんを見ながらやらせてもらった。はまったのはmod_proxyの設定で
/etc/apache2/mods-enabled/proxy.confのProxyディレクティブのDeny from allに気がつかなかったこと
conf.d以下以外に.confがあるなんて知りませんでした。

a2enmod rewrite
a2enmod proxy_balancer

でproxy_balancerとproxyを有効にする。

バーチャルホストの設定にこんなのを加える

HTML:
  1. <proxy *>
  2.                 Order deny,allow
  3.                 Deny from all
  4.                 Allow from all
  5.         </proxy>
  6.  
  7.         ProxyRequests Off
  8.         ProxyPass / balancer://appname/ timeout=2
  9.         ProxyPassReverse / balancer://appname/
  10.  
  11.         <proxy balancer://appname>
  12.                 BalancerMember http://127.0.0.1:3000 loadfactor=10
  13.                 BalancerMember http://127.0.0.1:3001 loadfactor=10
  14.                 BalancerMember http://127.0.0.1:3002 loadfactor=10
  15.                 BalancerMember http://127.0.0.1:3003 loadfactor=10
  16.         </proxy>

/etc/init.d/apache2 force-reload

でリロード

参考サイト

burn baby burn!!!(Archives)
【Rails】mongrel cluster&apacheのリバースプロキシで複数のアプリ共存

mongrel clusterの設定

これはあまりはまらなかった。

mongrel_rails cluster::configure

とやると雛形がconfig/mongrel_cluster.ymlでできるので
適当に編集して、こんなのができた

log_file: /home/appname/log/mongrel.log
port: "3000"
environment: production
address: 0.0.0.0
pid_file: tmp/pids/mongrel.pid
servers: 4

あとは

mongrel_rails cluster::start

で動くはず。

参考サイト

83s : config/mongrel_cluster.ymlの書き方、とか
Rubyist - バリケンのRuby日記 - mongrel_cluster

spinファイルの作成

capistranoを使うとファイルの展開後にscript/spinというのを実行してくれる
今回は使わないのでファイルだけ作っておく

$ vi script/spin

↓中身

#!/bin/sh

capstrano

アプリケーションのルートディレクトリで

capify .

でCapistranoのレシピといわれるファイルがconfig/deploy.rbに作られる
それをいい感じに編集

RUBY:
  1. require 'mongrel_cluster/recipes'
  2. set :application, "appname"
  3. set :repository,  "http://path to subversion"
  4.  
  5. set :user, "ssh username"
  6. set :use_sudo, false
  7. set :scm_username, "svnuser"
  8. set :scm_password, "svnpass"
  9.  
  10.  
  11.  
  12. # If you aren't deploying to /u/apps/#{application} on the target
  13. # servers (which is the default), you can specify the actual location
  14. # via the :deploy_to variable:
  15. set :deploy_to, "/var/www/#{application}"
  16.  
  17. set :mongrel_conf, "#{current_path}/config/mongrel_cluster.yml"
  18. set :mongrel_clean, true
  19. # If you aren't using Subversion to manage your source code, specify
  20. # your SCM below:
  21.  
  22. role :app, "192.168.1.x"
  23. role :web, "192.168.1.y"
  24. role :db,  "192.168.1.x", :primary => true
  25.  
  26. task :after_update_code, :roles => :app do
  27.   run "chmod u+x #{release_path}/script/spin"
  28. end

cap deploy:setup

でリモートに準備がされる。

cap deploy:cold

でsubversionからファイルが展開されてmongrelが再起動される

参考サイト

つくるぶガイドブログ: capistrano で本番環境にデプロイ
FFTT : Capistrano(2006年4月の情報)
第7回 Rails アプリケーションの起動・停止・再起動 - Capistrano 入門 - Ruby on Rails with OIAX

まとめ

・こんなに苦労する必要があるRailsはなにかおかしい
・Capistranoの資料がもうちょっとあればなぁと思った。
・passenger(mod_rails)は普通に動いたのでメモリがあれば全然使えそう

coreserverをリモートから自動バックアップするメモ

10月 10th, 2008 admin

coreserverというレンタルサーバのデータのバックアップをとるようにしたメモ。

シェルが使えるのでファイルとかはrsyncでとれるのだけど、DBのファイルはみれないのでmysqldump(mysqlhotcopyは使えない。)などを使ってユーザのディレクトリにデータをもってくる必要がある。シェルスクリプトをcoreserverにおいてcronでバックアップしてもいいのだけど、バックアップ関連のスクリプト類は手元で一括管理したいのでRuby経由でSSHしてmysqldumpをするようにした。

シェルが使えて年間5000円のcoreserverはすごいとおもう。

(1)DBのデータを全部ダンプ

RUBY:
  1. require 'rubygems'
  2. require 'net/ssh'
  3.  
  4. host = "ホスト名"
  5. user = "ユーザ名"
  6. key = "秘密鍵の場所"
  7. mysqldump = "/usr/local/mysql/bin/mysqldump"
  8. path = "/virtual/username/"
  9. databases = [
  10.   {:db_name => "aaaa", :id => "username", :password => "pass"},
  11.   {:db_name => "bbbb", :id => "username2", :password => "pass2"},
  12. ]
  13.  
  14.  
  15. Net::SSH.start(host, user, :keys => [key], :passphrase => "") do |ssh|
  16.   databases.each do |db|
  17.     command = sprintf('%s -u %s --password=\'%s\' %s> %s/%s.dump ',mysqldump, db[:id], db[:password], db[:db_name], path, db[:db_name])
  18.     ssh.exec!(command)
  19.   end
  20. end

(2)rsyncでファイルをローカル(バックアップマシン)に移動

CODE:
  1. rsync -av username@example.com:remote_directory local_directory

まとめ

・公開鍵認証を使っているのでSSHのパスワードは聞かれない。(作業メモ
・mysqldumpを絶対パスで指定しないと駄目らしい。

さらにローカルにrsyncされたデータをpdumpfsで毎日バックアップして、さらにpdumpsf-cleanでローテートしている。なんだか面倒なので、みんなどうやってやってるのか教えてほしいなぁ

参考サイト

Pure Ruby な SSH クライアントライブラリ「Net::SSH」 - WebOS Goodies