ほんじゃらねっと

ダイエット中プログラマのブログ

Node.jsでドメインのはてなブックマーク合計件数を取得する

ブログを書いていると、

自分が書いた記事にどれくらいブックマークがついているかが

気になるものである。

はてなブログでブログを管理している場合は、

ブログ管理画面や「はてなのお知らせ」というChrome拡張で

ブックマークされたことを知ることができる。

chrome.google.com

Wordpress等でブログを管理している場合は、

はてなブックマークのトップページの検索欄にブログのURLを入力すると

記事ごとのブックマーク数を表示してくれる。

このブログなら下記のページ:

『ほんじゃら堂』 の新着エントリー - はてなブックマーク

まあ確認するにはこれで十分なのだけど、

「もっとサクッとブックマークの総数が知りたいんや!」

という人向けに(かどうかは知らないが)

はてなブックマークのAPIに、

「指定したドメインの合計はてブ件数を取得できるAPI」

というものがちゃんと用意されている。

下記サイトの「被ブックマーク合計数取得API」のところだ。

はてなブックマーク件数取得API - Hatena Developer Center

XMLRPC経由ではてブ合計件数が知りたいドメインを渡すと、

その件数を返してくれる。

このAPIを使えば、

例えばブックマーク件数が増えた時に通知する、みたいなこともできそうだ。

このAPIを呼び出すスクリプトを作成して、

前回の件数と異なる件数が返ってきたらメールなりチャットなりに通知するような

内容にしておけば、即座に知ることができるだろう。

Node.jsでAPIにアクセスするスクリプトを作る

今回はXMLRPC経由で件数を取得するだけのスクリプトを作成してみよう。

練習も兼ねてNode.jsで作成してみた。

Node.js XMLRPC Client Sample

sample_hatebu.jsを実行するとはてブ件数が表示される。

Hatebu.getHatebuCountに渡しているURLを変更すれば

好きなドメインのはてブ数を表示することができる。

XMLRPCには下記のライブラリを使用している:

github.com

Node.js実行環境のインストール方法やスクリプトの実行方法については

入門記事を作成したので、下記を参照いただきたい:

blog.honjala.net

おわり

モチベーションアップに繋がるので、

こういったポジティブな情報の取得はどんどん自動化して

目に入るようにしておきたいものだ。

Subversionにコミットしたらチャットワークに通知するNode.jsスクリプト

ある開発プロジェクトで、

非技術系のメンバーにも進捗を知っておいてもらいたいものの、

「お前らSubversion入れろ」とか「リポジトリログを見ろ」といっても見そうになく、

いちいち伝えるのも面倒大変そうだったので、

Subversionにコミットがあったらメッセージ内容がリアルタイムで

チャットワークに流れる仕組みを作ってやった。

www.chatwork.com

チャットワークはチーム間でのやりとりに使っていたので、ここに流してやろうというわけだ。

チャットワークはプレビュー版ながらAPIを公開してくれているので、

developer.chatwork.com

API経由でメッセージ投稿を行うスクリプトと、

Subversionのフック機能を組み合わせることで実現することができた。

Node.jsでチャットワークのAPIにアクセスするスクリプトを作成する

今回はNode.jsでスクリプトを作成した。

手軽に環境が作れて、速くて、一通りライブラリが揃っているので気に入っている。

スクリプトの内容としては、

コマンドラインからリポジトリパスとリビジョン番号を受け取って、

svnlookコマンドでリビジョンをコミットしたユーザー名、メッセージログを取得して、

メッセージを組み立てて、

チャットワークのAPI経由で投稿している。

Subversionが動いているサーバにnode.js環境をインストールして、このスクリプトを配置する。

svn2chatwork.js

var execSync = require('child_process').execSync;
var _ = require('underscore');
var request = require('request');

if (process.argv.length < 4) {
    return;
}

var repo = process.argv[2];
var rev = process.argv[3];

