あと味

たくさん情報を食べて、たくさん発信すると、あとになって味わい深い。

Movable Type と Web Components

この記事は Movable Type Advent Calendar 2017 の17日目の記事です。

先日、会社にて、事業部の先輩で、Polymer Japan の運営者でもある @sizuhiko さんより、Polymer のハンズオンを受ける機会があり、遅ればせながら Web Components に入門しました。

Polymer は Google が開発を主導しているライブラリで、Web Components のためのポリフィルでもあり、Web Components を簡単に扱えるようにする機能なども提供するライブラリです。

Web Components は、すでに使える次世代の Web 標準テクノロジーで、下記の4本柱からなるテクノロジーです。

  • カスタムエレメント
  • シャドウ DOM
  • HTML インポート
  • HTML テンプレート

私が下手な説明をするより、ググったり、@sizuhiko さんの下記のスライドなどをご参照いただいた方が良いと思います。

それで何ができるの?

Web Components を使うと HTML, CSS, JavaScript で作られた自己完結したコンポーネント(カスタマイズ箇所の定義も自由)を他者に提供したり、または提供されたものを自分のサイトで扱うことができるようになります。

例を見た方が早いと思うので、webcomponents.org を覗いてみてください。このサイトには共有された Web Components がたくさん掲載されていて、例えば Google Maps の機能を扱う Web Components は下記のように提供されています。

Google Maps の Web Components では google-mapgoogle-map-marker といったカスタムタグと、緯度、経度などの専用の属性を指定することで任意のマップを描画できます。

例: 福井県庁の地図を描画する場合

gistba02fd28ccbf22b65f76ceffbe60f90f

他にも、Android 等の UI などで使用されている Material Design 用のコンポーネントpaper- というプレフィックス付きで提供されていたりするので、Material Design を採用する UI などは、CSS を使った煩わしいスタイリングをしなくても、Web Components の技術を使って専用のタグで宣言的に記述できます。

上記の例は汎用的なコンポーネントの例となりますが、自社用のコンポーネントを Web Components を使って構築するというユースケースも当然あり、YouTube の新 UI などはまさにそれに該当するのかなと思います。

YouTube の新 UI では、ブラウザの開発者ツール等で要素を調査すると、ytd-yt- というプレフィックスの付いたカスタム要素で、UI が構築されていることが確認できます。

それと Movable Type と何が関係あるの?

MT の肝となる機能の一つに MT タグがあります。

MT で管理されているコンテンツは、MT タグを使って取得・加工・表示することができます。単にそれだけに留まらず、外部の API からデータを取得したり、HTML のプリプロセッサとして動くマクロのように HTML を置換したりなど、様々なことが MT タグを通して実現できるようになっています。PowerCMSテンプレートタグリファレンスを見れば、プラグインで追加したあらゆる機能が、オリジナルの MT タグを定義することで活用できるようになることがわかります。

MT タグが CMS で管理しているコンテンツを扱うための一種のカスタムタグと考えると、Web Components との共通性が見いだせます。

また、MT で管理するコンテンツや機能を定義して、それらを取り出したり、加工するための MT タグを定義して、テンプレート上で HTML と MT タグを記述し、必要な表現を定義するというワークフローが、Web Components のワークフローのそれと類似しているので、MT を使ったサイト制作やウェブアプリケーション制作のワークフローと親和性が高いように思います。

例えば、MT で管理するコンテンツの内、静的に扱いたいコンポーネントは MT タグで定義して、動的に扱いたいコンポーネントは Web Components で定義するといった使い分けができそうです。

試しに下記のようなカスタム要素を作ってみました。Polymer を使うと、このくらいのものであれば、ロジックを書くことなく簡単に作れました。

github.com

bower install https://github.com/taiju/mt-entries-list-sample.git でインストールした後、下記のように HTML インポートして使います。

gistef21bb0771928287f15974c9660e31d8

mt-entries-list というカスタムタグにサイトの ID や、検索パラメータを属性に指定すると、DataAPI を通して動的に記事一覧のリンクを ul > li > a のリストとして描画することができます。

このサンプル自体に実用性はありませんが、これをベースにして、サイトやアプリケーション専用に作り込めば動的に描画できる最新記事一覧や関連記事一覧など、実用なコンポーネントも作れると思います。

まとめ

MT 7 ではコンテンツタイプという機能が追加されるので、何を、どこに、どのように管理するかを、自由に定義できるようになり、よりコンポーネント指向な情報設計ができるようになります。

