スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Railsを試す 続き

さて、前に簡単にチュートリアルを試してから、自作で簡単なアプリを作った。

Javaでも作ったTodoリストをRailsに置き換えてみようと思った。
それにしても、他のチュートリアルや他の人にサイトにもあるのだが、TodoListってHello Worldに続く基本なのだろうか。私が作ったのは専ら自分で実際に使うためだったが。
今回は他の人のは参考にしないで自分独自に進めた。

Cookbookのチュートリアルである程度基本はつかめたので、あとはやりながら学んでいけばいいだろうと思ったが、それがよくなかった。こういうとき、どうしたらいいのだろうかというのが簡単にわからない。Rubyの習得もままならないままだったので、生産性はさほどよくない。

Webではチュートリアルは多いものの、体系立って説明しているものはない。パラメータとかセッションとかどういう仕組みになっているのかよくわからない。個人の日記などは参考になるが、しばしば情報が古かったりする。なので、Googleであれこれ探しながら問題を解決。

やはり本を買え、ということかな。きちんと勉強してから取り掛かったほうがいい。

以下、気づいた点、詰まった点。

1.テンプレート
 ruby script\generate scaffold tbl
 のコマンドでscaffoldを基にしたファイルを出力してこれを元に修正したのだが、あとからtableタグをすべてに仕込んだので効率が悪かった。テンプレートを修正してから実行すべきだった。

2.文字コード対策
application.rb
        before_filter :set_charset
        protected
        def set_charset
                @headers["Content-Type"] = "text/html; charset=Shift_JIS"
        end

3.結構型付けが厳しい(Ruby)
    <option value="1"<%= " selected" if @preference.finished == '1' %>>済み</option>
@preference.finished == 1ではだめだった。

4.dateのformatを変えたい
デフォルトでは、Sun Mar 26 12:00:45 東京 (標準時) 2006などと表示される。
environment.rbに以下を加えてフォーマットを変えることができる。
::ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.update :default => "%m/%d %H:%M:%S"

5.validation
validateは変換された後に行われるからなのか、intのフィールドに文字列を指定しても引っかからない。文字列を入力すると0として扱われている。しかしvalidationで戻される値はそのまま文字列となっている。

6.検索ロジック
作ったオブジェクトは、todo, state, categoryの三つ。todoの検索条件にpreferenceオブジェクトを用いて、これはテーブルに一行だけ保存する。パラメータでは検索条件が、preference[category_id]などとして渡すようにした。
最初にテーブルから一行取り出し、パラメータにあるもので上書きし、保存し、そこからConditionを生成して、todoリストを取得する。

  def list
    @preference = Preference.find_first
    
    if params[:preference]
      params[:preference].each {|x|
        @preference.send('' << x[0] << '=', x[1])
      }
      @preference.update
    end

    condition = '1=1'
    @preference.attributes.each {|x|
      condition << ' AND ' << x[0] << '=\'' << x[1].to_s << '\'' if x[1] != nil && !['id','create_dt','update_dt','finished','sort'].include?(x[0])
    }
    
    ids = []
    State.find(:all, :select => 'id', :conditions => 'finished =' << @preference.finished).each {|x|
      ids << x.id
    }
    condition << ' AND state_id in (' << ids.join(',') << ')'

    p condition
    @todo_pages, @todos = paginate :todos, :per_page => 10, :conditions => condition, :order => @preference.sort

    @categories = Category.find_all
  end


rubyのスタイルは、javaとは違うし、またいろいろな書き方ができる。これよりももっとスマートな書き方もできると思う。習熟するまで結構時間がかかるような気がする。
railsを使うと記述量が少ないので生産性が高いといえるかもしれないが、習熟したとしても、せいぜい1.x倍ぐらいじゃないかと思う。宣伝文句である「10倍」はscaffoldをそのまま使うだけでよい場合だけだろう。

以下、参考サイト
tag[ rails ]
Find the Plural of a Word (Rails 1.0.0)
優しいRailsの育て方
Ruby on Rails の Wiki
masuidrive on rails
オブジェクト指向スクリプト言語 Ruby リファレンスマニュアル

スポンサーサイト

J2EE勉強会 第17回

