jquery.simplejsonrest.js 最終案

// Simple JSON-REST plugin for jQuery

//low level

$.SJR.option = {
	"success" : function(response, status){alert('success');},
	"error"   : function(xhr, status, thrown){alert('error');}
};

$.SJR.option.type = 'POST';
$.SJR.option.url  = '/_je/myDoc';
$.SJR.option.data = '{"foo":"bar"}';

$.SJR.ajax();

//基本

option  = {
	"url"  : "/_je/myDoc",
	"data" : {"foo":"bar"} // or "foo=bar"
};
success = function(response, status){alert('success')};
error   = function(xhr, status, thrown){alert('error');};

$._GET (option, success, error);
$._POST(option, success, error);
$._PUT (option, success, error);
$._DELETE(option, success, error);

//jQuery的な使い方

//要素の内容を元に実行される。内容を走査する為のコーディングは不要。
$('form')._POST(success, error);
$('form')._PUT (success, error);
$('form')._DELETE(success, error);

//取得したデータを要素の内容に反映させる。基本的に内容を反映させるコーディングは不要。
id = '_docId=00roxf3RwEBi1ZobhYECZdUnT766oU97';
$('form')._GET(id, success, error);

//取得したデータを要素の内容に反映させるコーディング例。
//それぞれ違う要素だが同じ処理で反映させる事が出来る。
option.data = {"sort":"message.asc", "limit":"100"};
var success = function(response, status){
	this.empty();
	for (i = 0; i < response.length; i++) {
		var msg = $("<div/>").text(response[i].message);
		this.append(msg);
	}
};
$('#messages1')._GET(option, success, error);
$('#messages2')._GET(option, success, error);

最終案のソースを見てjqueryを知っている人ならピン!ときた人もいるかと思います。要はjquery.simplejsonrest.jsを使うとフォーム値に関してはリクエストするのに基本コーディングが無くなります。submit感覚で使えちゃうって事。

jquery.jsonengine.jsやめた。代わりにjquery.simplejsonrest.js 書き始めた

自分で作っといて何だけど jquery.jsonengine.js は使い物にならない!公開しないで良かった。その代わりを考えました。こんな感じで。

jquery.simplejsonrest.js

// Simple JSON-REST plugin for jQuery

//BASE

var _opt = {
	"type"    : "POST",
	"url"     : "/_je/myDoc",
	"data"    : {"foo":"bar"},
	"success" : function(){alert("success")},
	"error"   : function(){alert("error")}
};
$._OPTIONS(_opt)._AJAX();

//BASIC

var _data   = {"url":"/_je/myDoc","data":{"foo":"bar"}};
var _docId  = '_docId=00roxf3RwEBi1ZobhYECZdUnT766oU97';
var _filter = {"cond":"foo.eq.bar","sort":"foo.asc","limit":"100"};

$._OPTIONS(_data)._GET(_filter, success, error);
$._OPTIONS(_data)._POST(success, error);
$._OPTIONS(_data)._PUT(success, error);
$._OPTIONS(_data)._DELETE(success, error);

//USAGE

$('#aForm')._GET(_docId,  success, error);
$('#aForm')._POST(success, error);
$('#aForm')._PUT(success, error);
$('#aForm')._DELETE(success, error);

$('#aData')._OPTIONS(_data)._GET(_filter, success, error);

こんなソースなら使いたくなる。自画自賛

教えてください。

こんなことやりたいです。

  • ドキュメント指向DBのデータとWebブラウザストレージのデータを同期させオンライン、オフラインで動作可能にする
  • Webブラウザのストレージで全文検索
  • 現時点のブラウザストレージの実装による違いを意識しないようプログラミングする。またはフレームワークを利用する

CouchDBやMongoDBとかドキュメント指向DBとWebブラウザGoogle GearsHTML5 localstorageとの同期ってどんな風に実装するんでしょう?Webブラウザのストレージについて全くの無知です。HTML5の仕様はまだ決まってないし、でも現在の実装でもいいから使いたいなぁ。クロスブラウザなローカルストレージを扱うJavaScriptライブラリってありませんかね?

jquery.jsonengine.js 書き始めた

jsonengineをシンプルに扱うことの出来るjQueryプラグインを書いたんだけど公開するかはまだ迷い中。ホンのちょっとだけ便利かなーと。こんな感じで書けます。

jquery.jsonengine.js のサンプル

