けいじゅ@日本ラショナルソフトウェアです.

In [ruby-list :04506 ] the message: "[ruby-list:4506] Re: mail address
class ", on Sep/22 18:18(JST) akira yamada / やまだあきら 
<akira / linux.or.jp> writes:

>やまだあきらです.

>いまだこりず(^_^;)にメールの解析用スクリプトのお話です.

(^^;;;

>この辺り, いろいろ考えてはみたのですが
>結局一度実装してみなきゃどっちがいいかわからないなぁ
>という結論にいたり, とりあえず class のままにしています.

>ですね. 
>機能としての目標はあるのですが, 
>それが ruby 的にどうなるかと言うのを予想できないので
>しばしこのままの状態で続けようと思っています.

そうですね. クラスの構造などは一度になかなか決められないなと感じていま
す. 私も, 最初クラスで作っていて後からモジュールにしたりすることは良く
あります. 

C++などと違ってrubyはかなり動的な言語であることと, クラス/モジュールの
構成に関して自由度が高いので, 実際に機能がはっきりしてきてからでも遅く
ないと思っています.

# UMLを推進している会社の人間がこんなこというと問題になったりして
# (^^;;;

>なるほど, と思ったのですがネストが許されちゃっていて
>特別扱いすると逆に複雑になるかな? と思えたので
>この点は以前のままにしています.
># コメントの処理だけわけちゃいましたが… 

>私がやっている程度の解析だと
>状態の数がこれ以上増えると言うこともないでしょうし.

どのように作らなくてはならないって決まりがあるわkでもないので, 作りや
すい方法で実現すればいいのでいいんじゃないでしょうか.

>ってなわけで, 
>前のやつはちょいと形が変わりましたが再び流してみます. 
>またコメントをいただけるとありがたいです.

懲りずにまた大量にコメントします.

まず, 個別に

[0]
>module RFC822_staff

今は定数だけですが, ここで定義されるメソッドがあるとさらにモジュールら
しくなるんですけどね.

[1]
個人的な好みですが:

>class Header
>  attr :header
>  attr :date
>  attr :subject
>
>  include RFC822_staff

classの直後にincludeがある方が好きです. 

[2]
>    @date = 
>      Header.date_to_gmtime(@header['Date']) if @header.key?('Date')
>    @subject = @header['Subject'] if @header.key?('Subject')

    @subject = @header['Subject']

で十分です. 気になるのは@dateは良いとして, @subjectをインスタンス変数
として割り当てなくても良いのでは? って感じがします.

[3]
>  def [](field_name)
>  def key?(field_name)

なかなか良いですね. Headerクラスのメソッドらしいですね(^^;;;

[4]
>  def get_address(field_name)
>    if @header.key?(field_name)
>      return Header.parse_address(@header[field_name])
>    else
>      return nil
>    end
>  end

なるほど, こういう実装になりましたか. それであれば,
Header.parse_addressはインスタンスメソッドでも良いと思います. 将来この
メソッドをprivateにしたいと思った時などは, インスタンスメソッドでない
とprivateにはできません.

[5]
>  def make_reply_header #(my_address)
>    h = ""
>
>    if r = self.get_address('Reply-to')
>      h += "To: " + r + "\n"
>    elsif r = self.get_address('From')
>      h += "To: " + r + "\n"
>    end

Header#get_address()は配列を返すんですよね... Reply-toやFromは, 通常は
1アドレスなので, これで動作しているんですが... 明示的に:

      h += "To: " + r[0] + "\n"

としておいた方が無難かと...

それに, Replu-To:は複数アドレス書けるみたいですね. それに対応するので
あれば:

h += "To: " + r.join(", ") + "\n"

ですね.

あと, += よりは, String#concatの方が実行効率は良いです. 今回のような場
合はconcatを用いても間違いはないでしょう.

あと, 勝手な要望ですが, MHの様に自分でカスタマイズが行いやすい構造になっ
ていると嬉しいですね.

# MHはカスタマイズがとりあえずできるだけですが...

[6]
>class RFC822
>  include Comparable
>  def <=>(o)

このあたり, rubyっていますね(^^;;;

[7]
>  def RFC822.new(f)
>    unless f.kind_of?(IO)
>      f = open(f, "r")
>      me = super(f)
>      f.close

f = open(f, "r")
begin
  me = super(f)
ensure
  f.close
end

は定石として覚えておいた方が良いかも?

[8]
>  def <=>(o)

なかなか素晴らしいのですが, このままでは

  Integer <=> aRFC822

などが, 実現できていませんよね?  

Integer#<=>(other)は, 当然, Integerのメソッドなので定義はできません(実
際にはできますが, それはおいておいて). そのかわり, 自分が知らないクラ
スが来ると, x, y = other.corece(self)を呼びだし x<=>yを実行するように
なっています. Rationalの場合を例にとると, Rational#coerce(other)は:

  def coerce(other)
    if other.kind_of?(Float)
      return other, self.to_f
    elsif other.kind_of?(Integer)
      return Rational.new!(other, 1), self
    else
      super
    end
  end

の様になっています. otherがFloatの時は自分をFloatにし, otherがInteger
の時は, otherをRationalに変換しています.

この場合のように, より一般的なクラスに変換が可能な時には問題がないので
すが, MatrixとNumericの場合のように自然な変換の定義ができない場合は, 
ちょっと工夫が必要になります. 詳しくは, matrix.rb(Matrix#coerce)を参照
して下さい.

[9]
>とりあえず mbox 形式は考えていません.

これからメイラを作るんだったら, ファイル形式を自由に選べる方がベストで
すね.

[10]
># まだエラー処理, その他はいっさいしていません.

確かに, もうそろそろ考えた方が良いかも...

[11]
>## o multipart ボディを扱える (まだ, やってない)

ここをどのように扱うかが, 興味ありますね.

[全体として]
前回はAddressクラスがありましたが今度はHeaderクラスになりましたね. 

正直いってこの段階では, Headerクラスの必要性はないと思います. class
RFC822にHeaderの機能をマージした方が良いと思います.

個人的にはAddressクラスは残しておいた方が良かったんではないかと思って
います. (正規化された)文字列としてHeaderの中にアドレスのパーズ機能を持
たせるよりも, クラスとして独立させてAddressクラスにアドレスのパーズ機
能を持たせた方が(Headerもスリムになりますし)すっきりすると思います. ま
た, aliasなどのアドレスを扱う機能のためにもその方がよいんじゃないかっ
て気がしています.

[最後に]
>## かなり太っちゃったなぁ… (-_-;

頑張りましたねえ...

__
................................石塚 圭樹@日本ラショナルソフトェア...
----------------------------------->> e-mail: keiju / bc.mbn.or.jp <<---