今回も先月に続いてJ2EE勉強会に参加した。
なかなかマニアックな人が多く、知らない用語、いろんな新技術・流行が出てくるので、刺激になるものの、今回もちょっと縮こまってしまい、発言は慎んだ。少し慣れが必要か。

今回のテーマは、
・Apache Geronimo
・Shale (Struts 2.0)
・Yahoo Design Pattern Libraryの紹介
だった。

Geronimoについては、Geronimoそのものに深く入っていかなかったものの、太一さんのソースコードの読み方については非常に参考になった。遠近法という命名がなかなかいい。あと、全体を把握し、実装の境界を知るというのも勉強になった。

私はソースコードを読むとき、骨子となる部分、あるいは自分が知りたい部分についての、呼び出し階層を明確にするために、その部分を抽出して、エディタに貼り付けて、その構造を頭に入れるようなやり方をしていた。

やはりソースコードというのはいい。ソースを見て初めて底辺に触れたという感じがする。UMLで骨子をつかんだとしても実感がわかない。まあ、そもそもなぜかオープンソースのプロジェクトにはほとんどUMLの資料が見つからないことが多いのだが。これだけUMLが騒がれているのに。

Shaleについては、前回と引き続きだったが、Clay(Tag Component)とDialog(Web Flow)という2大特徴の解説。

一番広まっているフレームワークの次世代版なので注目していきたいところ。私は正直、タグライブラリ不要派なので、またWeb Flowなんて必要なのという感じなので、ちょっと懐疑的に見ている。
単にXML地獄に突き進んでいる感じ。RailsやSeasarなどの設定ファイルを不要にする方向性と正反対だ。設定ファイルが必要なのかどうかというのは今度議論してみたい。

Web Design Patternについて。
これまた興味深かった。これが広く認知されるようになると、客の側から、この画面ではこのパターンを使って、という要求が出てくるようになるのだろう。

cf.
Designing Interfaces
Yahoo Design Pattern Library

純オブジェクト指向Webアプリケーション with Java

Railsで簡単なアプリを作ろうと思っていたが、先にJavaでも簡単にアプリケーションを作れることを示そうと思い、タスク管理のアプリケーションを作ってみた。

ORマッピング不要、データベースを使わないアプリケーション。全く大したものではないが、超小規模ならこれで十分。データはデータベースに保存せず、オブジェクトをそのままシリアライズしてファイルに保存する。なので、Tomcatがあれば、後は何のライブラリも加えることなく、データを扱うアプリケーションができる。(ランダムアクセスファイルではないので、メモリ上にすべて取り込まれ、変更の際もすべて出力されるので大量データには向かない)。

画面は2画面しかない。リスト表示画面と編集画面。これがリスト表示画面。
一覧に、タスク・日数・期限・優先度・ステータス・カテゴリ・作成日・更新日・備考からなるタスクのリストが表示される。列を選択してソートしたり、条件を指定してフィルターをかけることができる。これはすべてクリックリンクでjavascriptで即座に反映させる。


これが編集画面。ここで登録・更新・削除を行う。既存のタスクを元に新規にタスクを作ることもできる。

最初は、JSP+データクラスだけで作ったが、作りが悪いのでリファクタリングし、MVにし、そしてJSPからコントローラを分離して、MVCにした。また汎用性を高めるために、データの読み込み・出力は専用のManagerクラスを用意し、類似のアプリを簡単に作れるフレームワークにした。まあ、フレームワークというほど仰々しいものではないが。またシングルユーザのみ対応だったので、マルチユーザに対応するようにした。

これがクラス図。

まずTomcatの起動時にInitServletがRepositoryManagerをロードする。このServletはそのためだけに存在し、他では使用されない。RepositoryManagerは、オブジェクトを保存するパスやバックアップ数、リサイクルタイミングなど永続化に関する設定情報をweb.xmlから得る。

JSPはlist.jspとedit.jspとexecute.jsp。各JSPは、コントローラーであるTodoActionのインスタンスをセッションから取得する。TodoActionは、ユーザごとのTodoRepositoryオブジェクトを得る。これがModel。ユーザはBasic認証で認証するので、あらかじめtomcatのUserDatabase.xmlに登録しておく。

