のらねこの気まま暮らし

技術系だけど、Qiita向きではないポエムとかを書くめったに更新されないヤツ

jquery.pjax x Backbone.js を試す

週末ですね。こんにちわ。

まえおき

前回のエントリで「Knockback.js試すぜ!」みたいなことを言いましたが撤回します。ドキュメント見て止めました。
理由は以下の二点

  • Backbone.js, Knockout.js知識が必要
  • 日本語での資料が少ない

Backbone.js

というわけで、まずはBackbone.jsをためそうじゃないかと。
大本の目的が、pjaxでページ毎の挙動を制御したいっていう要求があったので、じゃあBackbone.jsだろうと。

http://backbonejs.org/

こんてんつ

  • Initialize
  • Routes
  • View
  • history

Initialize

まずはどうすればいいかの話。
必要最小限では、以下の様な組み合わせるになるのかなと。

    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
    <script src="/plugin/underscore-1.4.4.min.js"></script>
    <script src="/plugin/backbone-0.9.10.min.js"></script>
    <script src="/plugin/bootstrap2.2.2/bootstrap.min.js"></script>
    <script src="/js/main.js"></script>

jQueryは何はともあれ必要です。
Underscore.jsはBackbone.jsの動作に必要。
Backbone.jsは本体。フルセットです。
デザインを簡略化するためにBootstrapを利用しています。
main.jsに挙動を定義していきます。

Routes

何はともあれ、まずは一番の要求であるページ毎の挙動ですよね。

var myRouter = Backbone.Router.extend({
    "routes" : {
        ''       : 'index',
        "mypage" : 'mypage',
    },
  "index" : function() {
    },
    "mypage" : function() {
    },
});

$(function() {
    var router = new myRpiter();
});

そのうちまとめるとは思いますが、Backbone.jsではオブジェクトの継承にextendメソッドを利用します。
RouterであればBackbone.Routerをextendする。引数にHASH形式でメソッドや初期値等を設定します。

"routes"

HTTP_PATHと動作するメソッドを紐付けています。
shebungを前提として作られているのか、Rootの"/"を省略しています。すなわち、

GET /mypage でアクセスが来たら、myRouter.routes.mypage が呼び出される

actions

ルーティングされて、myRouter.indexが呼び出されたとします。
その時、例えば

GET /?param=value

みたいなリクエストが着ちゃうとコケるようです。

   "routes" : {
       "?param=:value" : 'index',
   },
   "index" : function(value) {
   },

このようにして、置き換えて上げると引数で受け取れるみたいなので、そういう書き方してあげるとよいんではないかと。

instance

new で定義したクラスを呼び出してあげれば、Routingが設定され、リクエストしたパスを見て処理を行なってくれます。
but,このままではpjaxとか使えません。

PJAX

Backbone.jsのRouterにはhistory管理が同梱されていて、簡単にpjaxを実装できるそうです。

以下のような記事を参考にすれば、わりかし簡単にpjaxを実装できると思います。実際出来ました。

http://kennyj-jp.blogspot.jp/2011/07/backbonejspjax.html
http://taiju.hatenablog.com/entry/20120414/1334390163
http://taiju.hatenablog.com/entry/20120414/1334390163

だが私はそれに反論を唱える

重要なのは、Backbone.jsが提供しているのはpushStateとRouter.navigateによるイベント処理だけだということです。

if 非対応ブラウザのでアクセス
if ページのレスポンスに時間がかかる
if 完全なHTMLをレスポンスしてきた
if InternalServerError等の例外

コケたらなにも反応しないボタンになるか、あるいは自前でアレコレと例外処理を用意しなくてはなりません。


・・・・・



ヽ(´Д`ヽ)(/´Д`)/イヤァ~





僕は、jquery.pjaxに甘えたくなりました。

jquery.pjaxとBackbone.jsを連携させることは可能であるか?

https://github.com/defunkt/jquery-pjax

jquery.pjaxはjquery用に書かれたpjaxのpluginです。
pjax処理の周辺でeventをbindしてくれていてhookが非常に簡単。
また、timeoutやlocation.hrefによる遷移などのサポートも充実していて、非常に使い勝手の良いpluginです。

But

jquery.pjaxが良い物なのは、わかる。このブログ開設当初から使っていた。
Backbone.jsはjQueryを使うことに異論を唱えていない。pluginを使ったからどうだということはない。むしろ積極的に使って効率化できるんじゃないかと思う。余談だ。

