2010-08-11 :-)
_ 朝ッ
1030 起床
_ [コードリーディング][Ruby][QuickML]QuickML を読む - config
QuickML の設定ファイルを管理するクラス。
/usr/local/sbin/quickml で QuickML::Config::load(config_file) としていたので、initialize() が最初に呼ばれるのではなく load が呼ばれる。
def self.load (filename) self.new(eval(File.safe_open(filename).read)) end
load() の中で new() している。どうして load() 経由で呼んでいるのかは分からない。誰か教えて。def self.ナントカ とするとクラスメソッドとして定義することになる。どうしてクラスメソッドにしておくのかというと、インスタンスを生成する手間を省きたいからじゃないでしょうか。たぶん
new() に渡している引数を見ていく。以下のような順番で処理される。
- File.safe_open(filename)
- それを read()
- それを eval()
- それを new() に渡す
File.safe_open() は lib/quickml/utils.rb の中で定義している。
class File def self.safe_open (filename, mode = "r") begin f = File.open(filename, mode) if block_given? yield(f) f.close else return f end rescue => e STDERR.printf "%s: %s\n", $0, e.message exit(1) end end end
block_given? とはなんぞや
block_given?
メソッドにブロックが与えられている時には真、そうでない時に偽を返します。
つまり、以下のように呼ぶと block_given? は真
File.safe_open(hogehoge) {|f| 何かの処理 }
以下のように呼ぶと block_given? は偽となる。
f = File.safe_open(hogehoge)
self.load では File.safe_open(filename) と、ブロックを渡していないので File.safe_open() では File オブジェクトが返る。
次に、その File オブジェクトの read() を呼ぶ。quickmlrc の全て読み込んでいる。
File.safe_open(filename).read
quickmlrc の内容はこう。Ruby のハッシュである。ここではまだハッシュオブジェクトではなく、たんなる文字列になっている。
# -*- mode: ruby -*- Config = { :user => "quickml", :group => "quickml", :port => 10025, :bind_address => "0.0.0.0", :smtp_host => '127.0.0.1', :smtp_port => 25, :domain => 'qml.area51.gr.jp', :postmaster => "rin@maaya.jp", :info_url => "http://QuickML.com/", :data_dir => '/usr/local/var/quickml', :pid_file => '/var/run/quickml.pid', :log_file => '/var/log/quickml.log', :verbose_mode => true, :max_members => 100, :max_mail_length => 100 * 1024, :ml_life_time => 86400 * 31, :ml_alert_time => 86400 * 30, :auto_unsubscribe_count => 5, :sweep_interval => 3600, :max_threads => 10, :timeout => 120, :use_qmail_verp => false, :confirm_ml_creation => false, # for confirming ML creation. (experimental) # :message_catalog => nil # for English messages :message_catalog => '/usr/local/share/messages.ja', }
次に、ここで読み込んだ Config を eval() する。
eval(File.safe_open(filename).read))
eval() してようやく Config はハッシュオブジェクトとして生成されたことになる。
その Config を self.new() に渡す。
new() を呼ぶと initialize() が呼ばれるので initialize() を見てみる。長いので省略
def initialize (config = {}) @data_dir = config[:data_dir] @smtp_host = config[:smtp_host] @domain = config[:domain] raise ArgumentError if @data_dir.nil? raise ArgumentError if @smtp_host.nil? raise ArgumentError if @domain.nil? @pid_file = (config[:pid_file] or "/var/run/quickml.pid") @max_members = (config[:max_members] or 100) @max_mail_length = (config[:max_mail_length] or 100 * 1024) # 100KB :
見たとおりに config にあるキーから値を取得などしている。
(config[:pid_file] or "/var/run/quickml.pid") という処理は config[:pid_file] が nil ならば "/var/run/quickml.pid" を設定するということを意味している。つまり quickmlrc に pid_file が定義されていなければ "/var/run/quickml.pid" を使う。いわゆるデフォルト値になる。
initialize() の最後に注目。
instance_variables.each {|name| self.class.class_eval { attr_reader name.delete('@') } }
instance_variables とはなんぞや
instance_variables
オブジェクトのインスタンス変数名を文字列の配列として返します。
obj = Object.new obj.instance_eval { @foo, @bar = nil } p obj.instance_variables # => ["@foo", "@bar"]
これで
["@data_dir", "@smtp_host", "@domain" ...]
といった配列が返る。それを each する。
self.class.class_eval { attr_reader name.delete('@') } を 1 つずつ見ていく。
self.class はつまり自分自身のクラス。
class_eval とはなんぞや。これは結局 module_eval である。
module_eval とはなんぞや。
ブロックが与えられた場合にはそのブロックをモジュールのコンテキストで評価してその結果を返します。ブロックの引数 mod には self が渡されます。
モジュールのコンテキストで評価するとは、実行中そのモジュールが self になるということです。つまり、そのモジュールの定義文の中にあるかのように実行されます。
ええと、つまり @data_dir などに attr_reader 属性を付加していることになるらしい。へー
_ また処女か
「処女」かどうかと「乙女チック」「乙女心」「清純」などというものは別の問題なので、そこをゴチャゴチャすると話にならない。
前者はたんに肉体の問題であり、後者は精神、立ち居振る舞いの問題である。
処女厨が叩かれるのは「処女イコール清純」という前提になってるからであろうよ。
だと思うんだよね。
_ [QuickML][Ruby][コードリーディング]QuickML を読む - logger
ログを印字する処理。同期に対して注意している。
require 'quickml/utils' require 'thread' module QuickML class Logger def initialize (log_filename, verbose_mode = nil) @mutex = Mutex.new @log_file = File.safe_open(log_filename, "a") @log_file.sync = true @verbose_mode = verbose_mode end
log_file.sync は結局 IO の sync を設定している。
sync
現在の出力同期モードを真偽値で返します。同期モードが真の時は出力関数の呼出毎にバッファがフラッシュされます。
そのまま。
private def puts_log (msg) @mutex.synchronize { time = Time.now.strftime("%Y-%m-%dT%H:%M:%S") @log_file.puts "#{time}: #{msg}" } end public def log (msg) puts_log(msg) end def vlog (msg) puts_log(msg) if @verbose_mode end
外部には log() と vlog() を公開しておき、結局各々 puts_log() を呼んでいる。puts_log() では RFC 3339 ぽい日付時間フォーマットに整形している(ref. RFC3339 インターネット上の日付と時間:タイムスタンプ )。( タイムオフセットが無いよね? )
def reopen @mutex.synchronize { log_filename = @log_file.path @log_file.close @log_file = File.safe_open(log_filename, "a") } end
同期を気にしつつファイルを一度閉じて開いている。reopen() は HUP を貰ったときに実行される。
trap(:HUP) { config.logger.reopen }
_ ,
結局個体差なので「処女だから云々」というのはつまり主語が大きいんである。
_ 買い物
@紀伊国屋
ラノベ分が枯渇した
4044748187
4094511539
4086305577
4840131627
4840134049
4840124019
4840126003
4044740046
4044740054
4044740062
4044740070