var cwApiKey = '<ChatworkのAPIキー>';
var endpointBase = 'https://api.chatwork.com/v1/';

var svnRootPath = '/var/www/svn'; //Subversionのリポジトリルートパス

var targetRepos = {}; 
// このtargetReposにリポジトリパスを追加したら複数のリポジトリに対応可能
targetRepos[svnRootPath + '/<SVNリポジトリパス>'] = {'roomId': '<投稿先ChatworkのルームID>'};

var post2Chatwork = function (roomId, message, callback) {
    var options = { 
        url: endpointBase + 'rooms/' + roomId + '/messages',
         headers: {
             'X-ChatWorkToken': cwApiKey
         },  
        form: {
            body: message
        },
        json: true
    };

    request.post(options, function (error, response, body) {
        if (!error &amp;&amp; response.statusCode == 200) {
            callback(body);
        }   
    }); 
}
if (_.has(targetRepos, repo)) {
    var repoInfo = targetRepos[repo];
    var message = 'New Commit\n';

    var result = '' + execSync('svnlook author ' + repo + ' -r ' + rev);
    message += 'User:' + result;

    result = '' + execSync('svnlook log ' + repo + ' -r ' + rev);
    message += result;

    // 下記のコメントを外すと更新ファイル一覧もメッセージに追加できる(大量にファイルが更新されると結構うざい)
    // result = '' + execSync('svnlook changed ' + repo + ' -r ' + rev);
    // message += 'Files Updated:\n';
    // message += result;

    post2Chatwork(repoInfo.roomId, message, function (body) {});
}

Subversionのフックスクリプトを作成する

Subversionのリポジトリを作成すると、その中にhooksフォルダが作成され、

そこに置いたフックスクリプトを各種タイミングで実行してくれる。

Subversion フックスクリプト

今回はコミット後に上で作成したスクリプトを実行してもらいたいので、

「post-commit」という名前で下記のような内容のファイルを作成する。

作成したスクリプトにパラメータとしてリポジトリパスとリビジョン番号を渡し、nodeコマンドで実行するコマンドを書いてる。

最初のexport文を書かないと、日本語が文字化けしてしまうので注意。

あと、node.jsのバージョンとか、どこにsvn2chatwork.jsを置いたかでパスが変わるので注意。

post-commit

export LANG=ja_JP.UTF-8

REPOS='$1'
REV='$2'

# <nodeコマンドへのパス> <svn2chatwork.jsへのパス> $REPOS $REV
/usr/local/node-v0.12.7/bin/node /home/test/svn2chatwork/svn2chatwork.js $REPOS $REV

編集後、post-commitに実行権限をつけたら以降のコミットがチャットワークに通知されるはず。

おわり

ソースを整理したり、リポジトリとルームの関連付けを外部化したりしたものをGitHubに置いている。

よかったら参考にしていただきたい。

github.com

今時Subversionなんか使わねーよ!と言われるかもしれないが。

blog.honjala.net

Subversion実践入門:達人プログラマに学ぶバージョン管理(第2版)

Subversion実践入門:達人プログラマに学ぶバージョン管理(第2版)

Node.jsでRedmineのREST APIにアクセスしてチケット取得&一括登録

とあるWeb開発プロジェクトで

WBS(Excel製)に書かれた、機能ごとの開発担当・開始日・期日・予定工数を

そのままRedmineのチケットとして登録するという、

手作業でやったらえらく時間のかかりそうな作業があったので、

RedmineのAPI経由でチケットを一括登録できるスクリプトを作成した。

続きを読む

Node.jsのRESTクライアントモジュールでBit.lyのAPIにアクセスする

Node.jsのRESTクライアントモジュールを試すため、 以前Groovyで作成したBit.lyの履歴を取得するスクリプトをNode.jsで焼きなおしてみる。

node-rest-client」というモジュールを使用した。

get_bitlinks.js

var RestClient = require('node-rest-client').Client;

var ACCESS_TOKEN = '<your access token>';
var ENDPOINT_BASE = 'https://api-ssl.bitly.com/';
var bitlyClient = new RestClient();

