2011年10月31日月曜日

Ruby の module

scaffold の中身をちょっと掘り下げる第2回目の記事です。
といっても、Ruby における module という言語要素の理解をするためだけの記事なので、本質的には rails / scaffold とは関係ないかもしれません。

modelname_helpers.rb は module として定義されていました。
大学院時代のトラウマ経験のせいで「モジュール」という言葉には過敏に反応してしまいます…。

モジュールという言葉も曖昧な言葉なのですが、個人的によく使っていた定義は「再利用可能なプログラム単位・部品」です。
例えば、java における package や class は上記の定義においてモジュールです。既存の package を import したり、既存の class の extend することでプログラムの再利用をしています。

Ruby の module は include という形でプログラムの再利用を行います。任意の class で
include ModuleName
とすることで、その module で定義されたインスタンスメソッドや定数などを使えるようです。
公式ドキュメントではない(?)ですが、Programming RubyのWeb版でちょっと触れられているのを見つけました。

class と module

この辺りについて書きたいことはあるけれど、何か難しく考えてるだけのような気がします。
ざっくり言うと、
  • class は(class だけに)分類を行ってその共通部分を抽象化する(スーパークラスにする)
  • 分類から漏れた・分類にそぐわない共通部分を module として抽象化する(抽象サブクラスにする)
くらいの表現になりそうですが、やっぱり詳しく書くのは止めておきます。

ActiveRecordとはなんぞや

昨日の「scaffoldの中身を見ていく」を読み返してみると、いくつか不思議なことを見つけました。なので、数回に分けてちょっと掘り下げたいと思っています。
やっぱり時間をおいて改めて考えるって大事ですね。

なぜ Post クラスの定義は空でよいのか?

Post クラスはmodel部分を担うです。Rails における model は(誤解を恐れずに言うと)その Rails アプリケーションで使うデータの表現です。ということは、「Postとはどんなデータなのか?」を表現しているはずです。Javaの人の感覚なら、そのデータを構成する属性がクラスのインスタンス変数として宣言されているのが自然に思えます。

でも、空です。ActiveRecord::Base を extend している部分を除いて

その部分が重要なんだろうと思って、ActiveRecord::Base の API ドキュメントを見てみると Posts クラスが空の定義で済む理由が何となく分かりました。一行目で
APIドキュメントから引用:
Active Record objects don’t specify their attributes directly, but rather infer them from the table definition with which they’re linked. Adding, removing, and changing attributes and their type is done directly in the database. 
要するに、「そのクラスに対応しているデータベースのテーブル定義を見て、そのクラスを構成する属性を推定している」ということだったようで、クラスにインスタンス変数の宣言などが必要ない理由が何となく分かりました。他の "Adding, removing, and changing ..." の部分がいかに実現されているのかが理解できたら、「しっかり分かった」っていうことにします。
# 分かっている人っぽく表現すると「それって Object-Relation Mapping だよね?」

あと、「もっと詳しい(例の載った)ドキュメント」としてREADMEも挙げてあったので、そのうちきちんとフォローしたいと思います。こちらは例が豊富なので、「なぜ動作するのか」よりも「いかに使うのか」に重点を置いた感じになっているので、自分でプログラムを作る際の参考にするべきドキュメントかなあ、と。

scaffold の中身を見ていく

とあるtweet
Scaffold をそらで写経できない Rails プログラマーは中2からやり直せ。
と、心温まるお言葉が表明されていました。

Ruby の if 文すら怪しい自分は小学生以下決定なわけですが、少しでも学年を上げるべく scaffold の写経をしたいと思います。この前、"rails g scaffold post ..."で作った足場を元に中身を見ていきます。

一通り書いてみて思った感想としては、 RailsGuide の getting started の中身を模写しただけみたいなブログになっちゃいました…。

app/models/post.rb

ソースコード:
class Post < ActiveRecord::Base
end
"rails g scaffold modelname" で作成されるモデルの中身は、ActiveRecord::Base クラスを extend しているだけの空のクラスであることがわかります。

app/helpers/posts_helpers.rb

ソースコード:
module PostsHelper
end
modelname_helpers.rb についてもほぼ同様に空の定義が与えられます(こちらは module ですが)。

config/routes.rb

このファイルは元々存在するファイルですが、generate scaffold で行が追加されます:
resource :posts
# post なんていう紛らわしいモデル名で作ってしまったのは失敗だったかなあ…。

routes.rb では URL → controller のマッピングを行っているようです。
http://guides.rubyonrails.org/routing.html の 2.2節 "CRUD, Verbs, and Actions" を見てみると、"resource :posts" ではデフォルトの7つの挙動が追加されるそうです。
  1. get /posts => index : post の一覧を表示
  2. get /posts/new => new : 新規作成のためのフォームを表示
  3. post /posts => create : 新規作成
  4. get /posts/:id => show : #{:id} 番目の post を表示
  5. get /posts/:id/edit => edit : #{:id} 番目の post を編集するフォームを表示
  6. put /posts/:id => update :  #{:id} 番目の post を更新
  7. delete /posts/:id => destroy :  #{:id} 番目の post を削除
…ちらっと目に入った2.5節の感じだと "resource :posts" と "resource :post"で意味が変わってくるような気がするので、その点は写経的には気をつけるポイントっぽい。
ただ、routes.rb の詳しい内容は上記のガイドだけでもブログ一回分以上の内容がありそうなので別記事にします。

おまけ:rake routes と rails-sh

