プログラム

RubyでHTTPSリクエスト発行する

Rubyでプログラム書いていてWeb系のAPIHTTPSでアクセスする必要があったので調べた。
RubyでPOSTメソッドを発行するには以下の様に標準添付されているライブラリのnet/httpを利用すればよい。

#接続先ホスト名
hostname = "www.foo.bar"
#接続先パス
reqpath = "/foo/bar"
#POSTする際のパラメータ
param = "foo=hogehoge&bar=hogehoge"
https = Net::HTTP.new(hostname, 443)
https.use_ssl = true
https.verify_mode = OpenSSL::SSL::VERIFY_NONE  #本当は良くないが証明書チェック省略
res = https.post(
reqpath, param
)
p res.body # HTTP応答のBODYを出力

Rubyは標準ライブラリがいいですねぇ。

Rubyでnilの判定

Rubyでnil (空オブジェクト) の判定は nil? で行える。*1
if修飾子で書くと綺麗に書ける。やはりRubyで書くと読みやすいな。

def foo do
res = db.find("id" => 1)
return 'error' if res.nil?   # resが空オブジェクトの場合は 'error' を返却
#ゴニョゴニョする
'ok' # okを返却
end

*1:nilもオブジェクトなのか。空という定義のオブジェクト

全文検索エンジンgroongaを使ってみる(1) – インストール

休暇中に何らかの全文検索エンジンを入れて触ってみようと決めていたのでGroongaを入れてみた。

特徴

Groongaの特徴は以下の様なところがある。

  • 操作は複数のプロトコルが利用可能
    • 独自プロトコル or HTTP or memcached binaryプロトコル
    • HTTPの場合, GETリクエストで各種クエリを投げる形。結果はJSONで得られる。
  • データ更新が高速
    • 前身のSennaMySQLMyISAMに依存していたので更新時にテーブル全体にロックがかかってボトルネックになっていたのを独自ストレージを実装したので問題が解消
  • 集計系のクエリ処理が高速
    • ドリルダウン(検索結果をグループ分けする様な処理)を高速に行うことができる
  • 管理ツール利用可能
    • phpMyAdminの様なツールが標準で付いている (HTTPを利用する設定でGroongaサーバを起動させた場合)
  • サジェスト機能
    • 渡された検索クエリを元におすすめのクエリーをユーザに提案できる
      • Groongaサーバが検索クエリー入力時を含めログに回収し、それを元に提案する
  • 検索スコアの調整
    • Groongaが検索結果として返却したスコアに対して検索スコアを調整することができる。(独自のパラメータを重みとして検索結果に反映させられる)

といった具合。
簡単に触ってみたところ、検索スコアの調整が非常にしやすい印象を持ったし、HTTPで制御できるのが非常に便利といった実感を持った。

インストール

インストールは、このページを参照するとそれぞれの環境にあわせたインストール方法が紹介されているので参照しながら導入する。
Macの場合、Macports or Homebrew が選択できるので、ここではHomebrewを使ってみる。
brewコマンドですぐにインストールが可能。

% brew install groonga
Warning: It appears you have Macports or Fink installed
Software installed with other package managers causes known problems for
Homebrew. If formula fail to build uninstall Macports/Fink and reinstall any
affected formula.
==> Downloading http://packages.groonga.org/source/groonga/groonga-1.2.4.tar.gz
######################################################################## 100.0%
==> ./configure --prefix=/usr/local/Cellar/groonga/1.2.4 --with-zlib
==> make install
/usr/local/Cellar/groonga/1.2.4: 590 files, 12M, built in 7.1 minutes

インストール完了後、groongaコマンドでインストールを確認できる。

% groonga --version
groonga 1.2.4 [darwin9.8.0,i386,utf8,match-escalation-threshold=0,nfkc,mecab,zlib,kqueue]

configure options: < '--prefix=/usr/local/Cellar/groonga/1.2.4' '--with-zlib' 'CC=/usr/bin/cc' 'CFLAGS=-O3 -march=nocona -mfpmath=sse -w -pipe' 'CXX=/usr/bin/c++' 'CXXFLAGS=-O3 -march=nocona -mfpmath=sse -w -pipe'>

これでインストール完了。
もう少しいろいろ触ったのだけど、激しくお腹が痛いので今日はここまで。

homebrew導入

f:id:hideack:20110816143245p:image