MT 7 向けの新テーマである Jungfrau(ユングフラウ) を見ると、テーマでオリジナルのコンテンツタイプを提供することも可能なようなので、コンテンツタイプと一緒に、そのコンテンツタイプを取り扱うための Web Components もテーマに同梱するといったユースケースが想像できます。

例えばアンケートを管理するコンテンツタイプと、DataAPI を経由して、アンケートの描画・回答ができる Web Components をセットで提供すれば、アンケートアプリケーションコンポーネントの配布が、テーマの配布だけで実現できそうだなぁと勝手に想像しています。

単純なサイト制作ではなかなか活用の場がないのかもしれませんが、Web Components は将来当たり前に使う技術になると思われるので、MT でどのように活用できるか考えると、いいろいろ夢が広がりそうですね。

ほなの。

MT のドキュメントを Emacs から引けるようにした

SLIME をインストールすると同時にインストールされる hyperspec-lookup コマンドのように、MT のドキュメントを検索できると良いなと思ったので、hyperspec-lookup を模倣して、パッケージにした。

mt-doc-lookup

https://github.com/taiju/mt-doc-lookup-el

インストールすると、M-x mt-doc-lookup-tags で、コマンドで指定した MT タグのドキュメントをブラウザで閲覧できるようになる。

同様に、M-x mt-doc-lookup-config-directives環境変数M-x mt-doc-lookup-modifiers でグローバル・モディファイア、M-x mt-doc-lookup で MT タグと環境変数とグローバル・モディファイアを横断的に検索し、閲覧できる。

リポジトリにも貼っている、利用時のスクリーンショットが下記。

f:id:jdg:20150517231524g:plain

スクリーンショットでは、ido + smex + ido-vertical-mode がインストールされていて、有効にしている状態での操作内容なので、それらが入っていない場合は、初期状態の Emacs の補完機能と同様、微妙な使用感になる。helm など、他の標準の補完機能を拡張するパッケージが入っているなら、その使用感で使えると思う。

mt-doc-lookup の横断検索は、リストから検索するのに対し、mt-doc-lookup-tags のように用途を絞る検索は、ハッシュから検索するので、多分、用途を絞った検索の方が速いと思う。

特に各コマンドを標準ではキーバインドはしていないので、お好みで。

標準では eww でドキュメントを開くようにした。(mt-doc-lookup-browser-function 変数で変更可能)

ドキュメントを閲覧するには eww で必要十分だし、コピペビリティも高くてオススメです。

検索の各候補は下記のワンライナーで出力したものを加工した。もしかしたら、足りてなかったりするかもしれない。

$ perl -Mojo -E 'say g($ARGV[0])->dom->find(q{a[rel="bookmark"]})->map(sub { sprintf q{("%s" "%s")}, $_->text, $_->{href} =~ s|^$ARGV[0]||er })->join("\n")' http://www.movabletype.jp/documentation/appendices/tags/

最初は Emacs lisp でがんばろうかと思ったものの、標準の道具では厳しそうだったので、やめた。

Mojolicious + Perl でのワンライナーだいぶ便利なので、代替なかなかない。

まとめ

これまでも、タグリファレンス等を eww のブックマークから開いて閲覧していたので、ドキュメントへのアクセスのステップが短くなり、今後多少なり捗る気がする。

ただ、ドキュメントがリニューアルしたら、即ゴミになるのが辛い。可能な限り追従したいとは思っている。

MT の管理画面を開くコマンドラインランチャー作った

この記事は、Movable Type Advent Calendar 2014 の 21 日目の記事です。

最近になって、遅ればせながら peco を使い始めました。汎用的なインタラクティブフィルタリングツールなので、どんなコマンドとも組み合わせて使うことができるし、ないと困るツールになりそうな気配です。

本題ですが、MT の特定の画面をサクッと開けるランチャーを作りたいなと以前から思っていたんですけど、一から考えて作るのは厳しいのでほぼボツ案だったのですが、peco または percol を使えばそれなりに満足するものが作れそうだなと思ったので、昨日作ってみました。

MT::Tool::Launcher

$ cd $MT_HOME
$ ./tools/launcher --protocol=https --host=cms.example.com --cmd=/path/to/peco

実行例

例1) あるウェブサイトの記事作成画面を開く

https://github.com/taiju/mt-tool-launcher/raw/master/artwork/launcher1.gif

例2) すべてのブログのプラグイン設定画面を開く

https://github.com/taiju/mt-tool-launcher/raw/master/artwork/launcher1.gif

上記のような感じで使います。

所感

ローカルマシンに環境作って開発してるケース以外では使うことないと思いますが、自分はローカルマシンで開発することが多いので、あると便利かなと思っています。

一応 Windows でも動くように作ったつもりなんですけど、手元に検証に使える環境がないので動かなかったらごめんなさい。MacUbuntu では動作確認してます。

peco や percol は汎用的なツールなので、他のツールスクリプトにもいろいろ応用ききそうで、発想が広がる感じがします。

ということで 21 日目の記事でした。引き続きお楽しみに。

ほなの。

MTの管理画面でMT標準のJSテンプレートエンジンを使う

管理画面のカスタマイズをしていて、JSでDOMを弄ってHTMLを出力しようと思った時に、コードの見通しを確保するためにJSテンプレートエンジンが欲しいなと思いました。

ただ、そのためにライブラリ読み込むのも微妙だし、正規表現で頑張るかと思ったところ、管理画面でJSテンプレートエンジンが使われてたことを思い出しました。

MTの管理画面で使われている、JSテンプレートエンジンのライブラリは、/path/to/mt-static/js/common/Template.js にあって、実装を確認したところ、シンプルで汎用的に使える小さなライブラリっぽいので、下記の通り機能をまとめました。

ちなみにこのライブラリは、数年前から更新が止まっていて、Github リポジトリの当該ファイルのコミット履歴も、「Common JavaScript ライブラリをリポジトリに入れました」的なログが一つあるだけのレベルです。

メンテが止まっていて、いつ役目を終えるかわからない感じの存在感なので、その点はご留意ください。

この記事の賞味期限もそれまでです。

使い方

下記のような感じで使えます。

var tmpl = new Template('Hello, [#= name #]!');
var context = new Template.Context({ name: 'taiju' });
tmpl.process(context);
// => "Hello, taiju!"

/*
下記で使う変数tmpl_textには下記のテンプレート文字列が保存されていると仮定する
  <ul>
  [# jQuery.each(vars, function (i, v) { #]
    <li>[#|h|u v#]</li>
  [# }) #]
  </ul>
*/
tmpl.compile(tmpl_text);
context.vars = { vars: ["<script>alert('foo');</script>", "バー", "baz"] };
tmpl.process(context);
/* =>
"<ul>

  <li>&lt;script&gt;alert('foo');&lt;%2Fscript&gt;</li>

  <li>%E3%83%90%E3%83%BC</li>

  <li>baz</li>

</ul>"*/

Underscore.jsのテンプレート機能のように、普通に言語の機能がそのまま使えるタイプのシンプルなテンプレートエンジンです。この手のテンプレートは学習コストが低く、言語のパワーがそのまま使えるので、個人的に好むタイプです。

管理画面はjQueryがロードされているので、jQueryのユーティリティを組み合わせてテンプレートを書くのが良いかもしれません。

ライブラリの機能

Template.jsによって、Template, Template.Context, Template.Filterというクラスが提供されます。

Template.jsは、Core.jsに依存しているので、利用するにはCore.js等の依存ライブラリがあらかじめ読み込まれている必要があります。

主に使うのはTemplateクラスになりそうですが、先のサンプルのようにTemplateクラスのインスタンスを作って処理する他、Templateクラスのクラスメソッドを使って、JSテンプレートをビルドすることもできます。

var templates = {
  foo: '[#= foo #]'
};
var vars = {
  foo: 'FOO'
};
Template.process('foo', vars, templates);
// => "FOO"

クラスメソッドの方が手軽に使えるかもしれません。

テンプレートの記法