routes.rb をググってた時、@ITの記事を見つけ、その中で rake routes コマンドと rails-sh を紹介していました。

rake routes コマンドは routes.rb で指定されたマッピングについての情報を表示するためのコマンドです。試しに実行してみたら、先ほど列挙した7つの動作が post について表示されました。resource :posts の効果が確認できた形です。

また、「rails コマンドとか rake コマンドは何かやたらと時間がかかるなあ。仮想マシンなのがいけないのかなあ」と思っていたんですが、@ITの記事を読んでて
以上のように、railsコマンドやrakeはRailsアプリケーションを実装する上で欠かせない便利なツール群です。
しかし、コマンドの実行のたびにRailsアプリケーションをロードするので、毎回少し時間がかかってしまいます。
それを解消するために本記事の筆者の1人である@jugyoが作ったツールが「rails-sh」です。
rails-shは、あらかじめRailsアプリケーションをロードした状態で起動するコマンドラインシェルのようなものです。
という記述を見つけ、時間のかかる理由が納得できました。
rails-sh を使うかどうかは後々考えます…。

app/controllers/posts_controller.rb

おそらく scaffold で生成される Ruby プログラムはここが全てなんだろうと思います。
…こういう書き方をすると、「TDDだから TestUnit の部分を重要視しないなんて、やっぱり初心者だな」と思われるんでしょうか。「メインの関心事(= システムの挙動)に関する部分で」っていう前置きがあれば正しい表現になるのかなあ…。

さて。
class PostsController < ApplicationController
...
end
まず分かることは、ApplicationController のサブクラスであることです。
ApplicationController は ActionController (ActionPack gemsの一部?)を extends しているクラスです。ApplicationController クラスの中身はほぼ空なので、その親クラスである ActionController クラスの中身に意味があるはずです…が、ActionController はちょと巨大なのでブラックボックスにしておきます。
# ほぼ空 = protect_from_forgery のみ = CSRF対策のためのコードが定義されている

PostsContoller クラスでは "resource :posts" で定義される7つの挙動に対応するメソッドが定義されています。
一番シンプルな形のメソッドは
def index
  @posts = Post.all

  respond_to do |format|
    format.html
    format.json { rendor json: @posts }
  end
end
というものです。

最初の @posts 変数への代入では Post クラス(モデルクラス)が使われています。この場合は .all なので、「全ての Post」となります。他のメソッドでの @post の代入では、
  • find メソッドを使って、モデルクラスのデータを検索している
  • new メソッドを使って、モデルクラスのデータを新規作成している
のが見て取れます。

あと、Rubyベストプラクティスを読んでいるおかげか、respond_to が「特別なメソッドを使ってDSLっぽく仕上げる」仕組みなのが想像できます。どうやら actoinpack/lib/action_controller/metal/mime_responds.rb で定義されているようですが、追いきれませんでした…。
exampleを見る限りだと、respond_to に渡されているブロックの引数 format (Webクライアントからのリクエストによって決まるらしい)によってどんな仕事をするのかを記述するようです。
また、(この部分はまったくプログラムを追っていませんが挙動から予想するに)html フォーマットの場合のデフォルトの挙動は methodname.html.erb に処理が移るようです。

その他の scaffold の生成物

きょうのところは勘弁してやる…してください。
      create    db/migrate/20111030125941_create_posts.rb
      invoke    test_unit
      create      test/unit/post_test.rb
      create      test/fixtures/posts.yml
      invoke    erb 
      create      app/views/posts
      create      app/views/posts/index.html.erb
      create      app/views/posts/edit.html.erb
      create      app/views/posts/show.html.erb
      create      app/views/posts/new.html.erb
      create      app/views/posts/_form.html.erb
      invoke    test_unit
      create      test/functional/posts_controller_test.rb
      invoke    helper
      invoke      test_unit
      create        test/unit/helpers/posts_helper_test.rb
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/posts.js.coffee
      invoke    scss
      create      app/assets/stylesheets/posts.css.scss
      invoke  scss
   identical    app/assets/stylesheets/scaffolds.css.scss
大まかに把握しておくと、db の部分、view の部分(erb および assets/coffee,scss)、テストの部分が手つかず。


2011年10月28日金曜日

Rubyベストプラクティス 3章(前半)

3章は Ruby の動的な性質を使って、柔軟なプログラムを構築する方法について。

「動的な性質」ってよく分からずに読み進めたら、結局リフレクションのことなんですね。
…ある意味、専門領域です。
ということで、3章前半はオープンクラスの話だと思ってしまおう。

3.1節 メソッドの定義を動的に変更

例として、BlankSlateというクラスが挙げられています。
ちょっと背景説明の方へ話を移すと、Ruby 1.9系では、言語組み込みとして BasicObject という「何の機能(メソッド)も持たないクラス」が提供されています。
「何の機能(メソッド)も持たないクラス」は method_missing などで動的なメソッド生成をするさいに衝突の危険を避けることが簡単なので重宝されます。
そういった便利なクラスが Ruby 1.8系では言語要素ではないので、Jim Weirich が BlankSlate を提供してくれています。

BlankSlate がやっていることを自然言語で説明すると、「そのクラスに定義されたメソッドを(一部を除いて)一旦 BlankSlate#hidden_method というハッシュに退避した上で undef_method している」です。
こうすることでメソッドの定義されていないクラスを作っています。
また、一旦退避(undef_method)させたメソッドを define_method で再定義(書き戻す)もできます。

これらの undef_method や define_method は動的にメソッド定義を行うための Ruby の仕組みで、メソッド(の定義?body?)自体がオブジェクトであることを利用しています。

3.2節 柔軟なインターフェース 

…同じタイトルが2章でもでてきます。Rubyにとって非常に重要なテーマなのでしょう…。

3.2.1 instance_eval()

2節でも似たようなテーマでコードブロックの話が出ていますが、ここでもコードブロックの話で、コードブロックに引数を渡す方法とその効用についての話…だと思います。
ちょっと何の話を目的にしているのか自信がありません…。

まずは、本に載っているサンプルプログラム(に近いもの)を実際に試してみます。
[kazu@cent6 bestpractice]$ ruby 03-2-1-instanceeval-err.rb
03-2-1-instanceeval-err.rb:29:in `generate_pdf': undefined local variable or method `full_name' for #<Document:0xb77d0e30> (NameError)
        from 03-2-1-instanceeval-err.rb:12:in `instance_eval'
        from 03-2-1-instanceeval-err.rb:12:in `generate'
        from 03-2-1-instanceeval-err.rb:28:in `generate_pdf'
        from 03-2-1-instanceeval-err.rb:35
instance_eval() で実行されるコードブロックは、そのオブジェクトをコンテキスト(= enclosing scope)とするので、そのローカル変数しか参照することができません。一方、コードブロックを call メソッドで呼び出す場合、そのコードブロックが定義された時点でのコンテキストを引き継いだ Proc オブジェクトの呼び出しになります。

つまり、
class Prawn::Document
  def self.generate(file, *args, &block)
    pdf = Prawn::Document.new(*args)

    block.arity < 1 ? pdf.instance_eval(&block) : block.call(pdf)
    ...
  end
end
というプログラムでは、
  • instance_eval(&block) は pdf オブジェクト(Documentクラス)のコンテキストで
  • block.call(pdf) は、blockの本体が作られた時点(MyBestFriendクラスのメソッド呼び出し時)のコンテキストで
実行される…はずです。

3.2.2 method_missing() と send()

Ruby では定義されていないメソッドを実行すると(さっき試したみたいに)「undefined ...」と当然エラーになりますが、method_missing() というメソッドを定義しておくと undefined methods を実行した際に、エラー処理の代わりに method_missing() が実行されます。

これを使って動的にメソッドを解釈させる仕組みの説明です。
Convention Over Configuration の例にもなっていると思います。

例として図形描画プログラムが挙げてあります。
複数の図形の描き方(線画、塗りつぶし、線画と塗りつぶしの両方、…)と複数の図形要素(直線、円、多角形…)があるときに、
stroke_line [0,0], [50,50]
fill_circle_at [100,100], :radius => 50
stroke_and_fill_polygon [100, 250], [...], ...
という書き方が出きるとスマートです。ただ、いちいち
def 描き方_図形要素(*args)
  図形要素(*args)
  描き方
end
というメソッド定義を行うのは手間がかかります。組み合わせ爆発の状態です。

これを method_missing() を使って、「"描き方_図形要素" というメソッドが来たら、"図形要素"メソッドと"描き方"メソッドを実行する」という仕組みにしています。
また、この時の「…を実行する」ための方法として send(methodname, *args, &block) を使っています。

3.2.3 DSLっぽいアクセサ

DSL = Domain Specific Language = 領域指定言語、つまり、特定分野の記述を行うための言語のことです。Ruby は、その言語の中で DSL 風に書けることがよく知られていて、一部の人には大人気です。

さて。

普通のアクセサは
font_size = 10
のようになるのですが、定義をちょっと工夫すると
font_size 10
という風に書けて、非常にDSLっぽくなります(CSSなんかでこんな記述ができそうな雰囲気)。
しかも、このメソッドは
font_size
と引数なしで呼び出すと値を返す reader にもなります。

でも、難しい定義ではなく
def font_size(size = nil)
  return @font_size unless size
  @font_size = size
end
とするだけです。

また、通常のアクセサの代わりもさせておくと、この書き方に慣れない人にとって有意義なので、
alias_method :font_size= :font_size
とエイリアスを作っておくTipsも紹介されていました。

3.3節 特異メソッド

…と思ったのですが、長くなったので3章は分割してお送りすることにします。

2011年10月27日木曜日

ブログのデザインをちょっと替えてみました。

全体の色を青系統から赤系統に替えて、ヘッダに画像を入れてみました。

ヘッダの画像はとある櫻花の画像生成を使って生成させていただきました。
こういうみんなに気軽に使ってもらえるWebサイトを自分で作れるようになりたいし、そうなるための Ruby の勉強だ!とモチベーションを上げてみます。

あと、テンプレートも替えてみました。Blogger は hN タグが使えなくてなんでだろうと思っていたらテンプレート側で使っているせいなんですね…。
自分でHTMLを編集して、カスタムCSSを使うことにしました。

…一旦、追加したカスタムCSSはテンプレートで直接「HTMLを編集」しないと修正できないのか。
修正できないのかと思って冷や汗かいた…。

ちょっと話はそれて Javascript へ。

たまにはお仕事での話もしてみます。

お仕事の方でプログラムを書く機会はさほど多く無いのですが、HTA という仕組みを使って Windows Server (ActiveDirectory) を操作するツールを VBscript で作っていたりします。
ツールをコンパイルが必要な言語で作成すると、「ソースコードが紛失して手直しが出来ない」「開発環境が必要になる・用意するのが面倒」などの困難がつきまとうので、(主に自分以外がメンテナンスするにあたって)ツール自体がソースコードファイルになり、メモ帳で修正可能なスクリプト言語を採用しています。

HTA は基本的にタダのHTML(バックエンドとしてInternetExplorerが動いているだけ?)なので、「View の上で全ての処理をするお粗末なWebアプリケーション」を書く感覚に近く、自分のスキルでも何とかなってしまいます。
…そろそろ、綺麗な形で書き直したくなってきたなぁ。

で、愚痴はともかく、ActiveDirectory から複数件のユーザアカウントデータを検索して表示するツールを作ったんですが
「ソートしたい…」
と素朴な気持ちが出てきました。

HTMLで表データをソート、となったら「Javascript で何とかなるんじゃない?jQueryとかすごい便利だし。」と思い、 …ハマりながらもうまくいきました。
# ちなみに HTA では Jscript (≒ javascript)もサポートしているので、
# jQuery が使えます。ラッキー。

Tablesorter

ということで、検索結果をソートするために Tablesorter を使いました。
Tablesorter は jQuery のプラグインです。一番基本的な使い方は "Getting Started" で示されているように
  • jQuery と Tablesorter の .js ファイルを読み込む
    • Tablesorter のサイトから両方ダウンロードできます。jQuery のバージョンにこだわりが無ければ揃えて入手できていいかも?
  • Table 要素に対して tablesorter() を実行するような Javascript を記述する
    • 特定の Table 要素をソートしたい場合は Table タグに id 属性をつけておくと良さそう
  • Table タグに class="tablesorter" を指定する(必要?)
  • Table タグの子要素として、ヘッダ行を thead タグで囲む
  • Table タグの子要素として、データ行部分を tboday タグで囲む
とします。

ただ、今回、ソートをしたいデータは VBscript で作成する(= 元のHTMLにはないデータ)なので、AJAXの際のプログラミングを参考にします。
http://tablesorter.com/docs/example-ajax.html
# Advanced example らしい…。
しかし、この例でも「空のテーブルが存在する状態」から始まっているので、table 要素全体を VBscript で生成しようとしている自分のケースにはそのままは使えません。
そこで、上記の例で出ている「あるIDの要素がクリックされたときに~」という記述を参考にして、「あるIDの Button 要素(「検索する」ボタン)がクリックされたときにtablesorter を実行する」というプログラムにしました。

VBscript で生成される前のHTML要素を javascript で扱えない(当たり前なんですが…)ことに途中で気づき、「どのタイミングでどのデータが存在しているのか?」を考えていたのが山場でした…。
jQuery で動的に要素を追加してると結構ハマるポイントのような気がします。

また、tablesorter は1行目の値を見て、「その列にどんな値が入っているのか?(文字列?数値?日時?)」を推測してくれるのですが、間違って推測することがあるので、自分で指定することにしました。
参考URL: http://mottie.github.com/tablesorter/docs/example-option-date-format.html

以下の javascript の "sorter" の部分が各列の型を指定している部分です(その前の 0: や 1: が「何列目」を示す 0-origin インデックス)。
$(function() {
  // call the tablesorter plugin 
  $("table").tablesorter({ 
 
    dateFormat : "mmddyyyy"// set the default date format 
 
    // or to change the format for specific columns, add the dateFormat to the headers option: 
    headers: { 
      0{ sorter: "shortDate" }// dateFormat will parsed as the default above 
      1{ sorter: "shortDate", dateFormat: "ddmmyyyy" }// set day first format 
      2{ sorter: "shortDate", dateFormat: "yyyymmdd" }  // set year first format 
    } 
 
  }); 
});
…ということで、VBscript で生成した表をソートするのに javascript が使えました、という報告です。

はじめてのあしば @rails3

rails new weblog と適当に作った勉強用の rails アプリケーションに scaffold を作っていこうと思います。
# なお、以前のエントリにあるように、この weblog では "rails g rspec:install" を実行済みです。
$ rails g scaffold post title:string body:text published:boolean createtime:date modifiedtime:date
$ rails g rspec:scaffold post 
…ひとまず、scaffold はエラーを吐かずに実行できました。

足がかりはできたので、DBを用意します。デフォルトの database.yml を見てみると

development:
  adapter: sqlite3
  database: db/development.sqlite3
  pool: 5
  timeout: 5000t
と sqlite を使うようになっています(= DBサーバの起動とか気にしなくてよい)ので、
$ rake db:migrate
やっちゃいます。うまくいっているみたいです。

サーバの起動もしてみます。
$ rails s -p 30080
openskip (= rails 2.3.5) では config ファイルにも port 番号を書いた気がしたんですが、今回の rails 3 ではコマンドでポートを指定するだけで良かったんでしょうか。
→ initial_settings.yml でググってみたら openskip の記事しか出てこない、ってことで特殊な事情だと理解しておきます。

さて、http://localhost:30080/posts でめでたく「はじめてのあした@rails3」がうごきました。

さて中身を見るのは…次にします。

2011年10月24日月曜日

Ruby のプログラムをほとんど書いたことが無いもので

Ruby の初歩的な文法やイディオムを知りません。

Rubyベストプラクティスを読んでいても「何だ、これ?」なことがたくさんあります。
この前書いた inject もそうでしたが、そのうち何回も出会いそうなので記録を残しておきます。

クラスのインスタンス変数

2章にこんなコードが出てきます:
class SortedList
  def initialize
    @data = []
  end
end
この時の @data のように冒頭に @ が一つついたものはインスタンス変数です。
Ruby でもこの用語でいいのかな?公式リファレンスに載ってた)

ちなみに

変数名の冒頭に @@ でクラス変数、 $ でグローバル変数、何もつけないのがローカル変数になります。
これで変数のスコープがおおよそ決まりますが、(特にローカル変数に関して)関数定義やコードブロックなどもう少し細かくスコープの区切りがある模様です。

変数の初期化

3章にこんなコードが出てきました:
@hidden_methods ||= {}
展開すると、意味が透けて見えてきます:
@hidden_methods = @hidden_methods || {}
Ruby の評価規則では、
  • 二項演算子 || は
    • 第一項を評価した値が nil または false の場合、第二項を評価し、その評価した値を返す
    • 第一項を評価した値が nil または false 以外の場合、第一項を評価した値を返す
  • 条件文を評価する際、nil または false 以外の値はすべて true と同等とみなす
となります。たぶん
なので、@hidden_methods が未定義である = nil である場合は、第二項の {} で初期化されます。たぶん

2011年10月23日日曜日

Ruby ベストプラクティス2章

Rubyベストプラクティスの2章はAPIの作り方ということで、
  • Rubyの関数・メソッドの引数の特徴
  • コードブロックの効果的な使い方
などを示しながら、API(≒ メソッドシグネチャ?)を自然に分かりやすく構築するコツについて書いてありました。

2.2節 Rubyの引数

Ruby では引数のデフォルト値を指定できる「オプション引数」を宣言できたりします。
def foo(bar="defaultvalue")
   ...
end
こうやって宣言した関数 foo は、 foo "hoge" とも foo (= foo "defaultvalue" と同じ)とも関数呼び出しができます。
ただ、オプション引数は複数存在するとその順番によって、省略できなくなってしまいます。

擬似的なキーワード引数

オプション引数とは別の話として、ハッシュを使った擬似的なキーワード引数の話が載っています。
キーワード引数の良いところは、順番に依存しない関数・メソッド呼び出しが行えることです。
「累乗を計算する power 関数は、第一引数が底だっけ?指数の方だっけ?」と迷うことなく
power({ :base => 2, :exp => 10 }) 
と記述することができ、うっかり 10の2乗を計算することを回避できます。

合わせ技

ハッシュによるキーワード引数とオプション引数(の考え方)とを組み合わせると、任意の場所の引数についてデフォルト値を定められる(= 省略可能にできる)関数やメソッドを定義できる、というのが 2.2 節のまとめです。

2.1節 統一された呼び出しインターフェース(API)

順番が逆ですが、2.2節で出てきたような引数処理のテクニックを利用すると、関数・メソッドを同一の呼び出し方法で異なる使い方が可能になる、という例を Table() メソッドを例に示しています。
異なる使い方:
  • hash データを引数として受け取り、それを解釈して、Table を構築
  • csv ファイル名を引数として受け取り、それを読み込んで、Table を構築
  • csv フォーマットの string を引数として受け取り、それを解釈して、Table を構築

2.3節 コードブロック

話題は変わって。

本の中でも「Enumeratable を使ったことがあるなら、自然とコードブロックを使っているはずだろう」と触れられているように、普通の Ruby プログラムであればコードブロックはいたるところに出てきます。
僕の中で一番最初に思いつくのは Array#each でしょうか。

この本では、コードブロックを使ってリファクタリング(共通部分の抽出)を行う例が挙げてありました。
元 Java の人としては、exec(...) を宣言した ISomeMethod インターフェースを実装するクラスを作って、そのインスタンスを対象のメソッドの引数として渡し、そのメソッドの中で exec(...) を呼び出す形に対応付けて覚えることにしました。

2.4節 Rubyらしいコード

また、話題は変わって。

「Ruby らしいコード」になるためのテクニックをいくつか紹介しています。

attr_*

Ruby のクラスにクラス変数 ( @varname ) を定義するとき、たいていの Rubyist は(Javaプログラマと違って)
class Message
  attr_accessor :message

  def initialize(m)
    @message = m
  end
end
と書くそうです。
こう書けば、(本来カプセル化されてアクセスできないはずの)Message#message を参照・書き込みできます。
読み出し・書き込みのどちらか一方だけ自動生成したい場合は、attr_reader / attr_writer があります。

メソッドの後ろの "!" と "?"

Ruby の「暗黙のルール」として、
  • methodname? は bool 値を返す
  • methodname! は「注意するべき操作」を示している
    • 一般的には、破壊的操作(元のデータを書き換える操作)を示していることが多い
    • 逆に ! のついていない対応する methodname は「(元のデータそのものではなく)コピーされた値を処理する操作」
というのが慣習になっています。

演算子もメソッド

つまり、再定義によるカスタマイズが可能、ということです。 + や << などで表現できる「操作」があれば、それをカスタマイズされた演算子として定義するとコードがすっきりしそうです。

おまけ:inject

2章の途中で、(かなり嘘が入っていますが、ざっくり言うと)こんなサンプルプログラムが出てきます。
a = [ 1, 3, 4, 5, 7]
a.inject(0) { |sum, element| sum + element }
# => 20
あまり Ruby の経験のない自分ですが、このコードが異様なのは何となく分かります。「sum って一体どこから出てきたのだろう。どうやら要素の足し算をしているようだけど…」

inject について調べてみたら、関数型言語で言うところの fold 関数であるらしいことが分かりました。
fold 関数については関数型言語好きな後輩から「foldはすばらしいですよ」と解説を受けたことがあったので、…存在だけは知っています(ごめんなさい、自信を持って理解してるって言えません)。「畳み込み」っていう言葉もイメージだけはできている気分です。

僕の中では、(配列・リストの)要素に対して構文木を作るイメージなんですが、それで伝わる人ってどのくらいいるんでしょうか…。

2011年10月22日土曜日

ralis3 + rspec2 (の3歩手前くらい…)

rails 2.x と rails 3.x では、使い勝手(コマンド)がかなり違っているようです。
それに対応する rspec についても使い方はひどく違っています。

まず、
$ rails new weblog
として新しい rails プロジェクトを作りました。
「ruby なら TDDでしょ」ってことでまず rspec を用意しようと思い、rails プロジェクトの Gemfile に
# Rspec for Test Driven Development
group :test do
  gem "rspec", "2.7.0"
  gem "rspec-rails", "2.7.0"
end
「よーしこれでOKだ」と思って
$ rails generate rspec:install
としたら、
/usr/local/rvm/gems/ruby-1.9.2-p290@rails3/gems/execjs-1.2.9/lib/execjs/runtimes.rb:47:in `autodetect': Could not find a JavaScript runtime.
See https://github.com/sstephenson/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable)
書いてある通り、 javascript runtime が無いようです。
https://github.com/sstephenson/execjs
には therubyracer が一番最初に書いてあります。
gem list therubyracer --remote してみたらパッケージがあるようなので
$ gem install therubyracer 
でインストール。さらに Gemfile に
# Javascript runtime
gem "therubyracer"
と書いて rspec:install にリトライ→成功!
…ふぅ。