TodoRepositoryは、Todoオブジェクトのリスト他、リストの表示方法に関する情報を持つ。Todoオブジェクトがタスク1つに対応する。オブジェクトのソートはComparetorを使っている。

各JSPからTodoActionの相応メソッドを呼び出し、処理後、表示のためにTodoRepositoryやTodoオブジェクトを得って、JSP上にセットする。JSPではActionのメソッドの呼び出しとデータの取得以外、一切ロジックは書かない。
ただリダイレクトはJSPに書く。なので、strutsのActionServletのような中央集権的なコントローラはなく、画面遷移の制御はJSPで行う。

Utilクラスは、文字列の変換等のユーティリティを集めている。

簡素な作りのため、まだまだ修繕の余地はある。エラー画面も用意していない。validationはjavascriptで行っている。発展させていくとシンプルさは失われていく。

アプリの機能としても、ソートを逆順にしたり、ステータスを別個登録できるようにしたりとかいろいろ考えられる。

オブジェクトをそのままファイルに出力するのではなく、CSVやXMLでエクスポートしたい。クラス図ではその手のメソッドを書いたがまだ実装されていない。XMLとオブジェクトを交互に変換するには、Quickという便利なライブラリがある。使い方はここに書いてある。結構簡単そうだが、しばし時間が必要。

階層化されたデータを扱う

階層化されたデータをMySQLで扱うオレンジニュースからの技術ネタ)より

リレーショナルデータベースは階層化したデータ用に開発されなかったと考えたと思います。リレーショナルデータベースのテーブルは階層化されておらず(XMLのように)単に平たいリストです。階層化されたデータは親と子供の関係を持っており、リレーショナル データベースのテーブルでは自然に表すことができません。

とあるが、若干異論がある。RDBでも階層構造を格納することはできる。ただそれを階層構造として表示するのが厄介なだけである。

例えば、グループがサブグループを含んでいる場合、

グループ1
 グループ2
  グループ3
 グループ4
グループ5
 グループ6

これをXMLで表現すると綺麗にこのとおりになるが、RDBだと、

グループ1 0
グループ2 1
グループ3 2
グループ4 1
グループ5 0
グループ6 5

という形で、親へのポインタを別列に持つことになり、これゆえ平べったいと言われるのだろう。
だからといって、XMLのようにツリー構造であることが常によいとは限らない。この階層が無限に連なった場合、全グループを並列に並べようとしても下層まで追っていかないといけない。一方、RDBならその列を見るだけですべて取り出せる。この場合は表形式の方がシンプルだ。

ただRDBの場合、表からツリー構造を表示するとなると厄介だ。

先のリンクページでは、MySQLでどのようにするかが述べられている。

SELECT t1.name AS lev1, t2.name as lev2, t3.name as lev3,
t4.name as lev4
FROM category AS t1
LEFT JOIN category AS t2 ON t2.parent = t1.category_id
LEFT JOIN category AS t3 ON t3.parent = t2.category_id
LEFT JOIN category AS t4 ON t4.parent = t3.category_id
WHERE t1.name = 'ELECTRONICS';


このような感じで、JOINをレベル数分だけ使う方法(Adjacency Listモデル)が示されている。しかしこの場合、あらかじめレベルがわかっていないとできないし、数十レベルになったとき筆者が述べているとおりパフォーマンスの問題が出てこないか心配になる。

そして別の方法が示されている。「ネストしたセット・モデル」と書かれていたが、これはツリー構造を一筆書きで追っていって、そのノードの右と左に番号を振っていくやり方で、右番号と、左番号が各ノードに付与される。そのメリットが延々と書かれていた(全部追っていくのは大変orz)。
どうやって先の方法と同じ階層表示ができるのかは原文の方に書いてあった。

SELECT CONCAT( REPEAT( ' ', (COUNT(parent.name) - 1) ), node.name) AS name
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
ORDER BY node.lft;


私はMySQLでこの階層問題の処理をしたことがあるが、ツリー構造の作成は、プログラム上で行った。
1.まずルートノードを探す(複数の場合あり)
2.その各ノードを親に持つ子ノードを探し子ノードリストに加える。
3.2を再帰的に繰り返す。またその際深さレベルもノードの属性にセットする。
という感じだった。

