「7つの言語 7つの世界」 Ruby 3日目(最終日)(Jugemより移植)

Posted by johtani on Wednesday, September 14, 2011

目次

ということで、Rubyの最終日の感想。 今回もセルフスタディの私の回答が最後の方に記載されてます。見たくない人は気をつけてください。 ツッコミ大募集です。コメント欄にどしどしコメントください。そこは違うだろ?こっちのほうがいいのでは?という感じで。

感想:

◎メタプログラミングが特徴

例:ActiveRecordのhas_many、has_oneがいい例

◎オープンクラス

クラス定義をいつでも変更可能。 あらゆるクラスやオブジェクトをいつでも再定義できる 書きやすいコードのために再定義が可能=何でもできるが気を付ける必要あり。 DSLの定義に便利。 確かに便利。ただ、範囲を限定しないと予期せぬ場所で問題が発生しそう。 ※解析するための手段はいいのがあるのかな?=>method_missingみたい

◎method_missing

対象メソッドが見つからない場合に最後に実行されるメソッド 通常はNoMethodErrorが発行される。

◎モジュールによるメタプログラミング

defやattr_accessorなどその一例。 DSLではモジュール内にメソッドを定義してメソッド+定数を利用 ActsAs…ってそういう意味合いだったのか。 親クラスバージョン、親クラス+マクロ、モジュールそれぞれの実装の仕方の紹介。 ※マクロもinclude同様、実行順で、メソッドの上書きが発生するのか? ActiveRecordではメタプログラミングを利用してDBのカラム名からアクセサを追加。 シンタックスの美しさ=読みやすさ

感想&疑問点:

メタプログラミングはフレームワークを作成するのが便利そう。 ただし、エラーや問題が起きた時の対処をきちんと準備しておかないとひどい目に合いそう。 クラスやモジュールはわかりやすい単位で1ファイルにまとめるもの? ファイル名の規則とかあったりする? ディレクトリ構成でパッケージ構成が可能? 複数のモジュール(gemとか)を組み合わせて使っている場合にincludeの順序がどのようになるかが気になる。 予期せぬ順序でincludeされて利用しようと思ったメソッドがオーバーライドされてるとかありそう。 追っかけるのがまた大変そうだ。 異なるパフォーマンス(開発者の開発速度)の観点が一番おもしろかった。ただ、なれるまでは大変そう。すんなりinjectとかコードブロックをうまく利用するイメージがわかない。 まぁ、思考については反復練習かな。これは他の言語でも一緒かな

ようやく、Rubyの世界が終わりました。楽しかった。次は未知の言語である「Io」です。

   

(試してみよう:)

○eachメソッドがCsvRowオブジェクトを返すようにCSVアプリケーションを変更せよ。そのCsvRowのmethod_mmissingを使って、与えられた見出しの列の値を返すようにせよ。
モジュールにて実装してみた。 ```

module ActsAsCsv

def self.included(base) base.extend ClassMethods end       module ClassMethods def acts_as_csv include InstanceMethods end end

module InstanceMethods

def read
  @csv_rows = []
  file = File.new(self.class.to_s.downcase + '.txt')
  headers = file.gets.chomp.split(', ')

  file.each do |row|
    @csv_rows << CsvRow.new(headers,row.chomp.split(', '))
  end
end

def each(&block)
  csv_rows.each(&block)
end

attr_accessor :csv_rows

def initialize
  read
end

end

class CsvRow def initialize(headers, csv_contents) @headers = headers @csv_contents = csv_contents end

attr_accessor :headers, :csv_contents

def method_missing name, *args
  csv_contents.fetch(headers.find_index(name.to_s))
end

end end

ここまでが実装したモジュール+クラス。
以下は実行例。id,name,sizeというheaderをもつCSVを使ってみた。

require ‘acts_as_csv_module_mod.rb’ => true class RubyCsv include ActsAsCsv acts_as_csv end => RubyCsv csv = RubyCsv.new => #<RubyCsv:0x103c4c530 @csv_rows=[#<ActsAsCsv::CsvRow:0x103c4c030 @csv_contents=[“1”, “RubyCsv.class”, “20”], @headers=[“id”, “name”, “size”]>, #<ActsAsCsv::CsvRow:0x103c4be78 @csv_contents=[“2”, “JRubyCsv.class”, “50”], @headers=[“id”, “name”, “size”]>]> csv.each {|row| puts row.name} RubyCsv.class JRubyCsv.class => [#<ActsAsCsv::CsvRow:0x103c4c030 @csv_contents=[“1”, “RubyCsv.class”, “20”], @headers=[“id”, “name”, “size”]>, #<ActsAsCsv::CsvRow:0x103c4be78 @csv_contents=[“2”, “JRubyCsv.class”, “50”], @headers=[“id”, “name”, “size”]>] csv.each {|row| puts row.id} (irb):8: warning: Object#id will be deprecated; use Object#object_id 2179096600 (irb):8: warning: Object#id will be deprecated; use Object#object_id 2179096380 => [#<ActsAsCsv::CsvRow:0x103c4c030 @csv_contents=[“1”, “RubyCsv.class”, “20”], @headers=[“id”, “name”, “size”]>, #<ActsAsCsv::CsvRow:0x103c4be78 @csv_contents=[“2”, “JRubyCsv.class”, “50”], @headers=[“id”, “name”, “size”]>]


<span style="color:#FF0000">※idというcsvフィールド名にしたら、object_idとかぶっているようでwarningが出てしまった。</span>

CsvRowクラスの定義がモジュールの中に入っているが、この実装でも動くみたい。ただ、パッケージみたいな感じ7日までは調査してない。。。

comments powered by Disqus