では何が問題か。
jquery.pjaxもBackbone.Routerもhistoryの操作を行なっている。
重複操作になり得る可能性は無いか?

とりあえず試す

template/layout.html
<script src="/plugin/jquery.pjax.js"></script>
js/main.js
var viewPjax = Backbone.View.extend({
    "events" : {
        "pjax:send"    : 'send',
        "pjax:succes"  : 'success',
        "pjax:complete": 'complete',
        "pjax:timeout" : 'timeout',
    },
    "el" : document,
    "initialize" : function() {
        $(document).pjax('a', pjax_container);
    },
    "send" : function(e) {
        console.log('pjax send');
    },
    "success" : function() {
        cosnole.log('pjax success');
    },
    "complete" : function(e) {
        $('body').trigger('init');
        this._navigate();
        $(window).trigger('load');
    },
    "timeout" : function(e) {
        console.log('pjax timeout');
    },
    "_navigate" : function() {
        window.router.navigate(location.pathname.substr(1), true);
    },
});

$(function(){
    new viewPjax();
    window.router = new myRouter();

    Backbone.history.start({pushState : true});
});

イグザクトリー そのとおりだ

遷移するけど、バックボタンを二回押さないと戻らないという罠にはまった。
おそらくpushStateが二回走っているが為に履歴が重複登録されているんだろうと想定。

対策

試行錯誤しているうちになんか動くようになった。誰か説明してくれ。

var viewPjax = Backbone.View.extend({
    "events" : {
        "pjax:send"    : 'send',
        "pjax:succes"  : 'success',
        "pjax:complete": 'complete',
        "pjax:timeout" : 'timeout',
    },
    "el" : document,
    "initialize" : function() {
        $(document).pjax('a', pjax_container);
    },
    "send" : function(e) {
        console.log('pjax send');
    },
    "success" : function() {
        cosnole.log('pjax success');
    },
    "complete" : function(e) {
        $('body').trigger('init');
        this._navigate();
        $(window).trigger('load');
    },
    "timeout" : function(e) {
        console.log('pjax timeout');
    },
    "_navigate" : function() {
        window.router.navigate(location.pathname.substr(1), {
            "trigger" : true,
            "replace" : true,
        });
    },
});

$(function(){
    new viewPjax();
    window.router = new myRouter();

    Backbone.history.start({pushState : true});
});

まとめ

  • Backbone.jsでRouterによるページ毎の挙動実装ためした
  • Backbone.jsとjquery.pjaxによるpjax実装をためした

いまいち釈然としないものの、ひとまずよくわからないけれどpjax動いてるんだよね。釈然としない。
Backbone.jsについてはちゃんと中身を確認して把握しないと使い物にするには大変だなぁと思いつつ、ここまで実質半日程度だから比較的軽度の勉強で済む予感。大変なのはこれからか。

JSをModelとかViewとかに分離できるのはすごくありがたい。
なにせいままでイベントとロジック祭りで何処に何があるのか非常にわかりにくかった。

今後、Knockbackとかも試して行きたいが、とりあえずBackboneを使い倒せるようになろうと思う。


うおし、尻みよ尻。

2013/03/06追記

この話には続きがあります。

jquery.pjaxとBackbone.jsの並行運用は
そ ん な 簡 単 な 話 じ ゃ な か っ た
http://mizuki-r.hatenablog.com/entry/2013/03/06/093235

月の終わり、あるいは始めるための

中二分が足りません。こんばんわ。

とりあえず1月おわっちゃうので、2月にやりたいことをメモするだけのおもしろみのない記事です。

その前に振り返り

技術的にしか振り替えれることが無い。絶望した。

  • Amon2のpluginを書いたよ。
  • Bootstrap使い倒したよ
  • Compass使い倒そうとしているよ
  • pjax挫折したよ

これから何をするか

  • Text::XslateでBootstrapのHTMLをよしなにするマクロをどうにかしたい
    • 提案はした。実用に耐えうるかは微妙。要件等
  • Knockback.js使ってみたい
    • pjaxのdispatcherと、jsを手早く効率的に書くための一手段としての検討
  • BootstrapとCompassについて
  • Compassについて
    • mixinを量産したけど、include元を追うの大変よね。

まとめ。

上に挙げたことをとりあえず2月中二挑戦したい。


どうでもいいけど、小説書きたい。書きたいというか、書くためのモチベーションが欲しい。

Amon2::Plugin::Web::Auth::PathというPluginを書いた

ソースはこちら。トルネで録画したJOJOの映像が乱れまくって辛いです。
https://github.com/rymizuki/p5-Amon2-Web-Auth-Path

頑張って短くした前置き

Amon2にはWeb::Authという認証をサポートするプラグインがあります。が、それらは外部の認証自身のアプリケーションで使うためのツールであり、アプリケーションのページ単位での認証をおこなってくれません。
例えば、トップページは認証をかけない、マイページはかける。お問い合わせページはかけない、という細かい制御が難しい。
BEFORE_DISPATCHでPATH毎、あるいはController毎に管理するとか、Routerのany使うとかやりようはありますが、アプリの規模が膨らめば膨らむ程コードも膨らむ。可読性も下がるしあまりよろしくない。


Amon2::Plugin::Web::Auth::PathはPATH単位でBEFORE_DISPATCHを走らせて、認証の処理をContextから分離したものです。Pluginのinit時にpathsをArrayRefで渡すか、moduleを渡すかの選択ができます。
少ない認証だけでしたら、pathsでも十分なのですが、僕の要求ではWebContextの行数が倍に膨れ上がる複雑な処理だったので、出来ればそこは切り出したい、と思って外部Moduleを呼べるようにしました。ついでにDSLなんかをつくってみました。

そんなわけで、
Router::Simpleでの実装は、@takuji31さんのAmon2::Web::Authorizerを参考にさせて頂きました。

https://github.com/takuji31/Amon2-Web-Authorizer


処理の中で、認証が通ったか通ってないかの判断を明示的にするために$auth->success,$auth->failedのメソッドを呼ぶといいんじゃない?とid:karupaneruraにアドバイスいただいてまんま実装しました。

つかいかた

package YourApp::Web;
use Amon2::Lite;

get '/' => sub {
    my $c = shift;
};

get '/mypage' => sub {
    my $c = shift;
};

__PACKAGE__->load_plugins(
    'Web::Auth' => +{
        module => 'Twitter',
        ...
    },
    'Web::Auth::Path' => sub {
        paths => [
            '/'          => sub { $_[1]->success },
            qr{^/auth}   => sub { $_[1]->success },
            qr{^/mypage} => sub {
                my ($c, $auth,) = @_;
                if ($c->session->get('is_login')) {
                    $auth->success;
                } else {
                    $auth->failed;
                    # redirect to Auth::Site::Twitter
                    return $c->redirect('/auth/twitter/authenticate');
                },
            },
        ],
    },
);

1;


callbackにはWebContext, Plack::Util::initline_objectのインスタンス, Router::Simpleのmatchが渡されます。
callbackの中では$auth->succes/$auth->failedを呼びましょう。呼ばないと死にます。Appが。
callbackの返り値があればそのままreturnするので(Plack::Responseだけに固定したほうが良いのだろうか)$c->redirect, $c->render等ご自由に。after_dispatchにも影響します。controllerが走らないだけなの。

Router::Simpleつかってるのでパスの指定はRouter::Simpleのままつかえます。

モジュールを使う場合

こんなモジュールを書いて

package MyApp::Auth::Path::App;
use Amon2::Auth::Path::Declare;

path '/' => sub { $_[1]->success };

1;

WebContextではこんな感じでパラメータ渡してやる。

__PACKAGE__->load_plugins(
    'Web::Auth::Path', {
        module => 'MyApp::Auth::Path::App',
    },
);

以下はpathsと同様。

デバッグでしか使ってないけどとりあえず残した

initのoptionにon_success,on_errorのcallbackが渡せます。
$auth->success/failedの結果を受け取って、responseの返却をするまえに走らせるHookです。




とまあ、こんなかんじで。
とりあえず簡単なテストは追加したので、ひとまずはこれでよいかなーと。

さて、怠惰な休日を謳歌しましょう。

Amon2::Plugin::Web::ValidatorというPluginを書いた

ソースは以下。まだ全然書き途中
https://github.com/rymizuki/p5-Amon2-Plugin-Web-Validator

長めの前置き

以前からポツポツとリクエストのValidation系のModuleがどうにも肌に合わず、去年の秋ごろにData::Validatorを弄る感じで自作Moduleを使っていた。

別にあれがダメこれがだめで、というよりは社内で使うのにいろいろ制約があったことと、どうも冗長に感じること、Data::Validatorをメソッドのバリデーションに使っているのにリクエストはRuleの書式が違うことなど、どうにもまとまりがなかった。