bitlyClient.get(ENDPOINT_BASE + 'v3/user/link_history', {
    headers: {
        'Content-type': 'application/json'
    },
    parameters: {
        access_token: ACCESS_TOKEN,
        limit: 10,
        offset: 0
    }
}, function (data, response) {
    var result = JSON.parse(data.toString('utf8'));
    var linkList = result.data.link_history;
    linkList.forEach(function (link) {
        console.log(link.title);
    }); 
});

RestClientで呼び出すメソッドをgetからpostやputに変更すれば 登録、更新も行えるようなので、これ1つ覚えておけば色々なAPIアクセスに 使用できそうだ。

RESTful Webサービス

RESTful Webサービス

MongoDBに保存したデータをNode.js+ExpressでWeb表示する簡単なサンプル

そろそろNode.jsスクリプト作成にも慣れてきたので、

簡単なWebアプリ作成に挑戦してみた。

Meteor、MEAN.js、Sails.jsなど色々あって迷ったけど、

まずはシンプルにExpressを使ってみる。

MongoDBコレクションに保存された情報を

Webページ上に一覧表示するスクリプトを、

Node.js+Express+MongoDBで作成した。

続きを読む

ApacheのアクセスログをMongoDBコレクションにインポートするNode.jsスクリプト

MongoDBの勉強を兼ねて、Apacheアクセスログを後でログ分析や ビジュアライズに使えるようにMongoDBにインポートするスクリプトを書いた。

下記のスクリプトはログファイルを行単位で読み込んでMongoDBの 特定のコレクションに登録する。 行単位のファイル読み込み処理はNode.js標準で簡単にできる方法が用意されていない ようなので、node-bylineを使用している。

log_importer.js

var fs = require('fs');
var byline = require('byline');
var MongoClient = require('mongodb').MongoClient;

var dbUrl = 'mongodb://localhost:27017/<database name>';
var targetPath = '<path to access log>';

var logPattern = /(\S+) \- \- \[([^\]]+)\] '([^']+)' (\d+) (\S+) '([^']+)' '([^']*)'/;

var rs = fs.createReadStream(targetPath);
var stream = byline.createStream(rs);

MongoClient.connect(dbUrl, function (err, db) {
    if (err) {
        throw err;
    }   

    var apacheLogs = db.collection('apache_logs');

    var i = 0;
    stream.on('readable', function () {

        var line = null;
        while (null !== (line = stream.read())) {
            console.log(i + ':line start ' + line);
            var match = logPattern.exec(line);
            if (match != null) {
                var methodParams = match[3].split(' ');
                var method = methodParams[0];
                var path = methodParams[1];
                var http = methodParams[2];
                var log = {
                    host: match[1],
                    ts: match[2],
                    method: method,
                    path: path,
                    http: http,
                    status: match[4],
                    size: match[5],
                    agent: match[7]
                };
                apacheLogs.insert(log, function (err, result) {
                    if (err) {
                        console.log('log insert error:', err);
                    } else {
                        console.log(i + ':inserted');
                    }
                });
            } else {
                console.log(i + ':match error');
            }
            i++;
        }
    });
    stream.on('end', function () {
        db.close();
    });
});

参考にしたサイトや使用したライブラリ:

RegExp - Mdn https://developer.mozilla.org/ja/doc/Web/JavaScript/Reference/Global_Objects/RegExp

Read large text files in nodejs https://coderwall.com/p/ohjerg/read-large-text-files-in-nodejs

Node.js how to read one line of numbers from a file into an array http://stackoverflow.com/questions/7379310/node-js-how-to-read-one-line-of-numbers-from-a-file-into-an-array

npm line-reader https://github.com/nickewing/line-reader

npm node-byline https://github.com/jahewson/node-byline

JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス

JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス

Nashornスクリプト実行時のクラスパス設定を容易にするためにNasven.jsを使う

