0

jsondiffpatchでJSONの差分を取る

2つのJavaScriptのobjectの差分が取れる。JSONの差分が取れるCLIツールもついている。
https://www.npmjs.com/package/jsondiffpatch


元データ


wikiのページのようなデータを2つ用意する。増えた行、減った行、少し編集された行がある。
let page = {
title: "ruby",
lines: [
{text: "プログラム言語", user: "shokai"},
{text: "http://ruby-lang.org", user: "shokai"},
{text: "111", user: "shokai"},
{text: "2222", user: "shokai"},
{text: "33333", user: "shokai"},
{text: "444444", user: "shokai"}
],
updatedAt: Date.now()
};

let page2 = {
title: "ruby",
lines: [
{text: "プログラミング言語", user: "shokai"},
{text: "テキスト処理に便利", user: "kazusuke"},
{text: "http://ruby-lang.org", user: "shokai"},
{text: "111", user: "shokai"},
{text: "2222", user: "shokai"},
{text: "33333", user: "shokai"}
],
updatedAt: Date.now() + 300000
};


差分を取る、差分をパッチとして使う


jsondiffpatch.diff(left, right) で差分が取れる。patch、unpatchでちゃんと変更を適用できるか確かめる。
配列内のオブジェクトの比較のためにobjectHashという関数が必要。これはオブジェクトに持たせるのではなく、jsondiffpatch.createに引数として渡す。配列の比較をしない場合は必要ない。
https://github.com/benjamine/jsondiffpatch/blob/master/docs/arrays.md
また、patchとunpatch関数はパッチを当てた元データを破壊的に処理してしまうので、cloneしてからpatch/unpatchするようにした。

import {assert} from "chai";
import * as JSONDiffPatch from "jsondiffpatch";
import md5 from "md5"

const jsondiffpatch = JSONDiffPatch.create({
// https://github.com/benjamine/jsondiffpatch/blob/master/docs/arrays.md
objectHash: (obj, index) => {
return md5(obj.text + obj.user);
}
});

// patch/unpatchが元データを破壊的に処理する為必要
function clone(obj){
return JSON.parse(JSON.stringify(obj));
}

let diff = jsondiffpatch.diff(page, page2);

console.log(JSON.stringify(diff, null, 2));

// patchあててもちゃんと同一になる
assert.deepEqual(jsondiffpatch.patch(clone(page), diff), page2);

// patchあててpatch戻す
assert.deepEqual(jsondiffpatch.unpatch(clone(page2), diff), page);

assert.deepEqual(
jsondiffpatch.unpatch(
jsondiffpatch.patch(clone(page), diff)
, diff)
, page);


diffはdeltaというフォーマットになっている。差分なので元データにある1111や2222などの行は含まれない。
{
"lines": {
"0": [
{
"text": "プログラミング言語",
"user": "shokai"
}
],
"1": [
{
"text": "テキスト処理に便利",
"user": "kazusuke"
}
],
"_t": "a",
"_0": [
{
"text": "プログラム言語",
"user": "shokai"
},
0,
0
],
"_5": [
{
"text": "444444",
"user": "shokai"
},
0,
0
]
},
"updatedAt": [
1457784139836,
1457784439836
]
}


MongoDBで使う

まだ試してないけどmongooseのプラグインで、pre-saveで保存前後の差分を取得できるやつがある。
https://www.npmjs.com/package/mongoose-diff

これを使うと最新データのみ保存して、古いデータは変更履歴のみ保存するといったことが楽に実装できそう。

0

koa v2でsocket.ioを使う

koa 2.0.0-alpha.3
socket.io 1.4.5

app.listen(port)せずにapp.callback()すればhttp.Serverに渡せるハンドラが返ってくるとのことだったのでそのようにしたらできた。

import Koa from "koa";
const app = new Koa;

import convert from "koa-convert";
import koaStatic from "koa-static";
app.use(convert(koaStatic("./public")));

import {Server} from "http";
const server = Server(app.callback());

import SocketIO from "socket.io";
const io = SocketIO(server);

import pkg from "../../package.json";
app.name = pkg.name;

import logger from "koa-logger";
app.use(logger());

import router from "./route";
app.use(router.routes());

import Jade from "koa-jade";
new Jade({
app: app,
viewPath: "server/view",
debug: app.env !== "production",
noCache: app.env !== "production",
helperPath: [ ]
});

module.exports = {
app: app,
server: server
};


const server = require("./src/server").server;

const port = process.env.PORT || 3000;
server.listen(port);

あとは io.on(“connection”, (socket) => { }); とかで接続が取れる
クライアント側も普通に /socket.io/socket.io.js ができてる

0

node-lindaで電気をつける

nikezono君と山田くんが年末ぐらいに作ってた



  1. hubotが「電気つけて」コマンドを受けて{name:”light”, cmd:”on”, where:”delta”}をwrite
  2. たぶんRaspberry Piがこれと似たような感じで{name: “light”, cmd:”on”}をwatchしてサーボモーターを回してから、{name:”light”, cmd:”on”, where:”delta”, response:”success”}をwriteしているはず(ソースが公開されていない)
  3. hubotが{response:”success”}に反応し、「つけたと思う」をslackにmsg.send
  4. 照度センサーが付いたMacminiが{type:”sensor”, name:”light”, where:”delta”, value:数値}をwrite
  5. {type:”sensor”, name:”light”}でwatchしているherokuのスクリプトが変化を検知して{type: “hubot”, cmd: “post”, value: “〜〜で電気がつきました”}等をwrite
  6. hubot-lindaの組み込みで{type:”hubot”}に反応してslackに書き込む機能があるので、slackに「電気がつきました」等が書き込まれる

全然メンテとかしていないんだけど、2の部分みたいに勝手に誰でも接続できて拡張されちゃんと動き続けるようになってて便利

0

monorepo(lerna)の各packages下でコマンドを実行する

lernaで管理してるmonorepoの各packagesディレクトリの下に移動しながらコマンド実行するやつ作った

https://www.npmjs.com/package/lerna-run


使用例


各packagesに移動してbabelでコンパイル
順番に実行していく
% lerna-run babel src/ --out-dir lib/


各packagesに移動してbabelでwatchしてコンパイル
–parallelオプションをつけると順番にではなく一気に全packagesで実行する
% lerna-run --parallel babel src/ --out-dir lib/ --watch


lernaについては
lernaでmonorepoした
に書いた

0

Edisonに最新のNode.jsを入れた

Raspberry Piにnode.jsインストール
と同じ方法

Edisonのopkgにあるnodeが0.12とかで古すぎるので、自分で5.5を入れた。
CPUはAtomなので、linux-x86のビルド済みバイナリをダウンロードしてくればすぐ使える

http://nodejs.org/dist/

% wget http://nodejs.org/dist/v5.5.0/node-v5.5.0-linux-x86.tar.gz
% tar -zxvf node-v5.5.0-linux-x86.tar.gz
% mv node-v5.5.0-linux-x86 ~/node

最初から入っている/usr/bin/nodeを消す方法がよくわからなかったのでホームディレクトリに置いてしまった

PATHを前の方に通した

.bashrc
export PATH=$HOME/node/bin:$PATH