受け付けるデータのチェックを行うのであれば、ModelもContollerも同じフォーマットであるにこしたことはないし、Mouse::Typesとか型チェック系のModuleも使っているのだから、ある程度ルールも共通化できる。
nameパラメータは何文字以上何文字以内、とか。Webインターフェースだけが受け付けるわけじゃなく、CUIインターフェースでも利用する可能性があるいじょう、どちらかに依存してしまうのはよくないし、かといってModelでValidationすれば他でしなくてもいい、ということではないと僕は考えている。


Data::ValidatorはRuleごとインスタンスをキャッシュできたり、Roleで挙動を変えられたり、TypeConstraintsで型を定義できたりしていて使い勝手が良い。
メソッドなんかで引数のバリデーションに使うのであれば、メソッドの先頭に持ってきて、明示的にどういう引数を期待しているのかを記述できる。これはメンテナンスのときに非常にありがたい。


そんなこんなもあって、最初はData::Validatorでリクエストをバリデーションしていたんだけれど、だんだんその冗長性に嫌気がさしてきた。余談だけれど、僕は一月半で一つのサービス作るくらいの勢いでコードを書いていたから、そりゃもうValidationしまくりなのです。
要求としては以下が追加された。

  • なるべく短くValidationを行いたい
  • バリデーションを行ったパラメータだけを受け取りたい
  • エラーメッセージとパラメーターを紐付けて受け取りたい
  • Fileのアップロードもバリデーションしたい(サイズとかContentTypeとか)


そこまで高機能なValidatorに思い当たらず(探せばあったかもだけど、入れるのがかなり手間の予感)、僕だけの要求が強そうなので、勉強もかねてとりあえず書いてみた。
ついでにそこからAmon2::Pluginにしてみた。


だいたいそんなところが、書いたよって話のコンテキスト。いや長い。

READMEの練習みたいなの

SYNOPSIS

package Project::Web::App;
use Amon2::Lite;

get '/list' => sub {
    my $c = shift;
    my $data = $c->validator(rule => +{
        page => {isa => 'Int', default => 1},
    })->valid_data;
    my $result = $c->model('any')->run(%$data);
    $c->render('list.tx', {result => $result});
};

post '/add' => sub {
    my $c = shift;
    my $validator = $c->validator(rule +{
        name  => 'Str',
        profile => 'Str',
    });
    if ($validator->is_success) {
      ... things ...
    } else {
        return $c->render('error.tx', {errors => $validator->get_errors)};
    }
};

