SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

japan.internet.com翻訳記事

10分でできる初めてのRubyプログラム

プログラムを拡張しながらRubyの動作を学習する

Launcherの実装

動作の追加

 これから作成するプログラムの主な目的は、指定されたファイル名を受け取り、これを関連するアプリケーションに渡して何らかの処理を行うことです。そこで、ファイル名とアプリケーションのマッピング方法をプログラムに認識させるために、Launcherクラスのインスタンスの作成時に、何らかのマッピングを渡す必要があります。クラスメソッドnewを使ってクラスのインスタンスを作成できることは、既に示したとおりです。最初から何らかのデータセットを含んでいるインスタンスを作成するには、newメソッドに引数を渡します。これを行うには、もちろん、Launcherにコードを追加しなければなりません。

def initialize( app_map ) 
  @app_map =  app_map
end

 Rubyでメソッドを定義するには、defキーワードに続けて、メソッド名と引数リスト(必要な場合)を指定します。引数リストはわかりやすいように括弧で囲まれていますが、コードの意味が明白な場合は省略してもかまいません。

 Rubyオブジェクトには、多様な動作が組み込まれていることに注意してください。これをそのまま使用することもできれば、上書きすることもできます。

引数をnewメソッドに渡すのにinitializeメソッドを追加する理由
 「newメソッドに引数を渡したいはずなのに、どうしてinitializeというメソッドを追加するのだろう」と思っている人もいるでしょう。その理由は、Rubyがクラスからオブジェクトを作成する方法に関係しています。
 Launcherをはじめとするすべてのクラスは、Objectクラスを継承し、クラスから作成されるオブジェクトはデフォルトでinitializeメソッドを持つことが決められています。クラスメソッドnewが呼び出されると、まず、目的のオブジェクトに対してリソースが割り当てられ、次に、新しいオブジェクトのinitializeメソッドが呼び出されます。newを介して作成パラメータを提供するには、独自のinitializeメソッドを定義して、新しく作成されたインスタンス内で引数を処理する必要があります。

インスタンス変数

 initializeメソッドは、app_mapという1つの引数をとります。前述の変数と同様、メソッド引数の型も指定されていません。指定するのはメソッドが1つの引数(app_map)をとるということだけであり、メソッドの本体の中でこの引数は変数@app_mapに割り当てられます。@記号は、変数がインスタンス変数であること、つまり、このオブジェクト内のすべてのコードに対して有効であることを示しています。オブジェクト作成時にこのインスタンス変数を作成すると、コードに追加する他のメソッドでもこの変数を使用できます。

 指定されたファイルを関連アプリケーションで実行するためには、プログラムにさらにコードを追加します。

class Launcher

  def initialize( app_map )
    @app_map =  app_map
  end

  # Execute the given file using the associate app
  def run( file_name )
    application = select_app( file_name )
    system( "#{application} #{file_name}" ) 
  end

  # Given a file, look up the matching application
  def select_app( file_name )
    ftype = file_type( file_name )
    @app_map[ ftype ]
  end

  # Return the part of the file name string after the last '.'
  def file_type( file_name )
    File.extname( file_name ).gsub( /^\./, '' ).downcase 
  end

end

 runメソッドはファイル名を引数として受け取り、それをselect_appに渡すことで、実行すべきアプリケーションを見つけます。次に、Rubyのsystemメソッドを使って該当アプリケーションを起動し、ファイル名を渡します。systemメソッドは、指定されたコマンドをサブシェルに送り込んでいるだけです。一方、runメソッドからファイル名を受け取ったselect_appは、file_typeを呼び出して、正規化されたファイル拡張子を取得します。そしてこれを@app_mapへのキーとして使用し、実行すべきアプリケーションを見つけます。

 file_typeはファイル名を受け取り、RubyのFileクラスのクラスメソッドを使って拡張子を取得します。extnameで返される文字列には、ピリオド(.)とファイル拡張子が含まれます。これは必要ないため、gsub(global substitute:全置換)を使ってその部分を除外します。残りの部分をdowncaseですべて小文字に変換します。

 コンパクトなコードにするために、ここでは上記のメソッドをつなげて記述しています。つまり、File.extnameから返される文字列をgsubが受け取り、gsubから返される文字列をdowncaseが受け取ります。

 ここまでのサンプルコードは、StringおよびHashであることが期待されるオブジェクトを使用してきました。しかし、本当に注意すべきは、これらのオブジェクトが特定のメッセージに適切に応答することです。このような小さなプログラムでは、メッセージとランタイム動作に基づく巧妙で強力なオブジェクトシステムの重要性は感じられないかもしれませんが、大規模なRubyプログラムを作成するときには、この点を理解していることが重要です。