Nashornというのは、Java8に同梱されている新しいJavascriptエンジンで、 Javaライブラリを呼び出して利用するJavascriptプログラムを作成することができる。 Node.jsで適当なライブラリが見つからない場合やJavaライブラリに精通していて ちょっとしたスクリプトを作成したい場合の良い選択肢になりそうだ。

Java8環境をインストールしたら標準で付属してくる「jjs」コマンドを使うことで 作成したNashornスクリプトを実行することができる。

Javascriptの言語仕様に加えて、いくつかJavaクラスを呼び出すための関数が 追加されている。Node.jsでnpmライブラリをrequireする感覚でJavaのライブラリを 呼び出せるようになる。

詳細は下記のユーザーガイド参照: Nashorn User's guide https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/nashorn/

ここからが今回の本題。

NashornスクリプトはJavaのクラスと同じ扱いなので Apache Projectや自作のJar等の標準以外のJavaライブラリを使おうとすると、 当然ながらクラスパスにそのJarファイルを追加して実行する必要がある。

jjsコマンドにはclasspathオプションがあり、ここでjarファイルを指定すれば それで実行できるのだけど、多くのjarファイルを使用している場合などは 毎回classpathを指定するのが面倒で、せっかくコンパイル無しでサクッと 実行できるのだからクラスパス指定ももっと簡単にしたい。

と思って探してみたら、「Nasven.js」(Nashorn + Maven + .js)というスクリプトが 公開されてた。これを使うとMavenと同じ感じでビルドファイルを作成しておき、 そこでクラスパス等のビルド情報を記述しておくことができる。

詳細は下記の記事参照: Nashorn Maven: Easy Library Classpath Setup https://blogs.oracle.com/brunoborges/entry/nashorn_maven_easy_library_classpath

試しにApache POIを使用するスクリプトを作ってみたら、うまく動作した。

nasven.jsを使用するNashornスクリプトの入手から作成・実行までの手順:

  1. Githubからnasven.jsをダウンロード
  2. nasven.jsようのビルドファイル(例:app1.mvn)を作成する
  3. メインのNashornスクリプトを作成する
  4. nasven.js経由でNashornスクリプトを実行する

ビルドファイルの例:

app1.mvn

var maven = { 
    main: 'app1.js',
    options: '-scripting',
    dependencies: [
      'org.apache.poi:poi:3.11',
      'org.apache.poi:poi-ooxml:3.11',
      'org.mongodb:mongo-java-driver:3.0.0'
    ]   
};

Nashornスクリプトの例:

app1.js

var Biff8EncryptionKey = Java.type('org.apache.poi.hssf.record.crypto.Biff8EncryptionKey');
var WorkbookFactory = Java.type('org.apache.poi.ss.usermodel.WorkbookFactory');
var XSSFWorkbook = Java.type('org.apache.poi.xssf.usermodel.XSSFWorkbook');
var MongoClient = Java.type('com.mongodb.MongoClient');
...

Nashornスクリプトを実行する(Linux環境の場合):

$ nashven.js -- app1.mvn

Windows7環境では下記のようにコマンドを変更したら動作した。

Nashornスクリプトを実行する(Windows環境の場合):

$ jjs -scripting nashven.js -- app1.mvn

Maven Centralからのライブラリのダウンロードも自動化されるので、 スクリプトの作成から実行までの手間が軽減され、手軽に使用できるようになった。

Apache Maven 3クックブック Javaソフトウェア開発のための特選レシピ集

Apache Maven 3クックブック Javaソフトウェア開発のための特選レシピ集

Javascriptでできることを調べて、メイン言語にするメリットを確認する

Javascriptプログラミング言語としてどうか、ということはさておいて、 「もうJavascriptさえ覚えておけば何でもできるんじゃない?」 というくらいJavascriptでできることが増えているので、それをまとめてみる。