__PACKAGE__->load_plugins(
    'Web::Validator' => {
        module     => 'Data::Validator',
        message  => {str => 'input type is strings!'},
);

1;

がいよー

初期化時に"module"を読み込んでいるのは、Data::Validator以外も使えるようにしたいとか思いそうと思ったから。

messageはdecamelizeしたRuleのISAをキーにしたHashRef。なんでdecamelizeしたのかは忘れたけどたぶんそういう仕様なんだ。

$c->validatorでruleをHashRefで読み込んでいるけれど、外部ファイルにまとめて初期化時に渡すことも可能。

__PACKAGE__->load_plugin('Web::Validator' => \%rule);

もともとは、こっちで使ってたんだけど、@kfly8 氏に突っ込まれて$c->validatorからRuleを呼べるようにした。

Data::ValidatorはNoThrow,AllowExtraのRoleを使っており、Validationしない余計なパラメータがあっても大丈夫。失敗した時に死んだりしない。まだ頑張れる。

メソッドチェイン厨としては、いまのインターフェースは結構きにいってて、最短一行3メソッド呼び出しでバリデーションデータを受け取れるのは嬉しい。

一応テストもちょっとずつ追加して、週末にはMakefile.PLあたりをもうちょいちゃんとしたいとおもっているのと、Podもちゃんと書こうと思ってる。

is_successとかvalid_dataとかget_errorsとか以外にもいろいろメソッドは追加しているので、その辺もちゃんとやりたいのです。



2年間くらいPerl書いてきたけど、低機能なValidatorでもみんな気にせず使ってて、僕だけなのかこんなことに悩むのは、と長らく思っていた。
単純に慣れの問題とか、優先順位の問題とか、そもそもそんなにValidator拘るようなコード書いてへんってのもあるのかなーとは思う。管理画面クリエイターだもの、僕は。


ともあれ、2年目にしてようやく実用的な(?)モジュールを公開できそうなのはちょっとした成長だと思う。
ちなみに最近週一で新しいPlugin生やしてるので、使えそうなのあったら社内外かかわらず出して行きたいと思ってる。とりあえず次は一応Githubには上がってる。ニヤリ

先輩のコードを大きくパクったという、ね・・・
ま、週末にでも書きましょう。

翌朝追記

早速コメントを頂いた。

id:kfly8

opts でon_error と on_success 受け取れるようにして欲しいなーw

わがままさんめっ

こんなふうにすると、Validation成功時・失敗時にコールバックを実行できる。

__PACKAGE__->load_plugin('Web::Validator', {
    module => 'Data::Validator',
    message => \%message,
    on_success => sub {
        my ($c, $validator) = @_;
        debugf 'yappie! %s', ddf $validator->valid_data;
    },
    on_error => sub {
        my ($c, $validator) = @_;
        return $c->render('error.tx', {errors => $validator->get_errors});
    },
});

例えば、自分が誰かの役に立つと思う時

そういう場合、たいてい空回りするかガス欠で失敗する。


僕に限った話ではあるけれど。


そういうアイディアが浮かんだ場合、人に言わないことが多い。
別に独り占めしようとか思っているわけじゃない。ただ、本当に役立ちそうに思えても、自分が思ってるほどではないのと、多分に自己満足とか自己顕示とかが含まれているので、自分が思っているより大分価値が低いものだと思っている。




年末の話。
部屋の大掃除を終えて、ちょっとマイドキュメント系のデータ整理をしていると、懐かしいファイルを見つけた。


ZIPで圧縮された、卒業研究の資料だ。何を研究し、どういう成果を上げたのかは、知る人ぞ知る。
覚えていれば大学の研究室の同期か、あるいは新卒の同期だけだろう。まあ忘れてていい。僕自身そんな価値あるもんじゃないと思ってた。


本当にやりたかったことは僕としては実現できなかったし、発表の方向性としては「それGoogleDocumentでよくね?」と僕がこっそりツッコミを入れてた。発表の結果としては僕のツッコミを裏切る好評ではあったものの、それでも価値は無いと思ってた。


でも、アイディアってのは唐突に降ってくる。前触れとか予兆とかなく、ただ唐突に。ふわりと。


マイドキュメントのデータを整理しおえて、ふーんそんなこともあったよね、と恥ずかしいものを隠すようにゴミ箱へ送り、ゴミ箱を空にする。いつもの流れ。
一息つくために僕はシャワーを浴びて、熱いお湯で体を温めながら、ふと思った。


あれ。あの研究、応用したらめっちゃ便利じゃね?


電撃が奔る程の衝撃はなかった。ただ納得はあった。僕はもともとそういうことがしたくて、あれをテーマにしたんだが、正直僕自身楽したいが為に選んだ、という方が本音だと思っていた。
今、こうして、ある別の問題に直面するまでは。




そんなことがあり、年が開けた。
便利なツールにはできると思う。努力しだいで。問題は、どこまでそのモチベーションが続くか。
経験上長くは持たない。僕が未来が無いと断じてしまえば、そこまで。
だから、「こんなものを作ろうと思ってる!」とは言えない。作り上げられる自身がないし、この先余裕があるかわからないから。余裕ができた時に必要性がなくなっているかもしれないから。


ただ、でも。
なんかこう、やろうと思ったけど何も言わずひっそりと動くのってどうしても苦手で、主張はシたくないけど決意したんだってことは記録しておきたい的な意味合いを込めて、ブログにこんな意味不明な記事を残そうと思った。


ネタバレは、挫折した後か、達成した後に。


そんな感じで作業にとりかかる。明日は名古屋だ。バスで6時間だ。時間はある。バッテリーの保つ限り。
それでは、良き青空を。

去年の振り返りとか

PerlのTips

Mouse

OOPっぽく書けるのとか、ある程度のパターン化ができるのでわかりやすくリファクタリングできるかなぁーと思って超こてこてに使ってみた。


結果として、Roleの使い方がいまいちで、リファクタリングしにくいわ拡張しにくいわ全般的に失敗だったといえる。


ちゃんと設計すべし

Teng

ORMapperでInflateやDeflateしつつSQLも書けて便利。大規模開発とかRowクラスをガリガリ使う感じのヤツに向いてる。


しかしながら、RowクラスでTransactionとかすると当然のごとく更新とかあちこちのModuleに分散してしまって見つけるのが困難。
だんだん複雑になって大規模になればなるほど管理がしづらくなってくる。


スケールと責任の分担をきちんと考えて実装すべし

Mojolicious

PerlのWAFのひとつ。依存モジュールがほとんど無く、独自実装。


最初こそ戸惑ったものの、ドキュメントが充実しておりさほど困ることはなかったと記憶している。
ただし気づいたらVersion3になってて全然追いつけてないのでソコが問題。

Amon2

Mojoとは逆にWAF自体は軽量な感じで使えるWAF。最近社内でも運用しはじめた。


継承ベースではあるが、Contextの存在と、Controllerをインスタンス化しない等、独特の作りがある。拡張しやすくPluginも書きやすい。

調子のって拡張しすぎてまた謎のWAFに変換しないよう気をつけましょう。

Text::Xslate::Syntax::Kolon

もともと社内でTTを使っててたけれど、Kolonのがいいよね!といって去年一部でKolonを使い始めた。

昨年12月頃からちゃんと使い始めて、Mouse的なメソッドの使い所とか、macroをroleとして用いた時の利便性とか色々わかるようになって、「じゃあこれできんじゃね?」といろいろ試している。

ViewとTemplateが果たして同一のものなのか、少々疑問を抱きつつ、処理の分散は行えているのではないかと思う次第。

Frontend

Compassを使い始めた

CSS FrameWorkであるCompassを個人的にも会社的にも導入して運用中。
随分簡単にCSS書けるようになって幸せ。最近ではBootstrapとの合わせ技を模索中。

Android

イヤダイヤダと逃げ続けてきたAndroidに秋ごろから着手。
一応、軽く作るくらいはできるけど、描画処理は苦手ね・・・


CUI構築が可能なMacは本当に素晴らしいと思った。

Server side

varnish

Cacheサーバー。知名度低いらしい。
WebSocketでハマったり、CacheしてほしくないのをCacheしちゃったりといろいろ大変だったけど、わりと簡単に設定できて気に入っている。

Private

小説

年のはじめのころは頑張ってたけど、夏くらいからもう全然触ってない。
なにかきっかけがあったわけではないけれど、忙しさに追われて諦めてしまったような感じ。
どこかで再燃するきっかけがあれば、こんどこそは、と毎年思っている。

声優ライブ

9月にスフィアライブに行ってから、のめり込む
11月に寿美菜子さんのソロツアー
12月に原田ひとみさんのダチャーン歳
1月にRitaさん

ライブ無しじゃ生きてけないかもしれんよ

趣味と私とサービスと

いま僕が仕事で携わっているサービスは、今後の方針次第では僕の趣味と重なる部分がある。
もちろん、お金やコストとか、いろいろな問題はあるけれど、どこへ向かってもその問題がある。


僕としては、僕の趣味と重なるのであれば、それを一生の仕事にしてもいいくらいの価値があると思っている。とはいえ、サービス自体の魅力が、「夢」や「机上の空論」と呼ばれれるものである現状、(ここでの発言を含めて)声を大にしては言えない。


もちろん、実現に向けて関わっている皆努力しているし、ちょっと無茶もしている。
それでもやはり、第3者を巻き込むには"力"が足りてない。人につきつけて、おお、と驚きを与え頷かせる力が。


今年こそ、そのサービスを成功させたい。させてやる。
ただ一介のエンジニアであるけれど、できることがあるなら挑戦していきたいと思う。

この世界は、資本主義である

最後に。
お金の話をば。



振り返ってみると、ここには書いてない悲しいお話がいくつもあるのですよ。
金がない。
金がない。


給料が低いとか、運悪く出費しなければならないことがあったとか、第3者による努力では解決できない問題ではない。

「あとで後悔したくない」「できる努力ならしておく」「最悪の状況に備えて云々」とは僕が密かに思っていることだが、こと金銭面では悉く無視している。
すなわち理性の欠片も無く欲望に任せて金を使っている。


後悔している。
毎月の請求を見るたびに、毎月の預金残高を見るたびに、後悔している。


今年こそは、理性的に、合理的に、無駄な出費をせず、貯蓄を行いつつ、チャンスを逃さず投資しようと、ここに誓う。
誓うとも・・・・!




そんな感じで本年もよろしくお願いします。

あけましておめでとうございます。

今年もよろしくお願いします。

...で、記事を終えようとしたんだけどとりあえず前年の振り返りをしようとおもってTwitterのログをあさってみたんだ。したら4月までしか残ってなかったんだ。
やべーよ4月以前って仕事のことしか覚えてねーよ。