2011年10月21日金曜日

Rubyベストプラクティスを読み始める

O'Reilly から発行されている Rubyベストプラクティス を読み始めました。
ちょっとでも Ruby プログラマの気持ちが分かれば、と考えています。

ひとまず1章でTDDな気持ちをちょっとだけ学びましたが「テストのために本体のコードがかなり歪んでないか?」って気分もしてます。
この辺りはバランスなんでしょうか。それともテストのために全てを犠牲にするのがTDDってもんなんでしょうか。
まだまだ Ruby プログラマの気持ちには程遠いようです。

ちなみに、「ベストプラクティス」がカタカナなことからお分かりのように日本語版を読んでおります。
こういうものは原著で読むべきだと思っているんですが、プログラムの多い本ほど日本語でもいいかなぁ…と。

2011年10月20日木曜日

ブログのタイトル

そのうち Reckless Novice が Rational Neet を経て、Reliable Nomad になれるといいな、と考えています。

そんな妄想。

2011年10月19日水曜日

RVM を使って複数の Rails を使い分ける

Ruby/Rails の学習題材に openskip を選んだために、gem でインストールした rails のバージョンは 2.3.5 です。

どうせ勉強するなら最新の rails 3 での挙動も並行して勉強したいところです。そもそも仮想ゲストOS上の環境なのでもう一つ作るっていうのもありなんですが、手間がかかりそうとかリソースが足りなさそうとか思うと、現在の環境に複数バージョンを共存させたくなってきました。