Macにhomebrew入れた。
homebrewMacPortsと同じくパッケージ管理ツールでRubyで記述されている。
たまたま試したいと思っていたプログラムの配布パッケージにhomebrewが用意されていたので使ってみた。

% /usr/bin/ruby -e "$(curl -fsSL https://raw.github.com/gist/323731)"

但し、環境によっては以下の様な表示が出て利用するcurlコマンドのSSL証明書の問題でインストールができない場合がある。
(GithubにHTTPS接続しなければならないのだけど、手元にあるcurl用の証明書ではSSL証明できない場合)

error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed

curlのオプションで -k を指定すれば、SSL証明書の確認をスキップ*1できるのだが、更にインストールのステップを踏んでいくと、その中でcurlコマンドが更に呼ばれることが生じるので、根本的に証明書の問題を解決しておく必要がある。

お手軽な解決策がQAに出ていたので、それを参考して解決した。
以下の手順で証明書をcurlに設定しておけばSSL証明書の確認時のエラーは回避される。*2

% wget --no-check-certificate https://www.digicert.com/testroot/DigiCertHighAssuranceEVRootCA.crt;
% cat /usr/share/curl/curl-ca-bundle.crt DigiCertHighAssuranceEVRootCA.crt > new-ca-bundle.crt;
% sudo mv /usr/share/curl/curl-ca-bundle.crt /usr/share/curl/curl-ca-bundle.crt.old;
% sudo mv new-ca-bundle.crt /usr/share/curl/curl-ca-bundle.crt

これでbrewのインストールが行えるはずなので、インストールを行えばbrewコマンドが利用できる様になる。
お約束的なのは、brewコマンドを単独で実行するとusageが出力されるので参照。
以下、利用例、

% brew info ruby
ruby 1.9.2-p290
http://www.ruby-lang.org/en/
Depends on: readline, libyaml
Not installed

Consider using RVM or Cinderella to manage Ruby environments:
* RVM: http://rvm.beginrescueend.com/
* Cinderella: http://www.atmos.org/cinderella/

NOTE: By default, gem installed binaries will be placed into:
/usr/local/Cellar/ruby/1.9.2-p290/bin

You may want to add this to your PATH.

http://github.com/mxcl/homebrew/commits/master/Library/Formula/ruby.rb

参照: Brew Install results in Curl SSL certificate problem on Formula with GitHub HTTPS tarball URL

*1:結果は自己責任。本来は行うべきではない。

*2:あくまでも自己責任。本来は信頼できるブラウザからルート証明書をエクスポートして設定するのが本筋か。

PHPでクローラを書く

PHPでクローラを書くときのライブラリないかな?と探したところ、sourceforgeで見つけたので使ってみた。結構、出来が良くて少し触ってみた感じ便利そうな雰囲気。

PHPCrawl
PHPCrawl is a webcrawler/webspider-library written in PHP. It supports filters, limiters, cookie-handling, robots.txt-handling and other features.
http://sourceforge.net/projects/phpcrawl/

ダウンロードして展開した際に含まれているexample.phpを見ると、それが全てだったりするのだけど、一応以下にサンプルファイルを一部抜粋して掲載。
どの様な形で実装できるかがわかると思う。

<?php
// Inculde the phpcrawl-mainclass
include("classes/phpcrawler.class.php");
// Extend the class and override the handlePageData()-method
class MyCrawler extends PHPCrawler
{
function handlePageData(&$page_data)
{
// --- ここにコンテンツがクロールされた際の挙動を実装する
// Print the URL of the actual requested page or file
echo "Page requested: ".$page_data["url"]."\n";
// Print the first line of the header the server sent (HTTP-status)
echo "Status: ".strtok($page_data["header"], "\n")."\n";
// Print the referer
echo "Referer-page: ".$page_data["referer_url"]."\n";
// Print if the content was be recieved or not
if ($page_data["received"]==true)
echo "Content received: ".$page_data["bytes_received"]." bytes";
else
echo "Content not received";
echo "\n\n";
flush();
}
}
$crawler = &new MyCrawler();
$crawler->setURL("www.yahoo.com");  // クロール対象のWebサイト
$crawler->addReceiveContentType("/text\/html/");    // クローラで取り込む対象のContentType
$crawler->addNonFollowMatch("/.(jpg|gif|png)$/ i");  // 探索除外のファイル拡張子
$crawler->setTrafficLimit(1000 * 1024);
$crawler->go();
?>