Oracleでは、SQLの抽出結果を階層構造で表示するテクニックという便利なものがある。

残念ながらMySQLにはないと思う。
こんな感じだ。

GRID GRNM PGRID
-- --------- ---
0 管理者 0
1 グループ名1 0
2 グループ名2 1
3 グループ名3 1
4 グループ名4 1
5 グループ名5 2
6 グループ名6 2
7 グループ名7 2
8 グループ名8 3
9 グループ名9 3
10 グループ名10 5

こういうテーブルM_GROUPがあったとする。
そこで下記のSQLを実行する。

select
  grid, level,
  lpad(' ',2*(level)) || grnm grnm
from
   m_group
start with grnm = '管理者'
connect by prior grid = pgrid;

ERROR:
ORA-01436: ユーザー・データでCONNECT BYのループが発生しました。

Oops!!!! 管理者のGRIDが0で親GRIDも0なのでループだといって怒られてしまった。
なので、
update m_group set pgrid=null where grid=0;

GRID GRNM PGRID
-- --------- ---
0 管理者
1 グループ名1 0
2 グループ名2 1
3 グループ名3 1
4 グループ名4 1
5 グループ名5 2
6 グループ名6 2
7 グループ名7 2
8 グループ名8 3
9 グループ名9 3
10 グループ名10 5

として、これで再度先のSQLを実行。

GRID      LEVEL GRNM
-- ------ ------------
   0          1   管理者
   1          2     グループ名1
   2          3       グループ名2
   5          4         グループ名5
  10          5           グループ名10
   6          4         グループ名6
   7          4         グループ名7
   3          3       グループ名3
   8          4         グループ名8
   9          4         グループ名9
   4          3       グループ名4

今度は綺麗に行った。何て便利なんだろう。MySQLでやったときのような小ざかしいプログラムを書く必要はなかった。

とはいえ、もしこれを使わず、Stored Procedureで処理したらどうなるだろうかと思った。方法としては、テーブルをごそっと持ってきて、先に述べたような手順でプログラム上で処理するか、あるいはSQLを使ってとなるが、これはあまり綺麗にできそうな気がしない。
どちらにせよ再帰関数が必要だが、ストアドで再帰的な関数は使えるだろうか。私の知る限りのPL/SQLでは原始的過ぎて、ちょっと面倒だ。ストアドの中でSQLを使わないなら、プログラムの中でやった方がずっと楽な気がする。

増補改訂版Java言語で学ぶデザインパターン入門マルチスレッド編

『増補改訂版Java言語で学ぶデザインパターン入門マルチスレッド編』無料プレゼント
だそうです。
応募すると当たるかも。

Ruby on Railsを試す 2

昨日に引き続き、Railsを使ってみた。

一度すべてアンインストールして、Instant RailsでまずはTutorialのとおりに動かして、動作を確認した。

それから、前に失敗した、bookmarkアプリの作成の手順を実行してみた。
すると、ブラウザでアクセスすると、

Mysql::Error: Lost connection to MySQL server during query

とのエラーが出て全く進まない。ユーザIDもパスワードも合っている。
Googleで検索すると、たくさんヒットする。BugだのMySQLのバージョンだの、MySQL gemだのいろいろ原因が出てくるが、どれもしっくりこない。Instant RailsでMySQLもすべてセットになっているのにおかしいなあと思った。
添付のcookbookではうまくいっていたので、それと比較した。いろいろ試した結果、rootだとうまくいき、作成したユーザだとうまくいかないことがわかった。MySQLを別のマシンにして接続すると、rootでなくてもうまくいく。なぜだ?

結局、passwordの設定が正しく使われていないんじゃないかという気がする。passwordを空にしてやると、きちんと接続できた。まあ、rootでもpasswordなしでもとりあえず勉強するだけだからこれでいいか。

生産性が10倍に上がるフレームワークで、勉強の生産性は落ちまくりだ。
まあ、なんでもそうだが、最初の環境を構築するところが一番大変だ。

Ruby on Railsを試す