$(function() {

//スタイル1:デフォルトのdocTypeを指定しパラメータをオブジェクトリテラルで渡す。updateは必ず_checkUpdatesAfterが付く。

	$.je.docType('tasks');

	$.je.create({"foo":"bar"}, function(response, status){
		// do something with task here
	});

	$.je.read({"foo":".eq.bar"}, function(response, status){
		// do something with task here
	});

	$.je.update({"foo":"bar","_docId":"00roxf3RwEBi1ZobhYECZdUnT766oU97"}, function(response, status){
		// do something with task here
	});

	$.je.destroy({"_docId":"00roxf3RwEBi1ZobhYECZdUnT766oU97"}, function(response, status){
		// do something with task here
	});

//スタイル2:実行毎にdocTypeを指定しパラメータをオブジェクトリテラルで渡す

	$.je.post('tasks', {"foo":"bar"}, function(response, status){}, function(xhr, status, errorThrown){});

	$.je.get('tasks', {"foo":".eq.bar"}, function(response, status){}, function(xhr, status, errorThrown){});

	$.je.put('tasks', {"foo":"bar","_docId":"00roxf3RwEBi1ZobhYECZdUnT766oU97"}, function(response, status){}, function(xhr, status, errorThrown){});

	$.je.del('tasks', {"_docId":"00roxf3RwEBi1ZobhYECZdUnT766oU97"}, function(response, status){}, function(xhr, status, errorThrown){});

//スタイル3:パラメータを全て文字列で定義して実行する

	$.je.put('/tasks?foo=bar', function(response, status){}, function(xhr, status, errorThrown){});

	$.je.get('/tasks/00roxf3RwEBi1ZobhYECZdUnT766oU97', function(response, status){}, function(xhr, status, errorThrown){});

	$.je.get('/tasks?cond=foo.eq.bar', function(response, status){}, function(xhr, status, errorThrown){});

	$.je.put('/tasks?foo=bar?_docId=00roxf3RwEBi1ZobhYECZdUnT766oU97', function(response, status){}, function(xhr, status, errorThrown){});

	$.je.del('/tasks/00roxf3RwEBi1ZobhYECZdUnT766oU97', function(response, status){}, function(xhr, status, errorThrown){});

});

最初はslim3のDataStoreっぽくチェーンメソッドで組み立てる形式で書いたけどIDEやらの支援機能が使えなくてあんまり意味ないかなってこっちの書き方に直した。これならjQuery知ってる人だと一目で使い方が分かってもらえるんじゃないかと。もっと汎用的なRESTFullプラグインがいいかな?ご意見お聞かせください。

jsonengineとExtJSはものすごく相性が良い

jsonengineとExtJSを使えば20行位のJavaScriptコードでtreeが動的に表示出来ちゃいます。勿論サーバーサイドは何も書きません。

ExtJSについてはここを参照してください。 http://extjs.co.jp/products/js/

私が書いたデモはここ。 http://uchjava.appspot.com/samples/extjs/samples/tree.html

コードはこんな感じ

Ext.onReady(function() {

	var treePanel = new Ext.tree.TreePanel( {
		dataUrl      : '/_je/testTree',
		requestMethod: 'GET',
		nodeParameter: null,
		root: {
			nodeType: 'async',
			text    : '/',
			id      : 'xxx'
		}
	});

	// event override
	treePanel.on("beforeload", function(node){
		this.loader.baseParams.cond = 'node.eq.' + node.id;
	});

	// render the tree
	treePanel.render(Ext.getBody());
	treePanel.getRootNode().expand();

});

ブラウザのデバッガで見れば分かるけど非同期にtree情報を読み込んでいます。興味ある人はデモのソースを読んでみてください。理解できたらtestTreeへ適当に情報追加したりしてもオッケー。

jQueryもいいけど標準で豊富にリソースが用意されているExtJSが断然おすすめ。

ExtJSの兄弟でSencha Touchがあるし今すぐデスクトップアプリもモバイルアプリも書けちゃうのが凄くいい。

jsonengineもExtJSも強力過ぎてウェブサービスが笑っちゃうくらい簡単に作れる時代になりました。

作者方に感謝。

jsonengineの全文検索機能をローカル開発環境でも動くようにした