Javascriptでできること

  1. Webサイトのクライアント側言語として使える
  2. Node.js+npmでWindowsも含めたクロスプラットフォームなバッチスクリプトが作れる
  3. Node.js+Gruntで作業自動化ツールが作れる
  4. Node.js+Express等でWebアプリのサーバサイド開発もできる
  5. Phantom.jsで擬似ブラウザを作成してキャプチャ取得等を自動化できる
  6. Java8以降ならNashornでJavaライブラリを使ったプログラムを作って実行できる
  7. JScriptExcel操作スクリプトが書ける
  8. Windows8アプリが作れる
  9. JXAでMac操作自動化スクリプトが作れる
  10. ElectronでクロスプラットフォームGUIアプリが作れる
  11. Atomテキストエディタの拡張に使える
  12. MongoDBのデータ操作に使える
  13. Microsoftのmainfold.jsでiOSAndroidWindowsアプリが作れる
  14. Google Apps ScriptもJavascriptベースな言語で作れる

つらくなったらこの記事を読み返して、モチベーションを回復しよう。 また思いついたら追加します。

パーフェクトJavaScript

パーフェクトJavaScript

Javascript(node.js)でExcelのシート一覧を出力する

node.jsはWindowsでもLinuxでも同じスクリプトが そこそこ安定して動くので嬉しい。

今回は、ExcelJSモジュールを使って、 Excelのシート一覧を出力するスクリプトを書いた。 リダイレクトでファイルに出力できる。 簡単なスクリプトだけど、こういうものを作っておくと必要な時にすぐ使えて便利。

output_excel_sheets.js

var Excel = require('exceljs');

var targetExcelPath = '<Excelファイルのパス>';

var workbook = new Excel.Workbook();
workbook.xlsx.readFile(targetExcelPath).then(function () {
    workbook.eachSheet(function (worksheet, sheetId) {
        console.log(worksheet.name);
    }); 
}); 

Node.jsとwebshotパッケージでWebサイトのスクリーンショットリスト作成を自動化する

Web開発を仕事にしていると

「マニュアルやプレゼンに使うのでWebアプリのスクリーンショットをくれ」

と依頼されることがよくある。

ChromeのFireshotのようなブラウザの拡張機能を使ったりすれば

1画面ずつ撮っていけるのだけど、

chrome.google.com

たくさんのページのスクリーンショットを、

変更がある度に撮り直すのは面倒なのでなんとか自動化したい。

「node-webshot」モジュールを使う

探してみると、

Node.jsの「node-webshot」パッケージを使えば

手軽にスクリーンショット撮影スクリプトが作れそうだ。

www.npmjs.com

Node.jsでWebKitブラウザの機能を利用可能にするPhantomJSの

スクリーンキャプチャ機能をラップしたものらしい。

PhantomJS | PhantomJS

PhantomJSはブラウザで行っている処理を

色々自動化するのに使えそうなので、また調査してみよう。

「node-webshot」パッケージは

下記のnpmコマンドでアプリにインストールできる。

npm install webshot --save-dev

URLのリストを作って、連続でスクリーンショットを撮るスクリプトを作成する

早速このwebshotパッケージを使って

スクリーンショット自動撮影スクリプトを作ってみる。

URLのリストを読み込んで

各URLのスクリーンショットを保存するようなものを作っておけば

画面に変更があってもコマンド1つで再作成できるので良さそうだ。

下記のようなスクリプトを作成してみた。

capture_screen.js

var webshot = require('webshot');
var fs = require('fs');

// 保存ファイル名とURLのリストを作成する
var baseUrl = 'http://test-domain.com';
var links = { 
    '01_login': baseUrl + '/login.html',
    '02_top': baseUrl + '/top.html',
    '03_shohin_list': baseUrl + '/shohin_list.html',
    '04_shohin_edit': baseUrl + '/shohin_edit.html'
};

// 各URLのスクリーンショットを指定したファイル名で「shots」フォルダに保存する
Object.keys(links).forEach(function (key) {
    var href = links[key];
    webshot(href, 'shots/' + key + '.png', {}, function () {}); 
}); 

URLリストの部分は撮影したいWebサイトのアドレスに合わせて変更する。

今回はデフォルトの設定で撮影を行っているが、

生成される画像のサイズ等はwebshot関数のオプションで指定できる。

サーバサイドJavaScript Node.js入門 (アスキー書籍)

サーバサイドJavaScript Node.js入門 (アスキー書籍)

  • 作者: 清水俊博,大津繁樹,小林秀和,佐々木庸平,篠崎祐輔,高木敦也,西山雄也,Jxck
  • 出版社/メーカー: KADOKAWA / アスキー・メディアワークス
  • 発売日: 2014/02/27
  • メディア: Kindle版
  • この商品を含むブログを見る

treeコマンドで出力したJSONをExcelで階層表示できるように変換する

あるプロジェクトのソース分析を行う必要があったので、

まずはソースファイルの一覧をExcelに出力してみることにした。

treeコマンドがいい感じで階層出力してくれるのだけど、

それをそのままExcelに持っていくと文字化けするので、

treeコマンドから一旦JSON形式で階層情報を出力し、

それをnodejsスクリプトで変換してcsvに出力する方法をとった。

treeコマンドで「-J」オプションを指定するとJSON形式で出力してくれる。

$ cd <プロジェクトディレクトリ>
$ tree -J &gt; file_tree.json

こうやって出力したjsonを下記のスクリプトでCSVにコンバートする。

階層毎に列がインデントされるようにして、

Excel上で拡張子でフィルタリングできるようにファイルの拡張子を出力する。

filetree2csv.js

//treeから出力したJSONをrequireで読み込み
var fileTreeJson = require('./file_tree.json');
var path = require('path');

//カンマの繰り返し出力のためのfunctionをStringに追加
String.prototype.repeat = function (num) {
    for (var str = ''; (this.length * num) > str.length; str += this);
    return str;
};

var parseFileList = function (level, fileNo, fileList) {
    fileList.forEach(function (file) {
        if (file.type != 'report') {
            var line = ''; 
            var type = ''; 
            if (file.type == 'file') {
                fileNo += 1;
                line += fileNo;
                type = path.extname(file.name);
            } else {
                type = 'dir';
            }   
            line += ',' + type;
            line += ','.repeat(level) + file.name;
            console.log(line);
            if (file.type == 'directory') {
                fileNo = parseFileList(level + 1, fileNo, file.contents);
            }   
        }   
    }); 
    return fileNo;
};

parseFileList(0, 0, fileTreeJson);

これを、下記のコマンドで実行する。

$ node filetree2csv.js > file_tree.csv

出力されたCSVをExcelで開き、タイトル行や罫線を整えたら、

ファイル一覧資料として使える。

Java8のJavascriptエンジンNashornでパスワード付きExcelを開く

以前Groovyで作成したパスワード付きExcelを開くスクリプトJavascriptでも 書いてみよう、ということでnode.jsで色々試してみたところ、パスワード付き Excelに対応しているライブラリが見つからなかった。 (普通にExcelを操作するだけならxlsjs等良さげなライブラリはあった)

他の手段がないかと調べてみると、Java SE 8に新JavascriptエンジンNashornが搭載され、 JavascriptからJavaのライブラリが使えるとのことだったので試してみた。

excel_decryption_test.js

var XLS_PATH= 'encrypted_file.xls';

var Biff8EncryptionKey = Java.type('org.apache.poi.hssf.record.crypto.Biff8EncryptionKey');
var WorkbookFactory = Java.type('org.apache.poi.ss.usermodel.WorkbookFactory');
var FileInputStream = Java.type('java.io.FileInputStream');

Biff8EncryptionKey.setCurrentUserPassword('<パスワード>');

var fis = new FileInputStream(XLS_PATH);
var workbook = WorkbookFactory.create(fis);
var sheet0 = workbook.getSheetAt(0);
var companyName = sheet0.getRow(4).getCell(1);
print('company Name: ' + companyName);

fis.close();

pom.xml

<project xmlns='http://maven.apache.org/POM/4.0.0' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
    xsi:schemaLocation='http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd'>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>sample</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>Maven Quick Start Archetype</name>
    <url>http://maven.apache.org</url>
    <dependencies>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.11</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.11</version>
        </dependency>
    </dependencies>
</project>

mavenで必要なjarをダウンロードしておき、 jdkのbinフォルダに含まれる「jjs」コマンドでclasspathを指定して Javascriptファイルを指定して実行する。

$ mvn dependency:copy-dependencies
$ jjs -scripting -cp target/dependency/poi-3.11.jar:target/dependency/poi-ooxml-3.11.jar:target/dependency/poi-ooxml-schemas-3.11.jar excel_decryption_test.js

うまくいった。

nashhornにはいくつかJavascript拡張関数が追加されており、 たとえばJavaのライブラリはJava.typeという関数を呼び出すことで簡単に使える。 クラスパス指定さえもうちょっと楽にできるようになれば、もっと使い勝手はよくなるはず。

他に、Java.load関数を使えばほかのjavascriptファイルを読み込むことができる。 これを使えば、jQuery等のライブラリを使って処理することも可能だろう。

Nashornの日本語ユーザーガイド

JavascriptライブラリとJavaライブラリを組み合わせて使えるのは、 色々と夢が広がって楽しみ。 文法としてJavascriptさえ使えるようになっておけば、Nashhorn、Node.js、JScript、ブラウザJavascriptなどの 実装を使い分けることで何でもできるようになりそう。

CakePHPとjQueryで汎用的なリンククリックカウンターを作成する

ページに記載した外部リンクが何回クリックされたかを知りたかったので、 リンククリックをカウントするスクリプトを作成した。 Google Analyticsのイベントトラッキング機能の簡易版。

リンクにあらかじめ決めたCSSクラスとリンクを識別するための属性を 設定しておき、リンクがクリックされた時にAjax処理でサーバに属性情報を 送信して記録する。

サーバ側はCakePHPで作成したけど、 大したことはしていないので他の言語でも実現できるはず。 1つコントローラを作成しておけばどのページからのリンクでもカウントできる。

HTMLソース

<html>
    <head>
        <script src='//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js'></script>
        <script type='text/javascript' src='/js/click_counter.js'></script>
    </head>
    <body>
        ...
        <a href='http://honjala.net/' class='click-count' data-linkname='ブログ'>ほんじゃら堂</a>
        <a href='https://twitter.com/honjala' class='click-count' data-linkname='Twitter'>Twitter</a>
        ...
    </body>
</html>

click_counter.js

$(document).ready(function () {
    (function($) {
         var ClickTrackService = {
            track: function (name, url, options) {
                var callerUrl = location.href;
                $.ajax({
                    'type': 'POST',
                    'dataType': 'json',
                    'url': '/click_tracks/track/',
                    'data': {
                        'name': name,
                        'url': url,
                        'caller_url': callerUrl
                    },
                    'success': options.success || function (data) {},
                    'error': options.error || function (xml, status, e) {}
                });
            }
        };

        $('a.click-count').live('click', function (e) {
            var linkName = $(this).attr('data-linkname');
            var url = $(this).attr('href');
            if (linkName != null &amp;&amp; linkName != '') {
                ClickTrackService.track(linkName, url);
            }
        });
    })(jQuery);
});

保存用テーブル定義

CREATE TABLE IF NOT EXISTS click_tracks(
    id integer not null auto_increment,
    name varchar(100),
    url text,
    caller_url varchar(255),
    ip varchar(20),
    created datetime,
    PRIMARY KEY (id)
) ENGINE=InnoDB;

click_tracks_controller.php

<?php
class ClickTracksController extends AppController {
    public $name = 'ClickTracks';
    public $uses = array(
        'ClickTrack',
    );  

    public function track() {
        $url = null;
        $name = null;
        $caller_url = null;
        if (array_key_exists('form', $this->params)) {
            if (array_key_exists('url', $this->params['form'])) {
                $url = $this->params['form']['url'];
            }
            if (array_key_exists('name', $this->params['form'])) {
                $name = $this->params['form']['name'];
            }
            if (array_key_exists('caller_url', $this->params['form'])) {
                $caller_url = $this->params['form']['caller_url'];
            }
        }

        $ip = $_SERVER['REMOTE_ADDR'];

        $this->ClickTrack->create();
        $track = array(
            'url' => $url,
            'name' => $name,
            'caller_url' => $caller_url,
            'ip' => $ip
        );  
        $this->ClickTrack->save($track);

        //JSONレスポンス処理
    }
}

本当はGoogle Analyticsのイベントトラッキングを使いたかったのだけど、 うまくクリックを認識してくれなかった。

jQuery MobileでiPhone用Webサイトを作成する

jQuery Mobile
http://jquerymobile.com/


いろいろはまったので、メモ。
バージョンは 1.0 ALPHA 2。

page毎にHTMLを分けて作成する

これは好みだと思うのだけど、URL直指定でそれぞれのページを呼び出せたりするようにするなら、分けた方が簡単な気がした。


構成としては、下記のような形にしてる。

<html>
<head>
    <!-- 全ページ共通のリソース読み込み -->
    <link rel="stylesheet" type="text/css" href="sample_base.css" />
    <script type="text/javascript" src="sample_base.js"></script>
  </head>
<body>
<div data-role="page" class="user-home-page">
<!-- このページ専用のリソース読み込み -->
<link rel="stylesheet" type="text/css" href="sample_user_home.css" />
<script type="text/javascript" src="sample_user_home.js"></script>
<!-- ページレイアウト定義 -->
<div data-role="header">
</div>
<div data-role="content">
</div>
<div data-role="footer">
</div>
</div>
</body>
</html>


jQuery Mobile はページ遷移時に遷移先の[data-role="page"]なタグだけを読み込むらしく、pageタグの外で定義されたcssやjsは読み込んでくれないので、pageタグ内でページ固有のリソースを読み込むようにしてる。


最初にURLを指定して呼び出したページのみ、完全なHTMLとして読み込んでくれるようなので、各ページを上記のような形にしておくことで直URL指定とページ遷移の両方に対応できるらしい。


ただ、ドキュメントを読んだところでは、JSやCSSについては1つにまとめておいて、表示するページ毎に処理を切り替えるのがオススメらしい。


pageタグの名前定義にclassを使っているけど、これはID属性が定義されているとページ遷移で呼び出した時にjQuery MobileがdivタグでラップしてIDを別途つけちゃうから。これをされるとページ遷移時とURL直指定時とでDOM構造が変わって色々面倒なのでclassを使用する。ドキュメントにも「classで指定すりゃいいよ」的なことが書いてあった。

ページ初期化処理

ページ遷移時にJSで初期化処理を行ないたい時は、$(document).ready(function () {}) ではなく、$(".ページクラス").live("pagecreate", function () {}) を使う。readyは最初のページ表示時点で呼び出し済みなので、2ページ目以降では呼び出されないっぽい。

$(".home-page").live("pagecreate", function () {
var page = $.homePage();
});

formタグに設定されたlive submitイベントが強敵

formタグにはあらかじめliveなsubmitイベントのハンドラが設定されていて、submitするとajax処理でページ遷移しようとしちゃう。

$('form').live("submit", function () { ... });


な感じで定義されてるので、特定のフォームのみsubmit処理をしないようにしたくてもうまくいかなかった。dieしたら他のページのフォームが効かなくなるし。設定($.mobile.ajaxFormsEnabled)で無効にした場合もやっぱり全フォームの送信処理が効かなくなっちゃう。


これに関してはまだ解決策は見つかっておらず、フォームを送信したくない場合はsubmit以外のイベントで処理するようにしてる。