Railsが注目を集めるにつれ、私も無視できなくなってきた。
先日も、Railsでこんなアプリが、Railsを初めてダウンロードしてから4時間でできましたとかいう話を聞いたりして、とにかく使ってみないことには、と思った。

JavaによるWebアプリケーション開発よりも生産性が10倍と謳われている。.NETでも確かそんなことを聞いたが。.NETはWebのViewの部分で生産性が優れていることのようだが、Railsでは、DBとの連携部分における生産性が高まっている。

データを中心に扱うアプリケーションにとって、アプリケーションのパターンは決まっている。データに対して参照、検索、作成、更新、削除を行い、対応した画面を作るだけ。ほとんど同じようなパターンなのになぜそれぞれ別個に作成しなければならないのか。

オブジェクト指向を使っても、また同じものは二度と書かないという上級プログラマが守るべき規則に従ったとしても、似たようなオブジェクトは乱立することになる。扱うデータが違うだけでやっている処理は似たようなものなのに、いちいち全部クラスを作成しなければならない。
Railsはこういうところの重複も排除しようとしたものだと思う(まだ勉強中だが)。

Railsが高い生産性を実現しているのは、Conventions over Configurationという考え方だ。クラス名やテーブル名が命名規則に従っていればフレームワークが自動的に処理してくれるというものだ。自由度は制限されるものの、名前だけで処理を記述するのと同じことになるのでコーディングはかなり楽になる。
このCoCは、Seasarプロジェクトでもよく言われている。S2DAOでもRailsがやろうとしていることはやっているという。

Ruby on Rails、略してRORと呼ばれるが、Ruby言語と相性がよかったというだけで、思想としてはRubyとは独立しているので、別の言語にも移植可能だ。実際、JavaではTrails(Rails on Java)があり、PHPにもRailsライクなフレームワークがある。
ここにはC# on Railsをどう実装したらいいのかと、いろいろな考察がある。

と前置きはいいとして先日早速試してみた。
10分で作るRailsアプリ for Windows
のページに従って、bookmarkアプリを作ろうとしたのだが、どういうわけか、WEBrickを起動してブラウザでアクセスしてみると、newやedit画面でテキストボックスが表示されない。MySQLにデータを投入してみると、リストや参照画面では表示される。なぜだろうと思ってGoogleってみたが、私と同じような経験をしているのは一人だけで、それに対するレスポンスはテンプレートを作る必要があるとのこと。scaffoldというのなら表示されてもいいはずだが。

ここで門前払いを食らってはと、InstantRailsをダウンロードしてきて、とりあえずcookbookのサンプルを動かしてみた。しかしこれアーカイブが37Mもあって、中にはなぜかPHPも入っている。
この続きは明日やってみたい。

ソフトウエアテストについて

今日MCEAの技術者交流会で、テストについての討論があった。残念ながら私は用事があったため開始早々に退出することになったのだが、配布された膨大な資料を電車の中で読みながらふと思ったことがあった。

・開発とテスト
まず一般的に言われているこの区分、はっきり言って奇妙だと思う。
確かにソフトウエアを開発するとき、コードを書いてテストするという流れだ。しかし開発者はテストしないのだろうか。コード書いて終わりなのだろうか。単体テストに限らず、結合テストについても結合部分について必要なコードは記述するはずだし、その部分についてはテストするはずだ。にもかかわらず、別フェーズでテストが必要というのは、単に開発者がテストしていないだけなのではないか?

・仕様書(設計書)とテスト仕様書
この区分も辺だ。確かにテスト仕様書の方は、チェックリスト形式になっており、仕様書の方は読み物形式になっているという形式的な違いやテスト仕様書で手順の記述が重複しているというのはある。しかし内容は、(仕様書に書かれる付随説明は除き)、同一であるべきだ。なぜなら仕様書に書かれていることはすべて実現されているかチェックする必要があるからだ。もし逆にテスト仕様書の内容の方が詳細であるなら、それは仕様書が不正確であることを意味する。

・テスターの役割・目的
テストを取り扱った論述は、テストが軽視されていると言う。確かにそのとおりで、私もテスターという「マイナー」な仕事はしたくない。しかし、一括りに「テスター」と言っていいものか? テスターにはスキルが必要だと言っていいものか?