オブジェクト、データ型、および動作について(ダックタイピング)

 Rubyは、オブジェクト指向プログラミングのメッセージ受け渡しモデルに従います。foo.barのようなコードがある場合、これは、fooで参照されるオブジェクトにメッセージ「bar」が渡されることを意味します。大抵の場合、fooで参照されるオブジェクトにはメソッドbarがあるため、このようなコードを目にすると、これは呼び出し側のfoobarメソッドだと考えがちです。たしかにこれは便利で一般的な考え方ですが、その裏で何が行われているかを理解することが重要です。

 オブジェクトはメッセージを受け取ると、最初に、対応するメソッドを探します。この検索は、オブジェクト自身のクラスから始まってObjectクラスに達するまで、継承階層をさかのぼる形で行われます。一致するものが見つからない場合は、メソッドmethod_missingが呼び出されます。もうおわかりかもしれませんが、method_missingには、例外の発生のみを行うデフォルト実装があります。しかし、デフォルトの初期定義を上書きできるように、method_missingも再定義できます。このメソッドは自由に再定義でき、オブジェクトに機能を追加して、任意のメッセージ要求を処理できるようにしたり、実際よりも多くのメソッドをオブジェクトに実装したりできます。

 この柔軟性はRubyの大きな魅力の1つの核を成すものですが、場合によってはトラブルの原因になるという重大な側面もあります。ご存知のように、変数の作成時やメソッド引数リストの宣言時に、データ型の宣言は行われません。しかし、データ型をチェックすることは可能です。コードでオブジェクトの型を確認し、それに応じて処理することができます。例えば、ファイル名(Stringオブジェクトなど)とファイルハンドル(Fileオブジェクトなど)のいずれかを受け取るメソッドを作成するものとします。

 ただし、Rubyのコードでは、単なる保護手段としてオブジェクトの型をチェックすることはほとんどなく、指定されたオブジェクトが特定の型であると自ら主張しない限り続行を拒否するということはありません。クラスとオブジェクトは実行時に可変なため、Rubyにおけるデータ型の概念は、基本的に、ある特定時点でのオブジェクトの動作として定義されます。データ型は、その基になるクラスではなく、オブジェクトが応答するメソッドによって定義されます。Ruby愛好者のLogan Capaldoは次のように述べています。「Rubyの世界では、何が親なのかということは問題にならない。重要なのは、何について話しているかということだけだ。」

 これを一般的に表す用語として「Duck Typing(ダックタイピング)」があります。これは、「アヒルのように歩き,アヒルのように鳴くなら,それはアヒルである」という考え方です。

バージョン0の仕上げ

 この最初のバージョンを、使える状態に仕上げます。ファイルの最後に次のコードを追加してLauncherのインスタンスを作成し、それを使ってアプリケーションを実行します。

def help
  print " 
  You must pass in the path to the file to launch.

  Usage: #{__FILE__} target_file
" 
end

if ARGV.empty?
  help
  exit
else
  app_map = {
     'html' => 'firefox',
     'rb' => 'gvim',
     'jpg' => 'gimp'
  }

  l = Launcher.new( app_map )
  target = ARGV.join( ' ' )
  l.run( target )
end

 helpメソッドは必要に応じて指示を出力します。ARGVは引数ベクトルです。組み込みのRubyオブジェクトであり、プログラムに渡されるすべてのパラメータを保持します。これが空の場合、プログラムは何も処理できないため、ヘルプを表示して終了します。そうでなければ、ハッシュオブジェクトを作成し、それを変数app_mapに割り当てます。

 { ... }という表記法は、Hashオブジェクトを作成する場合のRubyのリテラル構文です。Hash.newも使用できますが冗長です。リテラル表記では、=>を使ってハッシュキーを値にマップします。ハッシュはLauncherインスタンスにデータを割り当てるために使われますが、コマンドライン引数は1つの文字列にまとめられて変数targetに格納され、runに渡されます。

 このコードを試す前に、正しい実行プログラムを参照するように、app_mapで使用されるアプリケーション値を変更する必要があります。拡張子「rb」をテキストエディタにマップしている場合、次のようなコマンドでコードを試すことができます。

$ ruby launcher.rb launcher.rb

 ソースコードがエディタで開きます。

次のページ
バージョン1への拡張:動的ロードの使用

この記事は参考になりましたか?

japan.internet.com翻訳記事連載記事一覧

もっと読む

この記事の著者

japan.internet.com(ジャパンインターネットコム)

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

James Britt(James Britt)

アリゾナ州スコッツデールでRubyコンサルティング会社のNeurogamiを経営。仕事でRailsやNitroのアプリケーションを作成していないときは、Ruby-Doc.orgとRubyStuff.comの運営に携わる。2001年以来Rubyコミュニティで活動し、Rubyに関する書籍を多数執筆。米国およびヨーロッパのRubyカンファレンスで講演し、Phoenix Ruby Users Groupのコーディネータを務める...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/1528 2007/07/26 08:09

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー