「JavaScript」と一致するもの

Meteorには様々な便利パッケージがありますが、 その中でもうまく使えばだいぶコーディングを省けるパッケージを紹介します。 autoformはフォームを生成するためのパッケージです。 MongoDBのスキーマを定義しバリデーションするsimple-schema とともに使います。 collection2とも使えます。 今回はsimple-schemaで説明します。

初めに、パッケージのインストールは、

meteor add aldeed:simple-schema aldeed:autoform check

です。バリデーション用のcheckパッケージも入れました。

例えば、毎日の体重を記録するアプリを想定しましょう。 コレクションの定義は、

Records = new Mongo.Collection('Records');

となります。これのスキーマを定義するには、

RecordSchema = new SimpleSchema({
  date: {
    type: Date
  },
  weight: {
    type: Number
  }
});

のようにします。この例は単純ですので直感的と思いますが、より詳しくはsimple-schemaのドキュメントを参照ください。

さて、このスキーマを使ってフォームを作成するには、

<template name="inputForm">
  {{> quickForm id="inputForm" schema="RecordSchema" type="method" meteormethod="/records/insert"}}
</template>

とします。これにより、

autoform_scn.png

このような感じでフォームが完成しました。このフォームはtypeに合わせて適切なコンポーネントが使用されている上、値が入力されていなかったり不適切な場合はアラートメッセージを表示してくれます。

フォームがsubmitされた際にはmeteor methodsが呼び出されます。この実装は例えば下記のようになります。

Meteor.methods({
  '/records/insert': function(doc) {
    check(doc, RecordSchema);
    Records.insert(doc);
  }
});

このcheckが重要で、これがないと不正なデータを登録できてしまいます。フォームのバリデーションはクライアントサイドだけの話なので。 このcheckすら冗長だと考える方はcollection2を使ってみるとよいでしょう。

autoformの最も簡単な紹介でした。これ以外にも色々柔軟な使い方ができるので、おすすめのパッケージです。

最後に全体のコードを載せておきます。これだけですので、まずは試してみるとよいでしょう。


HTMLファイル

<body>
  {{> inputForm}}
</body>

<template name="inputForm">
  {{> quickForm id="inputForm" schema="RecordSchema" type="method" meteormethod="/records/insert"}}
</template>

JavaScriptファイル

Records = new Mongo.Collection('Records');
RecordSchema = new SimpleSchema({
  date: {
    type: Date
  },
  weight: {
    type: Number
  }
});

Meteor.methods({
  '/records/insert': function(doc) {
    check(doc, RecordSchema);
    Records.insert(doc);
  }
});

Meteor 1.2からはAngularとReactのサポートも追加されましたが、Blazeも根強い人気があります。今回はそんなBlazeをちょっと使いやすくするパッケージを二つ紹介します。

初めに紹介するパッケージは、Handlebar-helpersです。

インストールは、

meteor add raix:handlebar-helpers

です。これを使うと、テンプレートで、

{{#if $lt counter 10}}

のようなことが書けるようになります。実は本来テンプレートにはロジックを書くのはよろしくないので、これはあまり好まれない場合があります。しかし、通常の書き方だと、

{{#if isCounterLargerThanTen}}

のようになりヘルパー関数を用意する必要があります。よって、小規模プロジェクトなどでは原則を破ってロジックを書いてしまった方が効率がよい場合もあると思います。

次に紹介するパッケージは、blaze-magic-eventsです。

インストールは、

meteor add themeteorites:blaze-magic-events

です。これも好みが分かれるところですが、イベントハンドラを書きやすくするものです。テンプレートに、

<button onclick={{sayHi}}>Say Hi!</button>

と書いて、JavaScriptコードに、

Template.helloworld.events({
  sayHi (event, template) {
    console.log('event and templateInstance params', event, template)
    template.$('p').html('hi there from sayHi() handler!')
  }
});

と書きます。これも微妙にロジックを書いてしまっているので美しくないのですが、jQueryに慣れている人は分かりやすいかもしれません。

最後にせっかくなので、自分のパッケージも紹介しておきます。blaze-showhideです。これもテンプレートに簡単なロジックを書いて表示・非表示を切り替えるようにするものです。

以上、テンプレートにはロジックを書かないという原則を破ってちょっと便利にするパッケージの紹介でした。

node.jsとかブラウザで簡単な統計処理をしようと考えて使えそうなライブラリがないか調べました。 できるだけ依存ライブラリがないものがいいです。

構築にMakeを使うと書いてアイルからネイティブコードがあるのかと思いましたが、そうではなくpure JavaScriptでした。 http://jstat.github.io/にドキュメントがあります。かなり色々な関数がそろっているようです。 検定などもできるようです。JavaScriptのArrayをそのまま渡すこともできるので、お手軽です。 GitHubのStarは712です。

こちらも同じような感じです。Arrayを使うのが基本です。回帰や分類もできるようです。 これが紹介ページですが、フォントがいい感じです。 GitHubのStarは821なので、jStatとこれがメジャーどころでしょうか。

こちらはCollectionやVectorといったクラスを導入して抽象化しているようです。 と思いましたが、作られたオブジェクトは普通のArrayとしても扱うことができ、とても便利そうです。 READMEにはJavaScript標準のMathと連携しているサンプルが載っています。 JavaScriptのprototype chainのおかげですね。魅力的です。 Star数は327です。

ドキュメントページはこちらです。 Star数は711なので初めの2つと同じくらいですね。ここまで来るとStar数だけでは判断できないでしょう。 Vector and Matrix math for JavaScriptというふれこみで、他のものよりMatrix系が充実しているようにも見えます。

READMEにはほとんど情報がないのに、Star数が530という状態が不思議です。最近更新されていないところを みると過去に集めたStarでしょうか。それともどこか別のところにドキュメントページがあるのでしょうか。 Rっぽいことができるらしいです。コードとしては枯れているのかもしれません。

これまでのライブラリと比べると小規模なもので、Arrayを拡張して基本的な関数群を追加してくれます。 機能は少ないですが、これだけがやりたい場合はいいかもしれません。Star数は23です。


最後に、こんなページを見つけました。 中身はよく理解していませんが、JavaScript(というかv8)は意外と速いと言う気付きだそうです。

http://julialang.org/には新しく、もう少し見やすいベンチマークの表が載っています。

上記のライブラリたちのベンチマークも気になるところです。

ブラウザ側に情報を取っておく手段としてlocalStorageがあります。 昔はcookieを使っていたことが多く、今でもまだよく使われているのですが、 モダンブラウザ対応ならlocalStorageの方が適しているのではないかと思います。

以前、JavaScript nativeでlocalStorageを使うコードは書いたことがあって、 別に不満はなかったのですが、AngularでlocalStorageを簡単に扱えるライブラリがあると 思って調べてみました。

このあたりがメジャーそうです。順当に行くとStar数が一番多いangular-local-storageを選ぶのですが、 じっくりREADMEを読んでみます。APIは自明で分かりやすいです。bindはangularならではのAPIで便利そうです。 localStorageが使えない場合はcookieにfallbackするそうです。APIは抽象的に作られているということですね。

さて、2つ目のangularLocalStorageはどうでしょう。名前が似ていますが別物です。 bindもあるしAPIもシンプルで分かりやすいです。cookieへのfallbackもあるそうです。 angular-local-storageと比較すると若い分、Star数が少ないだけでしょうか。issueも処理されて アクティブに見えます。でも違いがない分、ドキュメントが充実しているangular-local-storageを選びたくなります。

最後の、ngStorageは、in the Angular Wayということで少し変わった感じです。

No Cookie Fallback - With Web Storage being readily available in all the browsers AngularJS officially supports, such fallback is largely redundant.

だそうです。確かにangularを使うようなブラウザでは、localStorageをサポートしていないケースは無いですね。 これは思い切りがよいです。気に入りました。

$scope.$storage = $localStorage;

使い方もこれだけです。APIとかそういうレベルではない感じ。とてもいいです。

And use it like you-already-know

だそうです。気に入りました。 これはつまり、localStorageを加えた3-way data bindingということですね。 これは応用が広がりそうです。時間があったらコードも読んでみたいです。

というわけで、おすすめはngStorageでした。

Gunosy RSSのURL変更その他

  • 投稿日:
  • by

久しぶりにgunosy-rssのコードをいじりました。

まず、URLを変更しました。新しいURLは、

http://gunosy-rss.axlight.com/

です。以前のURLでアクセスした場合は301 Redirectされます。 RSSリーダーがちゃんとURLを書き換えてくれればよいのですが、 おそらくすべてのRSSリーダー/アグリゲータが対応しているということはないでしょう。

今後、旧URLではアクセスできなくなることがあるかもしれません。 というのも、herokuの無料枠で動かしているのですが、その条件が変わるためです。 将来的にDNSでアクセス先を変更したりすることを想定してのことです。

しかし、当面はそんな面倒なことをするつもりはありません。 とりあえず、無料枠で動かし続けるためには、6時間スリープさせればよいようなので、 rssのttlを設定してみました。今後RSSリーダー/アグリゲータがttl正しく解釈してくれれば、朝の5時以降と夕方の5時以降に取得するようになるはずです。 しかし、これはもっと早く実装しておけばよかったですね。 ちょっと様子を見た感じだとアクセス頻度がだいぶ減ったように思います。 503 Retry-Afterが返ることも減ったようですし、本家Gunosyへのアクセス負荷も下がるかもしれません。

JavaScriptのDateのタイムゾーンが手元とherokuで異なっていて、はまりました。 ちょっとスマートでない方法で解決したのと、もともとのコードがスマートでないので見苦しいですが、ttlの算出周りに興味があればコードを見てみてください。 たぶんきっともっといい書き方があるはずです。

他にも今から見ると書き直したい部分があったり、そもそもライブラリのアップデータもしたいのですが、そこまでモチベーションがわきません。まあそのうち、ついでがあれば。


5/24追記。

タイムゾーンを設定する方法がありました。

$ env TZ=Asia/Tokyo node app.js

これでコードはシンプルになりました。

https://github.com/dai-shi/gunosy-rss

今さらながらbrowserifyを使ってみました。 存在は知っていたものの普段はcdnを使ってライブラリを読み込むようにしていたので、 必要性を感じていませんでした。 今回、ライブラリをローカルに保存したかったので使ってみました。 bowerと比較してnpmだけで済むのがいいです。

しかし、npm上のライブラリが対応しているかどうか分からないというのは難点ですね。 実際に使おうとしていたライブラリがうまくラップしていないものがありました。 そこで、browserify-shimを使いました。 これは、CommonJSでexportしていないライブラリをexportしたかのように使えるようにするものです。 関連して、napaを思い出しました。 うまくやればnapaとbrowserify-shimの組み合わせは最強ではないでしょうか。

browserify-shimの使い方はあまり検索しても見つからなかったので、メモしておきます。 基本的にはREADMEに書いてあるのがそのままなのですが、一瞬理解しにくかったです。

以下、package.jsonの中身の書き方です。まず、

{ 
  "browserify": {
    "transform": [ "browserify-shim" ]
  }
}

これが必須です。これにより、browserify-shimが認識されるようです。 transformは他にも用途があるのでしょうか。それも興味あります。 次に、

{
  "browserify-shim": {
    "./node_modules/foo/bar.js": { "exports": "Bar" }
  }
}

これはどのファイルのどのようにラップするのかを記述します。 詳細は調べていませんが、これはbar.jsのファイルの最後に

exports = Bar;

を追加したような感じです。もちろん、function(){}でスコープを切っているでしょうが。 さて、一番悩んだのは、どうやってこれを利用するかでした。 状況としては、./public/javascripts/hoge.jsというファイルでどのように使うかというものです。 これは、

var bar = require('../../node_modules/foo/bar.js');

としたら動きました。パスが違うのが曲者です。

改めて、これはnapaと組み合わせると便利かもしれません。 最近自分でよく使っているcdnとhttp://rawgit.com/の組み合わせに匹敵するかも。


5/13追記。

よりよい設定方法が分かりました。というか単にbrowserify自体の設定方法をよく理解していなかっただけでした。 package.jsonを、

{ 
  "browserify": {
    "transform": [ "browserify-shim" ]
  },
  "browser": {
    "bar": "./node_modules/foo/bar.js"
  },
  "browserify-shim": {
    "bar": { "exports": "Bar" }
  }
}

のように書けば、

var bar = require('bar');

として利用することができました。 分かってしまえば簡単なのですが、browserify-shimのドキュメントだけでは理解できなかったです。

AngularでBootstrapを使うライブラリとして、以前の記事では、UI Bootstrapを紹介しました。

最近、AngularStrapも使ってみたので感想を書いておきます。

AngularStrapは初期の頃に名前を聞いたことがあるような気がします。確か、bootstrap v2の時代に。今では、AngularStrapもv2になってjQuery依存がなくなりました。いい時代の流れです。

さて、AngularStrapはUI Bootstrapと比べると根本的な設計思想は似ています。どちらも、bootstrapのcssだけ再利用して、javascript部分は作り直し、jQueryには依存しない、Look&Feelのカスタマイズ性を考慮している、と言ったところです。

しかし、使ってみるとだいぶ考え方に違いがあるような気がします。 UI Bootstrapはできるだけシンプルにbootstrapのcssを使えるようにしている一方、AngularStrapはbootstrapのcssをベースにしてよりリッチな機能を提供しようとしているようです。最大の違いは、アニメーションのサポートでしょうか。

簡単な例として、Alertの違いを見てみます。最初は、UI Bootstrap。

<alert type="{{alert.type}}" close="closeAlert(alert)">{{alert.content}}</alert>

これだけです。ng-repeatを使う例がドキュメントに載っていますが、直感的です。ng-showとも組み合わせられます。

次に、AngularStrapです。いろいろあるようですが、attributeを使う例。

<button class="btn" title="{{alert.title}}" data-content="{{alert.content}}" data-type="{{alert.type}}" bs-alert>Show alert</button>

buttonではなくdivで直接表示しようと思いましたが、できませんでした。ドキュメントではできそうなので、何か間違えているのかもしれません。 ng-showは使わず、オプションか属性でshowフラグを指定します。このあたり思想が異なる感じです。durationとか指定できるのは便利です。 alertは単純な機能なので、そこまで違いませんが、他の機能はもっと違いがありそうです。

個人的には、ui-bootstrapの方が使いやすいです。しかし、AngularStrapでアニメーションが簡単に使えるのも魅力的です。使い分けることにします。

人に勧めるのはどちらがよいか迷います。勉強が目的ならui-bootstrapでしょうか、リッチな機能が欲しいならAngularStrapでしょうか。

LiveReloadをご存知でしょうか。Web開発をするときに、ブラウザのリロードを省く機能を提供するものです。 類似のものとしては、Live.jsというのもあります。 また、Meteorでも実装されています。

一言で言うと、websocketでコネクションを維持して、サーバ側のファイル変更を通知して自動でリロードするものです。 CSSの場合はリロードせずに変更を適用することもできます。 livereloadは商用のようですが、そのコアのソースコードはMITライセンスで公開されています。

livereloadはブラウザ拡張を使うことがメインのようですが、javascript版のクライアントもあります。 javascript版だと、mobile safariでも動きます。 livereloadのnode.js用のサーバは、https://github.com/livereload/livereload-serverで公開されていますが、 これはそのまますぐには使うことができません。 平たく言うとプロトコルが実装されているだけで、実際に更新通知する機能は含まれていません。

そこで、簡単に使えるライブラリとしてまとめてみました。特徴をまとめると、

  • livereloadのサーバコードの最新版を使用(現在npmに登録されているlivereload系のモジュールはほとんど旧バージョン)
  • livereloadのクライアントコードの最新版を使用(npm化されていないのでnapaというモジュールで導入)
  • fs.watch()を使ってファイル変更を迅速に監視(node-watchというモジュールを使用)
  • node-devを使うことでサーバ側のファイル変更を監視して、クライアントを自動リロードする機能を追加
  • これらの機能をたった一行を加えるだけで使えるようにパッケージ化

となります。

プロジェクトページは下記です。

https://github.com/dai-shi/easy-livereload

最低限の使い方は、

app.use(require('easy-livereload')())

ですが、細かい設定はオプションを引数に与えることでできます。

本質的には他人が作ったライブラリを結合して簡単に使えるようにしただけですが、 相当便利だと思いますので、ぜひお試しください。

最近、簡単なスライドをWebにアップするときにgistを使っているので、紹介します。

gistを使うと、スライド内のテキストをちょっといじるときなどにブラウザで作業できるので便利かなと思いました。実際はそれほどでもないのですが。

gistを使ってプレゼン資料を作るコツは二つです。

  • reveal.jsを使って1つのHTMLファイルだけで完結させる
  • http://rawgit.com/を使ってブラウザから開けるようにする

reveal.jsは以前紹介したので省略します。

rawgitはgithubのraw fileにcontent-typeをつけたり、cdnでキャッシュしてくれるサービスです。すばらしいですが、永続性は不明です。しかし、代替サービスがあれば乗り換えればよいだけとも言えます。

具体的なサンプルを見た方が早いかもしれません。以下に示すサンプルは、1つのHTMLファイルで、reveal.jsを読み込んで、そのHTML内でJavaScriptを書いて、Markdownを書いています。

まず、ソースはこちらのgistです。

次に、rawgitで開けるようにしたものがこちらです。

「実際はそれほどでもない」と言った理由は、gistを編集するとリビジョンが上がり、rawgitのURLも変わるからです。キャッシュされるのでそうでないと困るのですが。

なんか時代に逆行している感じなので、あまり受けないかもしれません。

GitHub便利です。その一つが、Compare ViewのDiffです。Pull Requestを確認するときもよく見ます。コマンドラインでgit diffと普通にやるより見やすいです。

さて、背景は省略しますが、HTMLでdiffを表示したいと思いました。AngularJSでの利用を想定しているので、JavaScriptのライブラリが欲しいです。

ところが探してもあまり見つかりません。よくまとまっているのはこちら。

http://stackoverflow.com/questions/3053587/javascript-based-diff-utility

ちょっと古い投稿ですが。一番人気なのは、これです。

https://github.com/cemerick/jsdifflib

オンラインデモはこちら。

http://cemerick.github.io/jsdifflib/demo.html

だいぶ求めているもののイメージに近いです。 コードも枯れているらしい。 HTMLが出力されるので、Angularで使うならdirectiveを書かなければいけないですが、それは仕方ないですね。

これで行こうかと思ったところでしたが、なにか物足りません。 なんというか、見た目がワクワクしないのです。 改めてGitHubのDiffと比較してみると、全体的に色が濃すぎる。 あと、単語の差分がハイライトされない。色については自分でcssを書いて調整すればよいでしょうが、単語のハイライトはそれでは済みません。

というわけで、もう一度検索しました。見つけたのがこちらです。

https://github.com/rtfpessoa/diff2html

オンラインデモはこちら。

http://rtfpessoa.github.io/diff2html/

おお、おしゃれ。これですよこれ。なんとStar数は3でした。まったく注目されていません。コードも枯れていないでしょう。まだ不具合もあります。でも、魅力的です。これを使いましょう。

ところが、使うには一つ問題がありました。diff2htmlはdiffの出力を入力としているのです。例えば、

diff --git a/foo.txt b/foo.txt
---
+++
@@ -1,4 +1,5 @@
+zero
 one
 two
-three
+tree
 four

このようなものです。つまり、次はこれを出力するライブラリを探しました。いくつかありそうでしたが、比較的すんなり結合できたのはこちらです。

https://github.com/qiao/difflib.js

入力の行が配列になることと、出力の一行目のヘッダが付かないことなど不足点はありまして、それらは自力で対応しました。

最後にこれらを全部つなげて、angular.jsのdirectiveを書いてできあがりです。下記のような感じで使えるようになりました。

<my-diff path="foo.txt" old-content="one\ntwo\nthree\nfour\n" new-content="zero\none\ntwo\n\tree\nfour\n">

便利です。ちょっと気になることと言えば、依存javascriptが3ファイルもあって、グローバルに関数を定義するようなものであることです。なにかうまく隠蔽する方法があるのでしょうか。