テストには目的がある。
1.バグの検出
2.動作の確認・リハーサル

2は、念のために故障していないかどうか確認する。そこで設定漏れ、ディプロイ漏れがないかどうかを確認する。またレグレッションテストにおいてもこれが目的。
この場合、テスターはテスト仕様書にのみ沿ってテストを行う。テスト仕様書には手順と結果が厳密に定義されている。機械的な作業なので、事務的能力さえあれば基本的に誰でもできる。このタイプのテスターにはスキルは大して必要ない。能力のない後輩プログラマーが、先輩プログラマーが作った自信満々の、本人が確認していないプログラムをテストさせられたりもする。これはこれで機能する。
もっともこの部分はさらに機械的に、マシンで自動化すべきだが。まあ、GUIとか複雑なやり方などまだまだ自動化できない部分は人手が必要だろう。

1については、開発者が仕様どおりに開発しているなら、仕様に沿ってテストする限り、バグは検知されないはずである。
ここで発見されるバグは、仕様あるいは開発者の考慮漏れに起因するものである。例えば、Webアプリケーションのテキストボックスで、HTMLタグを入れられた場合どう処理するかを考慮せずに開発した場合、熟練したテスターはこういうところにバグが潜んでいるだろうということで、それをテストする。
当然、仕様書にも書かれていないので、テスト仕様書に沿ったテストでは通常は発見できない。この1の目的で行われるテストでは、開発経験がある人間がやらないと難しい。もし開発経験が豊富な人間なら、検出したバグに対して何が原因かも指摘できる。ところが、2の目的でしかできないテスターであれば、こういう場合、適切なバグ報告もできない。
2なら、全部OKとなる確認をすることが目的なのでバグが見つかるなら、詳細をレポートしなくても、その箇所を報告するだけで十分開発者に伝わるはずである。

・結合テストとシステムテスト
多くのプロジェクトで、適切に分類されていないのを見た。中には単体テストと結合テスト、システムテストと、内容がほとんど同じなんてこともあった。結合テストの仕様書の中の「結合」という文字を「システム」に置換し、それに負荷試験と異常系試験を追加して、システム仕様書としているようなものもあった。
これはシステムテストの中に機能試験を含めていることが原因している。顧客試験を除き、単体テスト、結合テスト、システムテスト、運用テスト、それぞれ目的が違うので、仕様書の中身は別であるべきだ。

そうなっていないのは意味を理解していないからだ。最小の単位が単体テストだが、これまたプロジェクトによって粒度が違う。クラス単位、メソッド単位の試験が粒度としては小さいが、例えばGUIアプリケーションの場合、ロジックとプレゼンテーションが分離していない場合、ロジックだけを取り出すことができず、GUIからテストすることになる。こうなってしまうと、ページとページを遷移させて行う結合テストとかぶってしまうことになる。こうして単体テストと結合テストの境界が曖昧になる。
本来結合テストは、モジュール間の結合部分を確認するのが目的なので、単体テストで行ったチェック項目は必要ない。さらにモジュールをひとまとめにしたパッケージとパッケージ間、ユーザ間、他システム間、そういう連動部分には固有の問題が発生しがちなので、その点をテストするのが結合テストの目的である。

部分が集まって大きな部分となり、さらにそれが集まってシステムとなり、そのシステムが集まって全体のシステムができる。この階層それぞれに結合テストが発生する。一般に最後の結合がシステムテストとされているが、この考えでいくと、システムテストの出る幕はない。
システムテストには、性能試験、負荷試験、異常系試験などがあるが、機能試験は全体の再チェックという位置づけだが、場合によっては不要なことが多い。

とふとと思った。
まだまだ読む資料は残っている。明日頑張って読もう。

プロフィール

dayan

Author:dayan
小職は、SE(システムエンジニア)を専門としておりますが、技術的な情報を中心に、それ以外に経済関連の日記、たわいもない日記も載せていきます。
[公式HPもよろしく!]

天気予報

-天気予報コム- -FC2-
リンク
ブロとも申請フォーム

この人とブロともになる

カテゴリー
最近の記事
ブログ内検索
最近のコメント
最近のトラックバック
RSSフィード
月別アーカイブ


上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。