いろいろ楽しいことできそうだな。

jquery.ui.datepicker.mobile.js を使った際の日付選択イベント抽出

行いたかったのは、jquery.ui.datepicker.mobileで作られたカレンダーの日付をタップ(クリック)された際のそのイベントを検出すること。
jQueryのlive(type, fn)を利用すればよいということがやっとわかった。
bind関数との違いはbind(type,fn)を利用した場合、動的に後付された要素に対してはイベントの割り当てが行われないが、live(type,fn)の場合は後付された要素に対してもイベントハンドラが割り当てられる。
したがって、jquery.ui.datepicker.mobileで後付されたカレンダーの要素に対してイベントハンドラの割り当て(タップ・クリック時のイベントの割り当て)を行おうと思ったらlive(type,fn)を利用しなければいけならない。

以下、サンプル。
カレンダー中の日付をタップした際に選択された日付に対応する年月日がalertで表示されるもの。

<html>
<head> 
<meta charset="utf-8"> 
<meta name="viewport" content="width=device-width, minimum-scale=1, maximum-scale=1"> 
<link rel="stylesheet"  href="http://code.jquery.com/mobile/1.0a4.1/jquery.mobile-1.0a4.1.min.css" /> 
<link rel="stylesheet"  href="css/jquery.ui.datepicker.mobile.css" />
<script type="text/javascript" src="http://code.jquery.com/jquery-1.5.min.js"></script> 
<script type="text/javascript" src="http://code.jquery.com/mobile/1.0a4.1/jquery.mobile-1.0a4.1.min.js"></script> 
<script type="text/javascript" src="js/jQuery.ui.datepicker.js"></script> 
<script type="text/javascript" src="js/jquery.ui.datepicker.mobile.js"></script> 
<script type="text/javascript">
    $(".ui-datepicker-calendar").ready(function() {
        $("td").live("click", function(){
            alert($("#datepicker").val());
        });
    });
</script>
<body>
<div data-role="page" data-theme="b" id="jqm-home">
<div data-role="content">
<form action="#" method="get">
<input type="date" name="date" id="datepicker" value="" >
</form>
</div>
</div>
</body>
</html>

もっとも、datepickerのAPIイベントハンドラの操作できそうな気もするのだが…。
いろいろ探してみたが見つけられなかった。

未解決: jquery.ui.datepicker.mobile.js を使った際の日付選択イベント抽出

その後解決→jquery.ui.datepicker.mobile.js を使った際の日付選択イベント抽出

jQuery mobileとjQuery.uiのdatepickerを利用してスマートフォン向けの日付選択ダイアログを作るところまではうまくいっているのだけど、どうしても日付の選択のイベントを検出できない。

<html>
<head> 
<meta charset="utf-8"> 
<meta name="viewport" content="width=device-width, minimum-scale=1, maximum-scale=1"> 
<!-- 中略 -->
<script type="text/javascript" src="http://code.jquery.com/jquery-1.5.min.js"></script> 
<script type="text/javascript" src="http://code.jquery.com/mobile/1.0a4.1/jquery.mobile-1.0a4.1.min.js"></script> 
<script type="text/javascript" src="js/jQuery.ui.datepicker.js"></script> 
<script type="text/javascript" src="js/jquery.ui.datepicker.mobile.js"></script> 
<script type="text/javascript">
    $(document).ready(function() {
        $("#datepicker").datepicker({
            onSelect:function(dateText, inst){
                alert("foobar");
            }
        });
    });
</script>
<!-- 以下略 -->

表示は問題ない。あと、日付の選択状態になるのも問題無し。

f:id:hideack:20110611170848p:image

本当はカレンダーから日付を選択されたら"foobar"とダイアログ表示させたいのだけれども表示されない。
はて、何か勘違いしているかなぁ…。

Google URL Shortener API を使ってみる(2) – PHPで利用してみる