そんなこんなで調べていたら rvm (Ruby Version Manager)というのを見つけました。

インストール

基本的に以下の文書にしたがって(略
http://beginrescueend.com/rvm/install/

まずは download & install :

$ sudo bash <<(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer )
これだけで /usr/local/rvm/ 以下にインストールが完了します。
また、インストールの際のログの中に
NOTE: To all Multi-User installers - DO NOT forget to add your users to  the 'rvm' group.
という注意がありました。「Linuxのグループをプロジェクトごとに使う」っていうポリシー自体は聞いたことがあったんですが、こういう形で使うのは初めてです。 ひとまず、自分のユーザを rvm グループに追加しました。
$ sudo usermod -a -G rvm kazu
あと、「Rubyの最新版を入れるんだったら…が必要だよ」とたくさん yum のパッケージが列挙してあったので、必要になるか微妙だったんですが、足りていなかった libyaml-devel, libffi-devel をインストールしました。

Ruby 環境の作成


以下のページを参考にさせてもらいました。
http://www.machu.jp/diary/20110521.html

rvm では
  • system (rvm の管理外のsystem標準の環境: OSパッケージのrubyやgems)
  • rvm 以下に複数のバージョンの ruby 
    • 各rubyのバージョン以下に(独立した)複数の gemset
というかたちで環境を作っていきます。

とりあえず(1.9系の良さを知らないもので…)1.8系で満足ですが、この際 1.9 も用意しておこうと思います。ということで、rvm にて、
$ rvm install 1.8.7
$ rvm install 1.9.2
で1.8系および1.9系をインストールしておきます。
(どんなバージョンが利用可能かは "rvm list known" で確認できます)

さて。
この状態だと、rvm管理下の 1.8.7 / 1.9.2 の2つの環境、およびOS標準(system)の合計3つの Ruby 環境があります。
これを切り替えるのには rvm use を使います。
1.8.7 環境に切り替える:
$ rvm use 1.8.7
元のシステム環境に戻す:
$ rvm use system

次は、"rvm use 1.8.7" を行った(一旦 rvm 環境下に入った)上で、gemset の作成に移ります。
$ rvm gemset create openskip
$ rvm gemset create rails3
作成したら rvm use コマンドで gemset を含めた指定を行います。
$ rvm use 1.8.7@rails3
ここで gem list コマンドを使ってみると空っぽの gem 環境になっていて、以前インストールした gem とは切り離されていることが分かるので、望みの gem を揃えていきます。
$ gem list

*** LOCAL GEMS ***

$ gem install rails3
これで複数環境を使い分けられそうです。

(追記: 2011/10/22)
openskip の環境を system じゃなくて ruby-1.8.7@openskip として作ったときに

/usr/local/rvm/gems/ruby-1.8.7-p352@openskip/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:55: uninitialized constant ActiveSupport::Dependencies::Mutex (NameError)
というエラーに見舞われました。
エラーメッセージをググったら、特定の rails のバージョンと rubygems のバージョンとの組み合わせで起こるエラーのようです。
http://blog.digital-squad.net/article/196968134.html

rvm の ruby-1.8.7@openskip 環境で、
$ gem -v
1.8.10
$ gem update --system 1.3.7
ちなみに gem のバージョンは < 1.6 であればいいようなんですが、OS標準の gem と同じバージョンにしてみました。

2011年10月16日日曜日

openskip の環境構築(の途中)

なかなか壁が高いです。

基本的には github 上の文書にしたがって作業しています。
https://github.com/openskip/skip/wiki/_pages

必要な gems がインストールできない

gems のインストールで一悶着ありましたが、CentOS の ruby-devel パッケージを入れ忘れていただけだと分かり、そこはすんなり解決しました。
# rubygems の基本とか仕組みとかを理解しないときついな…。
# 複数パッケージの共存とか名前空間の概念とかがどうなっているんだろうか。

DB作成でこける: gemsまわりが原因

さらに、db:migrate にて、
undefined method `set_app_language_tags' for I18n::Locale:Module
というエラーが出たのですが、同様のエラーが locale_rails の方に報告されていました。
https://github.com/mutoh/locale_rails/issues/2

github 上の最新のソースコード(June 15th, 2010 以降のもの。ver. 2.0.5 は November 2009 なのでダメ)の i18n.rb に入れ替えると pass しました。

…と思っていたのですが、Openskip のコミュニティページの公式解法としては、(rails3 の?)依存関係のせいでインストールされる i18n という gem をアンインストールする、という方法らしいです。
https://www.youroom.in/r/openskip/entries/216558

ひとまずエラーが解消されたので、一歩先に進んでみることにします。
# やっぱり名前空間とかそういうところの勉強をしないと解決できない予感。

一通りの手順を実施できたと思ったら…

webrick を起動させて、手順に出てきた「初期管理者の作成URL」にアクセスしたら internal server error。

コンソールに表示されている webrick のログを見たら、haml が sass を使っている部分でエラーになっていました。
どうやら gem でインストールされた haml のバージョン(3.1.3)が異なるせいなのか、sass がインストールされていませんでした。

gem install で sass (3.1.10) をインストールした上で、openskip の config/environment.rb (66行目?)に
config.gem "sass", :version => '3.1.10'
を追加したら、管理者の追加とその後のログインまでうまくいきました。

さらに落とし穴…


うまくいったので、ログインしてからブログ投稿などを試して見ようと思ったらエラー。

ログを見たら i18n の文字が見えたので「やっぱりダメだったか…」と思い直して、openskipのサポートルームに載っていた teru さんのブログを参考に i18n, mail, actionmailer (v 3.x.x), actionpack (v 3.x.x), activemodel (v 3.x.x), activerecord (v 3.x.x), activeresource (v 3.x.x), activesupport (v 3.x.x) あたりの gem を外してみました。
http://d.hatena.ne.jp/teruzoh/20101228/1293508965

ということで、やっとまともにうごいた!

openskip のソースコードを読んでみる(の前に?)

勉強を始めたばかりのくせに最近サボりがちだったので、今日は取り戻す勢いで。

でも、Rails の基礎が分かっていないので、どのソースコードが肝なのかは手探りです。
…まずは spec から読んでみるかな、という気分です。

rspec が gem でインストールされていなかったので、インストールします。
$ gem install rspec --version 1.3.0
$ gem install rspec-rails --version 1.3.2
rspec-rails 1.3.2 をインストールすると(依存関係があるわけでもないのに、なぜか)rspec-core, rspec-expectation, rspec-mock がインストールされるので uninstall しておきます。rspec-rails 2.x には確かに必要なパッケージのようなんですが…。

あとで分かったのですが、rspec はバージョン 1.3.x と 2.x で全く別物で、
  • 1.3.x が rails2.x 向け
  • 2.x が rails3.x 向け
という使い分けがあるとかないとかそういう風に見えました。




…さぁ、まだ Ruby にはちっとも触れてないぞ、っと…。

CentOS on VMplayer のスペック

特にご興味な李かもしれませんが、仮想OS が VMware player 上でストレスなく動作しているようなので参考値ということで晒してみたいと思います。

まず先に手元のPCのスペックですが、
  • Thinkpad X201i 
    • CPU: intel core i5 M450 (2.40 GHz)
      • 論理プロセッサは 4 = 2個の物理コア x ハイパースレッディング
      • 以前の投稿でも触れたように、BIOS で intel VT は有効にしています
    • memory: 4GB
という感じです。この上で仮想ゲストである CentOS に
  • 論理CPU: 2
  • memory: 2GB
とそこそこたっぷりと思えるリソースを分けました。ほぼ問題なく動作していると思います。

ホストPCの半分くらいのリソースを明け渡しましたが、きっと Linux を扱っているときは Windows 側でそんなに仕事してないだろう、という予測の結果です。
ただ、Linux 側の Skype の調子が芳しくありません。手元に Bluetooth ヘッドセットがあるのですが、ゲストOS側の Bluetooth デバイスの認識機能は働いていそうなものの、接続して利用できる状態にはならないようです。
ゲストOS側で独立して接続するのだからマルチペアリングに対応していない機器だといけないんでしょうか…。

当分は Windows 側で Skype しながら Linux 扱うことになりそうです。

Openskip を github から拾ってくる

「rails アプリケーションの教材」として、 github 上のプロジェクトの中から(個人的には自然な流れで)openskip を選びました。

環境構築の方法が README に書いてあるのですが、これは環境を作るのにまた1日かかりそうです…。

2011年10月15日土曜日

github も使ってみる

せっかく git を使うなら github を使ってみようということでアカウントを取りました。
ひとまず linux マシンで使うので、

http://help.github.com/linux-set-up-git/

このあたりを見て、ssh key の用意とテストログインくらいはやってみました。

あと、次の投稿のネタになると思いますが、 github 上のレポジトリを fork しました。
github のレポジトリの fork に関しては
http://help.github.com/fork-a-repo/
こっちの文書を参考にしています。

git の本当に基本的な使い方

git を使うことにしました。

でも基本の使い方も思想も理解していないので、メモを残しておきます。

自分のソースコードを git の管理下に置く場合

ソースコードを置いている場所を /path/to/mycode とすると、
$ cd /path/to/mycode/
$ git init
とすることで、git レポジトリとなるように .git ディレクトリを作成します。

他所からソースコードを持ってくる場合

例えば、他の git で管理されたディレクトリや github からソースコードを持ってくる場合は、git cloneを使います:
$ cd /path/to/base/
$ git clone <source of .git file>
clone コマンドを使うと、baseディレクトリの直下に git プロジェクトの名前でディレクトリを作ってくれます。

その他の使い方

は、そのうち…。



Ruby 開発環境

やりたいことは Rails 開発なので、必要なものはそこそこあります:
  • ruby :  OSインストール時に入ってた
  • rubygems : yum install rubygems 
  • rake : yum install rake
  • emacs の整備
    • term/bobcat を有効にする
    • ruby-mode : これもOSインストール時に入ってた
    • elscreen : 開発サイトから拾ってくる
    • apel : elscreen に必要だったのでこれも拾ってくる
rails を導入するために gem でいろいろインストールしてたらエラーになりました。
header が見つからないということなので、どうやら CentOS の ruby-devel が入っていない、
ってことで
  • ruby-devel : yum install ruby-devel
  • 各種 gems
を導入。

2011年10月14日金曜日

CentOS 6.0 on VMware Player on Windows

こういう表現であっているのかわからないですが、手持ちのWindows PCにVMware Playerをインストールして、その上で CentOS 6.0 を動かしています。

画面描画が遅い気がする

ターミナルとemacsを動かす分には(= キーエコーが表示されるだけなら)そこそこ快適に暮らせますが、Firefoxの動作とかウィンドウの描画とかになるとちょっと辛そうです。

手持ちのPCは Thinkpad X201i なのですが、どうやら BIOS で intel VT が disable になっているようです。
一旦電源を落として BIOS 上で enable にしてみたら若干改善された気がします。

英語キーボード

個人的な趣味で英語キーボードを使っています。
ただ、キーボードでチルダやシングルクォートを入力しようとすると問題があることに気づきました。

同じような苦労をされている方を発見しました:
http://d.hatena.ne.jp/drambuie/20110603/p1

僕の場合はキーボードの設定を Generic 101 に変更すると良くなるようです。

はじめてのとうこう

初心者のくせに、Rubyエンジニアとして頑張っていこうと一念発起した人のブログです。

まず最初に

Rubyの開発環境が必要です。

ただ、WindowsではRubyの開発環境をそろえるのが難しいです(特に Rails の環境まで考えると…)。
ということで、 慣れ親しんだ Linux 上にRubyの開発環境を構築することにします。

VMware Player のインストールとCentOSのインストール

VMware Player を公式サイト http://www.vmware.com/go/get-player-jp からダウンロードして、exeをダブルクリック→インストール完了。

CentOS はミラーサイトから適当に version6.0 の netinstall 用isoをダウンロード。
僕は IIJ さんのミラーから入手しました。http://ftp.iij.ad.jp/pub/linux/centos/6/isos/i386/

  •  VMware player を起動して「新規仮想マシンの作成」を選択
  • 仮想マシンのハードウェアスペックを適当に決める。ただし、
    • 「CD/DVDドライブ」として、さっきダウンロードした iso ファイルを指定 ← 次回起動時にこのisoを読み込むので centos のネットワークインストールが開始される
  • 仮想マシンの作成を完了させて、さっそくマシンを起動すると iso ファイルを読み込んで centos のインストール開始
  • centos のインストール手順は割愛
    • まぁ適当に。
centos のインストールは大筋で成功したので、早速環境構築…、と思ったのですが若干力尽きてきました。

  • vmware-tools のインストール
  • sudoers の設定: wheel グループに対して許可
  • 自分のユーザアカウントを wheel グループに追加
  • emacs の設定
    • term/bobcat ただし emacs22 以降だと (load "term/bobcat") だけではダメらしい
    • elscreen …が見当たらない。拾ってこよう。