jsonengineのサンプルであるbbs.htmlはそのままだとローカル開発環境で動かした場合に「java.lang.IllegalArgumentException: Invalid uri」が発生し更新できない。例外発生の原因はyahoo apiサービスである日本語形態素解析を利用しているのが原因。問い合わせにGETメソッドを使用しているのだがパラメーター文字列の解釈で例外が発生するようだ。本番環境のappspot.comへデプロイすれば動作するが動作確認の為に毎回デプロイすると非常に開発のリズムが悪い。

全文検索機能は開発時にも是非使いたい。そうじゃないと魅力が半減してしまい結局サーバーサイドへ手を出す羽目になってしまう。

何とかならないかと詳しく調べたところyahoo apiはPOSTメソッドの問い合わせにも対応していたのでそれでテストしてみた。結果は問題なく意図通り動作した。具体的なソースを記録として残しておく。修正箇所はJEUtils.javahttp://code.google.com/intl/ja/appengine/docs/java/urlfetch/usingjavanet.html#Using_HttpURLConnection を参考にした。

JEUtils.java

public class JEUtils {

〜〜〜他のメソッド等は省略

    /**
     * Extract terms from the text by using Yahoo's term extraction web service.
     * 
     * @param text
     * @return a Set of extracted terms
     */
    public Set<String> extractTerms(String text) {
        final Set<String> propValues = new HashSet<String>();
//      final String result = callURL(YAHOO_PARSE_JA + text);
        final String result = callYahooAPI(text);
        final Matcher m = termPattern.matcher(result);
        while (m.find()) {
            propValues.add(m.group(2));
        }
        return propValues;
    }

    public static final String YAHOO_PROTOCOL_JA =
        "http://";

    public static final String YAHOO_HOST_JA =
        "jlp.yahooapis.jp";

    public static final String YAHOO_MASERVICE_JA =
        "/MAService/V1/parse";

	//ここは各自のappidを使った方がいいかも
    public static final String YAHOO_APPID_JA_ =
        "appid=" 
            + "QZWK7SGxg67FGZpOHgk2rMkwNL5EMOXhnNXqEDKpk32FwzA8PFcgFirTdE6zXJDnKtnp";

    public static final String YAHOO_RESPONSE_JA = 
        "response=" 
            + "surface,reading";

    public static final String YAHOO_FILTER_JA = 
        "filter=" 
            + "1|3|5|6|7|8|9|10";

    public static final String YAHOO_SENTENCE_JA = 
        "sentence=";

    public String callYahooAPI(String text) {
        final StringBuilder sb = new StringBuilder();
        try {
            URL url = new URL(YAHOO_PROTOCOL_JA + YAHOO_HOST_JA + YAHOO_MASERVICE_JA);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setDoOutput(true);
            connection.setRequestMethod("POST");

            OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
            String param =  YAHOO_FILTER_JA
                    + "&" + YAHOO_RESPONSE_JA
                    + "&" + YAHOO_APPID_JA_
                    + "&" + YAHOO_SENTENCE_JA
                    + URLEncoder.encode(text, "UTF-8");
            writer.write(param);
            writer.close();

            if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
                BufferedReader reader =
                    new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
                String line;
                while ((line = reader.readLine()) != null) {
                    sb.append(line);
                }
                reader.close();
            } else {
                // output error log
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sb.toString();
        
    }

}

JEUtilsTest.java

public class JEUtilsTest extends AppEngineTestCase {

    @Test
    public void testCallYahooAPI() {
        final String result = (new JEUtils()).callYahooAPI("本日は晴天なり");
        assertNotNull(result);
        final List<String> values = this.extractTerms(result);
        assertEquals("本日", values.get(0));
        assertEquals("ほんじつ", values.get(1));
        assertEquals("晴天", values.get(2));
        assertEquals("せいてん", values.get(3));
    }

    private static Pattern termPattern = Pattern
        .compile("<(surface|reading)>([^<]*)</(surface|reading)>");

    private List<String> extractTerms(String result) {
        final List<String> list = new ArrayList<String>();
        final Matcher m = termPattern.matcher(result);
        while (m.find()) {
            list.add(m.group(2));
        }
        return list;
    }

}

VMware Fusion で Apple wireless keyboard を使用している時の Windows IME のキー割り当て

IME のキー設定で

・ひらがなに IME オンを割り当て
・英数に IME オフを割り当て
・英数がダメなら無変換に IME オフを割り当て

以上で IME オン/オフの設定は終わり。