準備で取得したAPIキーを利用してGoogle URL Shortner API へアクセスすることで短縮したURLを取得することができる。
ここでは、PHP上から利用してみた例。HTTPのPOSTアクセスには、PEARのHTTP::Request2を利用しているので、PEARにいれていない場合はPEARコマンドでライブラリのインストールが必要。
以下、サンプル。このblogのURL(http://d.hatena.ne.jp/hideack/)をgoo.gl形式に短縮してみる。

<?php
require_once 'HTTP/Request2.php';
try {
$request = new HTTP_Request2("https://www.googleapis.com/urlshortener/v1/url?key=(取得したAPIキー)", HTTP_Request2::METHOD_POST);
$request->setConfig('ssl_verify_peer', false);
$request->setHeader("Content-Type", "application/json");
$request->setBody('{"longUrl": "http://d.hatena.ne.jp/hideack/"}');
$response = $request->send();
if ($response->getStatus() == 200) {
$body = $response->getBody();
$res = json_decode($body);
printf("SHORT URL = %s", $res->id);
}
else {
throw new Exception ("Server returned status: " .  $response->getStatus());
}
}catch (HTTP_Request2_Exception $e) {
echo $e->getMessage();
}catch (Exception $e) {
echo $e->getMessage();
}
?>

これを実行すると、

% php -q test.php
SHORT URL = http://goo.gl/NDkke

となって、短縮されたAPIを取得できたことがわかる。
ちなみに、APIの利用状況はAPIキーを取得する準備の際に利用したAPIコンソールを見ることでリアルタイムに把握することができる。
f:id:hideack:20110504224723p:image

Google URL Shortener API を使ってみる(1) – 準備

google短縮URL生成機能のGoogle URLのAPIを利用してみる。1日100万回利用可能。
APIを利用することでプログラム中から非常に長い文字列数で構成されるURLをhttp://goo.gl/〜 で始まるURLに変換することができる。
利用には、まずAPIキーをgoogle API consoleから取得する必要がある。

上記のサイトを開くとまずGoogleアカウントでのログインを求められるので、ID / PWを入力してログインする。
f:id:hideack:20110504224719p:image
初めてAPI consoleを利用する場合は、以下の様な画面が表示されるので中央の"Add project"をクリックする。
f:id:hideack:20110504224720p:image
次の画面でGoogleが提供するAPIの一覧が表示される。
全く初めて利用する場合は全てOFFとなっているので、その中からURL Shorter APIのStatusをクリックして、"ON"に切り替える。
f:id:hideack:20110504224721p:image
APIのStatusを変更した後、APIを利用する際に必要となるAPIキーを取得する。
画面左側の"API Access"をクリックすると、"Simple API Access"が表示されるので表示されている"API key"を控える。
f:id:hideack:20110504224722p:image

JavaScriptでテンプレートエンジン(jQuery templatesプラグイン)

PHPだとSmartyなどのテンプレートエンジンを利用したりするが、同様にJavaScriptでクライアントサイドでテンプレートに変数を流しこむ様な実装を行うことができるライブラリを探したところ、jQueryのtemplatesプラグインで実現できる様なので使ってみた。

Templates – jQuery API
A template contains markup with binding expressions. The template is applied to data objects or arrays, and rendered into the HTML DOM.
http://api.jquery.com/category/plugins/templates/

こういうのは、サンプルを見たほうが早いのでサンプルを以下に掲載。
jquery本体とプラグイン本体はサイトを直接指定している。

<html>
<head>
	<script type="text/javascript" src="http://code.jquery.com/jquery-1.5.min.js"></script> 
	<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script>
	<script type="text/javascript">
		window.onload=function(){
			var json=[{"name":"Tom", "like":["apple", "peach"]}, {"name":"Mike", "like":["melon"]}];
			
			$("#child").tmpl(json).appendTo( "#parent");
		};
	</script>
	</head>
<body>
<div id="parent">
<script id="child" type="text/x-jquery-tmpl"> 
			Name:${name}<br/>
			Like:
			{{each like}}
				${$value}
			{{/each}}
			<hr>
		</script>
</div>
</body>
</html>

上のHTML & JavaScriptで以下の様な表示が行われる。

f:id:hideack:20110504150456p:image

内容的にはサンプルのまんまなのだが、window.onloadで開いた際に定義されるjson変数を

$("#child").tmpl(json).appendTo( "#parent");

と呼び出すことで、以下の形式で指定することでHTMLのbody中に記載してある の間に記載されているテンプレート文法に変数がアサインされる。

JavaScriptでJSONを返却する様なAPIAJAXで呼び出してそのコールバック受付時にHTML上の特定のDOMにテンプレートに従った形式で反映される様にするとか便利に使えそうだ。