今回は軽めのネタです.初心者向けに書いてみました.Rubyやプログラミング初心者の人には,これからコードを書く時の手順の参考になるかと思います.
前提条件を明らかにして,問題と要求事項を定義し,その解決手法を選択し,最後にコードに落とす,という流れになっています.
前提
週に一度,大学の情報系授業の中でHTMLを教える機会があります.内容としては完全に入門レベルなので技術的に高いところまでは踏み込みません.プログラミングやテキストエディタを使ったことの無い学生への取っかかりといった位置づけの授業です.
「コード書く->エラーチェックする->エラーメッセージ見てエラーがあれば修正する->エラーチェックする->(以下略」の手順を身をもって体験してもらうために,僕の授業の中ではValidなHTML5を書くことを目標としてやっています.実際に動いているサイトで完全にValidなHTMLを出力しているページなどはほとんどないと思うので,僕自身はそこまで完全にValidなHTMLを出力することに大きなこだわりは無いです(XHTMLは除く.
そんなわけで,W3C公式のHTML ValidatorであるW3C Markup Validation Serviceを使って,学生には作ったHTMLの構文チェックをしてもらっています.
このValidatorは日本語化はされていないのですが,割と親切にエラー原因とその解決方法を提案してくれるので好きです.
また,ソースコードも公開されているので,自前のサーバに設置することもできるはずです.すばらしい.
問題と要件定義
さて,ここからようやく本題です.
学生に課題提出先に対してHTMLをアップロードしてもらうまでは良いのですが,これを採点のためにチェックするのがまた大変だったりします(学生も大変ですが,講師も大変なのです:p
そんなわけで,このW3C公式Validatorをpassするか(Errorが0件かどうか)を確認する手段が欲しいです.
ざっと用件定義してみると,以下の様な感じです.RFC風にMUST/SHOULD/MAYに分けてみました.
MUST要件(絶対必要)
- 特定のURLのHTMLがvalidであるかを調べることができること
- validateの結果はW3C Markup Validation Serviceと等しくなること(学生と同環境にする必要がある)
SHOULD要件(できることが望ましい)
- 同じ様なこと(特定のWebサービスのデータが取りたい,など)がしたくなったとき,ある程度再利用できるプログラムになっていること
- 複数の学生の提出URLに対してまとめてチェックがかけられること
MAY要件(できたらいいな)
- きれいなコードであること
- オプションで自前で設置したMarkup Validation Serviceにも接続できるといいな
解決方法の検討
問題と要件が洗い出せたところで,解決方法を検討しましょう.今回,とりあえず以下の方針で行くことにしました.
- プログラミング言語はRuby:慣れてるし,Techrachoの記事ネタにもなるから:p
- ベタな関数として実装し,URLを渡せばチェック出来るようなコードとする:クラスを作るほど複雑なことも特にないのと,関数化することによる汎用性の向上を行う
- 開発速度優先で,さっさと作る:出社中の電車内でサクッと書き上げられるレベルのもの.制限時間は調査,実装合わせて30分以内くらい
その結果,今回のプログラムでは以下の部分は捨てています.この辺は実装時間とのトレードオフになるかと思います.今回の要件の場合,前述の仕様で十分と判断しました.
- W3C Markup Validation Serviceは短時間に大量のリクエストを投げるとDoS攻撃認定されるのか,しばらくアクセス出来なくなる問題対策
- 外部サーバに問い合わせしてvalidationを行うので,1回辺り数秒の時間がかかる問題
- 汎用性が高めとは言っても,gem installできるほどではない
実装
以下の様なコードになりました.見れば分かりますが,エラー処理とかは割と手抜きです(Ruby 2.0.0にて動作確認済).
# coding: utf-8 # validate_html.rb: HTMLがValidかどうかを調べる. require 'net/http' require 'uri' require 'nokogiri' # options: http://validator.w3.org/ が解釈できるGETパラメータを追加できる def validate_html(uri, options = {}) http = Net::HTTP.new(uri.host) response, = http.get(uri.path) if response.code != '200' raise 'Page not found' end http = Net::HTTP.new('validator.w3.org') options['uri'] = uri query = '' options.each{|k, v| query << "#{k}=#{v}&" } puts query response, = http.get("/check?#{query}", 'User-Agent' => 'ruby-2.0.0-p247') doc = Nokogiri::HTML(response.body) doc.search('h2.invalid').each do return false end true end
注意すべき所としては,W3C Markup Validation Serviceは今年辺りからUser-Agentを設定しないリクエストにはエラーを返すようになっているので,User-Agentを必ず設定すること位でしょうか.後はそれほど特殊なことはしてないです.
また,事前にgem install nokogiriする必要があります.
if validate_html(URI.parse('http://web.sfc.keio.ac.jp/~morimori/sfc-itb-html5/'), doctype: 'HTML5') puts 'valid HTML5' else puts 'invalid' end
URI.parseに渡したURLがHTML5でvalid(エラーなし)なら'valid HTML5',1件でもエラーがあれば'invalid'と表示されます.warningの件数は無視しています.
まとめ
というわけで,今回はプログラミング初心者な人を対象に,普段プログラミングするときの思考の流れというか開発の流れをできるだけかみ砕いて書いてみました.
学生さんなどの場合,課題が既に決まっているお題を出されることが多いと思うので,最初の要件定義や問題をまとめるところの経験が少なかったりするので,参考になれば幸いです.