[#から#]で囲まれた部分が JS テンプレートの記述部分になります。

JavaScript の任意の式や文が記述でき、式の結果を出力したり、出力結果にフィルタをかけたりできます。また、Template.ContextのインスタンスをTemplateクラスのインスタンス(compile済み)の持つ、processメソッドに渡すことで、テンプレートに任意の変数を渡すことができます。

[##]というテンプレートの開始位置、終了位置を指定するための文字列は、TemplateクラスのインスタンスbeginTokenプロパティとendTokenプロパティを変更することで変更可能です。

頻繁に使うことになるのは、[# ... #] と [#= ... #] ですかね。テンプレートエンジンでよく見かける形式だと思います。

テンプレートのサンプル

[# ... #]

[# if (false) { #]
ここは出力されない
[# } #]

[#から#]で囲まれた範囲では、任意のJavaScriptの式や文を評価することができます。評価内容は出力されません。

[#-- ... --#]

[#-- 下記でゴニョゴニョする --#]
ゴニョゴニョ

[#--から--#]で囲まれた範囲はコメントになります。コメント中の式や文は実行されず、出力もされません。

[#= ... #]

[#= true ? 1 : 0 #]

[#=から#]で囲まれた範囲は、任意のJavaScriptの式を記述でき、評価された値が出力されます。

[#* ... #]

[#= foo #]
[#* return #]
[#= baz #]<!-- この変数の値は出力されない -->

[#*から#]で囲まれた範囲は、定義された特定のコマンドを記述できます。

とは言え、実際には、returnしか定義されておらず、returnコマンドを記述すると、その記述以降のテンプレートは評価されません。

[#| ... #]

[#|i "foo ${bar} baz" #]<!-- ${foo} や $foo という記述が変数展開される -->
[#|h "<script>alert(1);</script>" #]<!-- HTMLをエスケープする -->
[#|H "&lt;script&gt;alert(1);&lt;/script&gt;" #]<!-- HTMLをアンエスケープする -->
[#|u "テスト" #]<!-- URLエンコードする -->
[#|U "%E3%83%86%E3%82%B9%E3%83%88" #]<!-- URLデコードする -->
[#|lc "ABC" #]<!-- 小文字にする -->
[#|uc "abc" #]<!-- 大文字にする -->
[#|substr(0, 3) "abcdefg" #]<!-- 指定した位置から指定した位置まで文字を切り出す -->
[#|ws "   foo   " #]<!-- 前後の空白を除去する -->
[#|trim(3) "abcdefg" #]<!-- 引数で指定した文字以降を…(U+2026)で省略する -->
[#|date "2014-06-05T00:00:00+09:00" #]<!-- ISO形式の日付文字列をISO形式の年月日文字列に変換する -->
[#|localeDate "2014-06-05T00:00:00+09:00" #]<!-- ISO形式の日付文字列をロケールに合わせた表記に変換する -->
[#|rt "<script>alert(1);</script>" #]<!-- タグを削除する(内容は残す) -->
[#|rp(/foo/g,"bar") "foobarfoobar" #]<!-- 引数に指定した文字を引数に指定した文字に置換する -->

[#|から#]で囲まれた範囲は、評価された式の値に、定義されたフィルタを適用した上で値を出力します。

上記の中でも、iフィルタや、dateフィルタは、内部で使っている関数がCore.jsに依存しています。

MTのJSライブラリあまり調べたことなかったので意識していなかったんですが、結構グローバルオブジェクトを拡張してるようです。

rpフィルタはうまく動いていなかったので、PRしました。

また、フィルタはUnixのパイプのような感じで、複数組み合わせることができます。

[#|ws|rt|uc "   <span>abcd</span>  " #]<!-- 前後の空白を除去してから、タグを除去して、内容を大文字にする -->

Template.Context, Template.Filter について

Template.Contextには、いろいろメソッドが用意されていますが、Templateクラスを通して透過的に利用することがほとんどな気がしたので、紹介は割愛します。フィルタに関しても、基本はテンプレート内に記述するケースがほとんどでしょう。

Template.Contextのincludeメソッドは単体でも使うかもしれません。

var templates = {
  header: 'Header!!',
  body: '[#= context.include("header") #] Body!! [#= context.include("footer") #]',
  footer: 'Footer!!'
};
Template.process('body', {}, templates);
// => "Header!! Body!! Footer!!"

まとめ

よく使う機能に関しては、上記で紹介した通りです。あとはソース読んで確認してください。

昔はいろいろとMT標準のJSライブラリについて、ナレッジが共有されていたのかもしれませんが、今は、ググってもあまり情報が見つからない感じがします。

Template.jsに限らず汎用的に使えそうなライブラリもありそうですが、ドキュメントないし、自分のような新参者はソース読むしかない感じです。公式ドキュメントが待たれるところ。

Template.jsは、汎用的に使えるテンプレートエンジンだと思うので、MTの管理画面のカスタマイズ用途等で、特に他のライブラリを読み込む予定が他にない時は、このJSテンプレートエンジンを使おうかなと思ってます。

ほなの。

PSGI + SeleniumでMTの管理画面のテストをする

MTの管理画面は、JavaScriptに依存した部分が結構多く、PhantomJS等のヘッドレスブラウザを使ったテストが必要になるケースがあります。

CasperJSや、Seleniumなど、いろいろなツールがありますが、MTの管理画面のロジックは、Perlで書かれているので、Perlで書けるのがベストです。Perlでデータの初期化とかしたいですからね。

Perlで書ける方法でツールを絞り込むと、現状では、Selenium::Remote::Driverを使うのが良い気がしています。

ということで、下記のようなテストを書いてみました。

テストの実行には、MTのGitHubリポジトリに含まれる、tディレクトリが必要です。あと、試す場合は環境変数MT_CONFIGを書き換えずに、MT::TestでDBを初期化すると、既存データが吹っ飛ぶので、テスト用の環境は別途用意した方が良いです。

Test::TCPで、GhostDriverと、MTのPSGIの管理画面を空いてるポートで起動して、Selenium::Remote::Driverを使って、管理画面を操作する感じです。

実際書いてみると、いろいろハマりどころがあった感じですが、ハマりどころを回避しつつテストを書くことになりそうです。

テストと同時にキャプチャを撮れるので、エビデンス対策も取れてよいですね。上記のコードではダッシュボードと、記事作成後の画面をキャプチャしていて、下記のような感じのキャプチャが撮れました。JavaScriptも処理されていて、ちゃんとした見た目になってます。

f:id:jdg:20140501090135p:plain

f:id:jdg:20140501090152p:plain

もう少し便利メソッドをカジュアルに使いたい場合は、Wight を使うのも良い気がします。

他言語でもSelenium使う機会があるかもしれないので、今後の学習コスト削減のために、今のところは単純なアダプタであるSelenium::Remote::Driverを使おうと思います。

ほなの。

MTオブジェクトの複製を作るツールスクリプトを作った

Movable Type使ってる時に、たまに、ページングのテストや負荷テスト等をしたい時に、記事を大量にコピーしたい時があって、これまで下記のようなスクリプトを叩いたりしてコピーを作ってました。

これをMTオブジェクト全般に広げても良いかなと思ったので、ObjectCloneというプラグインを作りました。

ObjectClone プラグイン

usage のままですが、下記のように使います。

USAGE: perl tools/object-clone --model=NAME --orig_id=NUM [OPTION]

Requires:
  -m, --model=NAME      Model name (object datasource) of object to make clone.
  -o, --orig_id=NUM     Original object id clone object.

Options:
  -a, --amount=NUM      Amount of clones to make. Default 1.
  -d, --debug           Output debug info to STDERR.
  -h, --help            Show help.
  -r, --redefine=PAIR   Redefine column value with column key and column value pair.
  -u, --usage           Show usage.

Examples:
  # Make clones of 100 objects of MT::Entry from original entry that entry_id is 1.
  $ tools/object-clone --model=entry --orig_id=1 --amount=100

  # Make a clone of MT::Blog, and redefine name, site_path and site_url.
  $ tools/object-clone -m blog -o 2 -r 'name=Clone Blog' -r 'site_path=/path/to/site-path' -r 'site_url=/::/clone-blog/'

基本は、モデル名とコピー元のオブジェクトのIDを、それぞれ、--modelオプションと--orig_id オプションで指定してクローンを作ります。

クローンを複数作る場合は、作る数を--amountオプションで指定します。

他には、単に丸々コピーするだけではなくて、一部、値を変更する場合は、--redefineオプションで、フィールド名とその値を=で区切って指定します。

具体的には、Aというブログの記事を、Bというブログにコピーしたいような時などに、下記のように指定します。

$ perl tools/object-clone --model entry --orig_id 1 --amount 10 --redefine blog_id=3

コマンドラインオプションの単語が適切かどうかは微妙な感じもしますが、とりあえず必要最低限なオプションだけ用意しました。

値の変更が必要なケース

DB上の制約はないにも関わらず、実際にはMT::Entryのベースネームだとか、CustomFields::Fieldのタグ名は、値が重複しないようにCMS上で制限がかかっていたり、バックグラウンドでの値の生成の実装があったりします。

その辺の対応はオブジェクト毎に必要な感じなので、ObjectClone::Patcherとそのサブクラスに切り出しています。

単体で使うことはほぼないと思いますが、下記のような感じで使います。

use strict;
use warnings;

use lib qw(lib extlib plugins/ObjectClone/lib);

use MT;
use ObjectClone::Patcher;
use ObjectClone::Patcher::MT::Entry;

my $mt = MT->instance;
my $orig_obj = MT::Entry->load(1) or die MT::Entry->errstr;
my $new_obj = $orig_obj->clone;
my $patcher = ObjectClone::Patcher->model('entry')->new($new_obj, $orig_obj);
$patcher->remove_patch(basename => \&ObjectClone::Patcher::MT::Entry::basename)
        ->add_patch(basename => sub { 'new-basename' })
        ->add_patch(convert_breaks => sub { 'markdown' })
        ->apply_patch;
$new_obj->save or die $new_obj->errstr;

デフォルトで下記のPatcherクラスを用意しています。

  • ObjectClone::Patcher::MT::Entry
  • ObjectClone::Patcher::MT::Category
  • ObjectClone::Patcher::MT::Author
  • ObjectClone::Patcher::CustomFields::Field

とは言っても実際に実装があるのは、ベースネームをユニークにする処理くらいです。

独自オブジェクトをtools/clone-objectで生成したいときなどは、上記のPatcherクラスを追加すれば対応ができます。( ObjectClone::Patcher::{オブジェクト名} )

後は、apply_patchメソッドで、パッチを適用する前に、before_apply_patchというコールバックが呼ばれるので、デフォルトのパッチ処理を無効にしたり、それに加えて何かの処理を実行したりする時は、コールバックプラグインで拡張できます。

コールバックプラグインのサンプル

下記のようにコールバックを使ったプラグインが書けます。

id: ObjectCloneCallback
key: object-clone-callback
name: ObjectCloneCallback
callbacks:
  ObjectClone::Patcher::MT::Entry::before_apply_patch: |
    sub {
      my ($cb, $patcher) = @_;
      $patcher->add_patch('basename', sub {
        my ($new_obj, $orig_obj) = @_;
        'new-basename';
      });
    }

まとめ

主に開発ツールとして作ったので、開発時には役に立つ気がしています。

シェルスクリプトと組み合わせたりするのも良いかもしれません。

ほなの。

MT::Object をワンライナーしやすくする拡張書いた

コマンドラインで、MT::Objectを取得して、加工して、出力してみたいなことすることが結構あるんですけど、MT::Objectは大変ワンライナーしにくい印象でした。

MTのシステム管理者のIDとパスワードがわからない時、コマンドで強制的に作ることがあるんですけど、MT::Objectがワンライナーしにくいので、下記のようなファイルを用意してから、実行するというまどろっこしさです。

試しにワンライナーしてみると下記のような感じでしょう。

$ perl -I{lib,extlib} -MMT -e 'my $taiju = MT->instance->model("author")->get_by_key({name=>"taiju"});$taiju->nickname("taiju");$taiju->email("higashi@taiju.info");$taiju->auth_type("MT");$taiju->status(1);$taiju->set_password("password");$taiju->is_superuser(1);$taiju->save or $taiju->errstr;printf "You can use taiju (id=%d) (password=password)! taiju is suupppeeeer user!!\n", $taiju->id;'

...セミコロン......

これがワンライナーと言えるなら、minifyされた jQueryですらワンライナーになってしまいますね。

MT::Object::Chaining

ということで作ったのが下記になります。

MT::Object::Chaining - Methods chaining for MT::Object

MT::Object::Chainingを使うと、下記のように書けます。

perlコマンドのように、Iオプションや、Mオプション指定して、インスタンス取得するのすらダルいので、tools/chainというスクリプトでその辺をカバーしてます。

mオプションでモデルを指定して、eオプションでeval文字列を指定します。

$ cd /path/to/mt && tools/chain -m author -e '$model->get_by_key({ name => "taiju" })->nickname("taiju")->email("higashi\@taiju.info")->auth_type("MT")->status(1)->set_password("password")->is_superuser(1)->save->tap(sub { printf "You can use taiju (id=%d) (password=password)! taiju is suupppeeeer user!!\n", shift->id })'

こんな感じでメソッドチェーンで書けるようにする機能を提供するような感じですね。

まとめ

これでMT::Objectをワンライナーしやすくなりました。

jsonで吐くAPIなども用意しているので、

$ tools/chain -m author -e '$model->load->json' | jq '.[] | {name, email}'

みたいな感じでjqに渡したりなど、捗るかもしれません。

また、mapメソッドで一括置換して保存とか、reduceで加工した結果を出力するとか、普段使いに個人的に便利だと思う機能も入れました。

まだ、v0.1なのと、統一感に欠ける気もしているので、インターフェイスの変更するかもしれません。

詳しい使い方は、README.pod を参照下さい。

https://github.com/taiju/MT-Object-Chaining/blob/master/README.pod

ほなの。