時計を壊せ

駆け出してからそこそこ経ったWebプログラマーの雑記

YAPC::Asia 2013 で ぼくがかんがえたさいきょうのMVC についてはなします #yapcasia

YAPC::Asiaとは

日本で最大級の規模のPerlのカンファレンスです。Perlのカンファレンスですが運用、プロトコル、テスト等開発に関わる様々なトークがあり、和気あいあいとした雰囲気でときには意識高い話もしつつ、ゆるふわにPerlとその周辺技術等への学びや発見を得る事が出来ます。詳しくは http://yapcasia.org/2013/ をどうぞ。

今回、ありがたい事に僕が応募していたトークを採択して頂きました。

ぼくがかんがえたさいきょうのMVCとは

下のリンク先をご覧下さい。

http://yapcasia.org/talk/show/f60b8522-d43e-11e2-ac80-4cc16aeab6a4

ではちょっとさみしいのでこのトークを応募した経緯を書いてみます。


僕は仕事でソーシャルげーむを作っています。ソーシャルゲームは若干特殊な部分はありますが基本的には普通のWebアプリです。(最近はクライアントをAndroid/iOS向けに提供しているものも多いですがそれも多くの場合はバックエンドはWebAPIを提供するWebアプリです)
ソーシャルゲームは場合によってはかなりの種類の機能を提供する場合があり、対応デバイスもAndroid4系/Android2系、iOS/FeaturePhone(所謂ガラケー)などに対応させ、更に複数のソーシャルプラットフォームで動作させる場合もあり、プラットフォームによってOpenSocialの同じAPIでも微妙に仕様が違う場合があったり、同じ目的のAPIをそれぞれ独自の仕様で持っている場合があり、またゲーム自体のロジックが複雑な場合もあり、規模が大きくなる傾向があります。
ある程度の規模まではぶっちゃけ設計が適当でもなんとかなります。意味のわからないコードもひたすら追えばなんとかならなくもないでしょう。最悪全部書き直す事が現実的なコストでできる場合もあります。場合によってはschemaだけ真面目に設計してコードはゴリっと愚直に書いたほうが運用を含めた総合的なコストを低くする事もできる場合も多いと思っています。
しかし、ある程度以上になってくると、適当な設計のアプリケーションでは全てのコードを把握する難易度はかなり上がります。特にRPG系と呼ばれるジャンルのソーシャルアプリは確実に困る規模だと思います。
規模が大きいとどうしても複数人で同時に開発を進める事になります。規模が大きい割に設計が適当だと、同じ役割のコードが様々な場所に書かれたり、様々な機能が沢山搭載されている「ぼくがかんがえたさいきょうのClass!!!」「ぼくがかんがえたさいきょうのmethod!!!」みたいなやつが出来たり、非常にカオスなコードが出来上がります。処理の共通化も困難になり、そのようなコードベース上で無理に行われた共通化は諸悪の根源になりがちです。プロダクションコードでこのようなコードが出来上がると非常に悲しいことになります。テストが書かれていればかなりマシですが、それでもそもそもの設計がぐちゃぐちゃだとリファクタリングにかなり苦労する印象が強いです。コードレビューも苦労します。

これを解決するための考え方の1つが僕はソフトウェアアーキテクチャだと考えています。MVCはそのうちの1つですが、これも必ずしもそのままWebアプリケーションに当てはめられる訳ではなく、特にRDBMSとの関わり方やコンテンツキャッシュとの関わり方について悩んだり、ロジックが変に分散してしまったり、逆に殆どのロジックが詰め込まれる「ぼくがかんがえたさいきょうのClass」みたいな奴が出来て苦労する場合が多いと思います。その為、MVCの考え方を拡張して、目的のアプリケーションに合わせて整理して設計する事が必要になります。
そこに関してある程度考えがまとまったので、今回この話をする事にしました。
ただ、ぼくの考えも非の打ち所の無い完璧なものであるとは思っておらず、より良い感がえ方がきっとあると思うので、この話をきっかけにみんなそれぞれの「ぼくがかんがえたさいきょうのMVC」について話し合うきっかけにでもなれば、ベースの設計の重要性について考える機会が出来たらいいな風な感じです。
id:TAKESAKO さんの「SPDY、HTTP/2.0の使い方」とかid:xtetsujiさんの「mod_perlの展望とApacheの超絶技巧」とかid:akiymさんの「Herokuで学ぶ、初めてのPerl」とか裏番組みんな面白そうなので僕も聴きに行きたいくらいですが、もし興味があれば聴きに来て頂けると喜びます!

YAPC::Asiaに参加するには

チケットの購入が必要です。2日通し券で5000円、1日券で4000円です。なお、学生は無料です。(!!)
チケットの購入はこちらからどうぞ! http://yapcasia.org/2013/tickets/
明日、8/11までなのでまだ購入していない方はお早めに!

Yomiuri構想

まだ構想段階だけどこれだいぶイケてね?って思ったので勢いで書いてみる。

Yomiuriとは

手軽に拡張出来るJekyllインスパイア系ブログフレームワーク/オーサリングツール

特徴

CLIで記事が書ける

vimemacsなどお好みのエディタでどうぞ。

Github Flavord Markdownで記事が書ける

エンジニア的にアツい。

レイアウトの自由度

Kolon(Text::Xslate)の継承テンプレートで良い感じに拡張出来ます。
また、良い感じのメソッドを提供しているので
まるまるテンプレートを置き換える事もできます。
JS/CSSも自由に書けます。

機能拡張の自由度

JavaScriptとWebAPIを使って機能拡張を行う事をYomiuriはサポートします。
用意されたhook pointにJavaScriptAPIクライアント処理を埋め込むと良い感じにWebAPIを叩いてunderscore.jsで書いたtemplateをレンダリングするイメージです。
仕様として決まるのはWebAPIのCSRF対策*1JavaScriptのhook pointだけです。*2
この仕様を満たす事が出来ればWebAPIは自由な言語で実装する事が出来ます。
また、APIを楽に実装するための標準API仕様とサーバーサイド(Perl)/クライアントサイド(JavaScript)フレームワークも別途用意します。
大概はこれをつかうと楽にそこそこ便利なAPIをおてがるに開発出来る様になる予定です。

シンプルな動作

Jekillと同様に記事ファイル(Markdown)とテンプレートから静的なHTMLを生成するだけです。
gitで管理しやすいと思います。
静的ファイルサーバーがあれば公開可能なのでやろうと思えばGithub Pagesとかでも公開出来ます。*3

使い方イメージ

インストール

$ cpanm App::Yomiuri

新しいブログを作成

対話式で良い感じに設定作ってくれる。

$ yomiuri create MyBlog
title: My Blog of darkness
description: publishing my darkness
creating yomiuri.toml ... OK
creating MyBlog ... OK
$ cd MyBlog
$ git init
$ git add .
$ git commit -m 'initialize commit'

暗黒面を綴るブログが出来ました。

カテゴリの追加

カテゴリははてブと同様にタグっぽいやつ。

$ yomiuri category add darkness
$ yomiuri category add emacs

darknessとemacsという2つのカテゴリが出来ました。

記事の作成/編集

insert or update的な感じで処理してくれるのでファイル食わせるだけです。
h1(レベル1の見出し)の前に[]でカテゴリを囲んで書くとそのカテゴリの一覧に追加してくれます。

$ cat > entry/hoge.md
[darkness][emacs]My first entry.
===============

Hi.

I write a blog post for the first time. 
My hobby is to messing about with emacs.
^D
$ yomiuri entry entry/hoge.md

HTMLなどの生成

コマンド一発でお手軽生成

$ yomiuri publish
creating htdocs/index.html ... OK
creating htdocs/entry/hoge.html ... OK
creating htdocs/archive/1.html ... OK
creating htdocs/config.json ... OK
creating htdocs/meta.json ... OK
生っぽい感じで拡張
$ cat > htdocs/js/darkness.js
(function () {
  var Comment = {
    model: null,
    view:  null
  };

  Comment.Model = Backbone.Model.extend({
    apiUrl: "http://api.yomiuri.yourhost.com/comment/",
    list  : function () { return this.get("list"); }
  });
  Comment.View = Backbone.View.extend({
    el     : "#yomiuri-comment",
    model  : new Comment.Model(),
    events : {
      "click .button.add":    "openAddEditModal",
      "click .button.res":    "openAddEditModalWithRes",
      "click .button.submit": "submit"
    },
    initialize: function() {
      Comment.model.get(this.$el.attr)
      this.template      = _.template($("script#yomiuri-comment-template").text());
      this.modalTemplate = _.template($("script#yomiuri-comment-modal-template").text());
      this.render();
    },
    openAddEditModal: function (e) {
      $("#yomiuri-comment-modal").html(this.modalTemplate());
      $("#yomiuri-comment-modal").modal("show");
    },
    openAddEditModalWithRes: function (e) {
      var targetCommentId = e.target.getAttribute("data-comment-id");
      $("#yomiuri-comment-modal").html(this.modalTemplate({ targetCommentId: targetCommentId }));
      $("#yomiuri-comment-modal").modal("show");
    },
    submit: function() { throw "飽きた"; },
    render: function() {
      this.$el.html(this.template({ list: this.model.list }));
    }
  });

  Yomiuri.router.on("entry/:name", function () {
    new Comment.View();
  });
});

})();
^D

本日の一言

Jekillでよくね!!!!!!という気持ちは凄く分かります。
ぶっちゃけ最初はPerlでJekill的な奴自作してみよっかなー的なところから始まって、
JS自由に書いて機能拡張する事考えたらある程度フレームワークあると楽だよねってなって、
ああいう感じの静的HTML吐く系のブログエンジンを利用者が独自拡張するためのアプローチとして、
こういう感じの事が『手軽に』出来たら楽しいんじゃないかなと思っている。
そのためには仕様と実装をサポートするライブラリの整備が不可欠なので、
また、その肝心の部分はまだまじめに作り始めてないので実装が進んだらまた記事書きます。
まとまんねーーーーーーーーーーーーーー

本日のこだわりぽいんと

宗教間戦争が起きないようにcatで書きました

*1:http://d.hatena.ne.jp/hasegawayosuke/20130302/p1

*2:なんか足りない気もしつつ実装段階でまた考え直す

*3:Github Pagesに公開するのはgitのrootがそのままページのrootになるという仕様上ちょっと面倒なのでなんか上手い手段を考え中…

コンテキストとハッシュの怪

怖い話

割とハマりがちなので。
たとえば、以下のようなコードがあったとします。

use strict;
use warnings;
use utf8;

use Data::Dumper;

sub yyy {
    my $key = shift;
    return unless $key;

    return "yyy_$key";
}

my $hashref = {
    xxx => yyy(),
    aaa => 'bbb',
    ccc => 'ddd',
};

print Dumper $hashref;

上のサンプルコードの出力は以下のようになります。

$VAR1 = {
'bbb' => 'ccc',
'xxx' => 'aaa',
'ddd' => undef
};

マテマテマテマテって思うかもしれませんが、perlのバグなどではなく正常な挙動です。

コンテキスト

Perlにはコンテキストと呼ばれる概念があります。
これは、値の解釈によって処理を変えるための概念です。
詳しい説明は初めてのPerl*1を読むか、こことかこことかを読むと良いかもしれません。

Perlは値をスカラーコンテキストで評価する場合とリストコンテキストで評価する場合があり、それぞれで処理内容が変わる場合があります。
例えば変数への代入の場合はスカラー型の変数への代入の場合は値をスカラーコンテキストで評価します。

例1:

use strict;
use warnings;
use utf8;

my $calar1 = 'aaa';                 ## スカラーをスカラーコンテキストで評価
my $calar2 = ('xxx', 'yyy', 'zzz'); ## リストをスカラーコンテキストで評価(リストの最後の値が得られる)

print $calar1, "\n"; # 'aaa'
print $calar2, "\n"; # 'zzz'

アレイ型/ハッシュ型変数への代入の場合は値をリストコンテキストで評価します。
例2:

use strict;
use warnings;
use utf8;

use Data::Dumper;

# ARRAY
my @rray1 = 'aaa';                 ## スカラーをリストコンテキストで評価(与えたスカラー値が1つだけ入ったリストが得られる)
my @rray2 = ('xxx', 'yyy', 'zzz'); ## リストをリストコンテキストで評価

# HASH
my %ash1  = 'aaa';                 ## スカラーをリストコンテキストで評価(与えたスカラー値がkeyとなりvalueがundefなハッシュが得られる)
my %ash2  = ('xxx', 'yyy', 'zzz'); ## リストをリストコンテキストで評価

print Dumper \@rray1; # ['aaa']
print Dumper \@rray2; # ['xxx', 'yyy', 'zzz']

print Dumper \%ash1; # { 'aaa' => undef }
print Dumper \%ash2; # { 'xxx' => 'yyy', 'zzz' => undef }

また、更に変な書き方として、リストの中にリストを含む事も出来ます。
これは結合され1つのリストとして解釈されます。

('xxx', 'yyy', ('aaa', 'bbb')); # => ('xxx', 'yyy', 'aaa', 'bbb')

ハッシュ内では値はリストコンテキストで評価します。
なので、一番上の何も返さない関数の場合、空のリストとして解釈されてキーがズレてしまうのです。

{
    xxx => (),
    aaa => 'bbb',
    ccc => 'ddd',
}; ## { xxx => 'aaa', bbb => 'ccc', ddd => undef }

この場合、ほぼプログラマの思い通りの挙動にはならないでしょう。怖いですね。

こわいのこわいのとんでけー

上のコード類を実行してみた人は以下のような警告が出るのに気づいたかもしれません。

Odd number of elements in hash assignment

これはハッシュとして与えたリストが奇数だった場合に警告してくれています。
上のような間違ったコードを書いてしまっている場合がある事を危惧して(かどうかは知りませんが)教えてくれているのです。
なので警告は/dev/nullには決して捨てたりせずに*2、不可解な挙動が起こった場合は警告も含め怪しいメッセージが出ていないか確認するとよいでしょう。

*1:最近第6版が発売されたようですね

*2:捨てると椅子が飛んできます

perl触ってるんだから一度くらいソースフィルタを触ってみたいなと思ったのでVS.pmを書いた

VS.pmとはワンライナーでおてがるにベンチマークを取るためのPerlモジュールである。
ソースフィルタを実験したくなっただけなので実用性はほぼない。


こんな感じで使える。

perl -MText::Xslate::Util -MVS=1000000 -MURI::Escape -E 'xslate { Text::Xslate::Util::uri_escape($ARGV[0]) } vs orig { URI::Escape::uri_escape($ARGV[0]) }' 'xxxxxxxxxx<"&x7axxxx;xxxxxxxxxxxxxx'


ソースはこんなの。


use Filter::Util::Call; すると
いろいろ必要なサブルーチンをエクスポートしてくれるので、
importの中でこんな感じで回せば使えるっぽい。
おてがる!

    filter_add sub {
        my $status;
        if ($status = filter_read()) {
            die "filter abort. status: $status. " if $status < 0;
 
            s!(^|\s*vs\s|;\s*)(\s*)(\w+)(\s*){!
                my $p = index($1, 'vs') > -1 ? ',' : $1;
                $defined_in_caller->($3) or $is_registered_block->($3) ? "$1@{[$2||'']}$3@{[$4||'']}" : "${p}spec '${3}' => sub {";
            !emsgx;
        };
        return $status;
    };


なお、複雑な構文の場合はDevel::Declareを使うとよいらしい。
CPANモジュールでソースフィルタを使っているモジュールはTryCatch、Smart::Commentsなどがあるらしい。


コード書いたのはずいぶん前だけどそういえばブログ最近書いてないなと思ったので書いた

#chibapm いってきたたたたた

#1も#2も行ったけど#1のレポ牡蠣忘れてたのでついでにかく。

#1

内容は他の方々が既にしっかりまとめてくださってるのでそれに頼らせていただくとして、
Perl初心者な人とある程度がっつり使ってる人の両方がいて、
Perl初心者な人はこういうのやりたい!もっとうまいやりかた教えて!みたいなかんじで、
懇親会でも他の言語と比べてみてどうだとか、突き詰めれば手続き型でちゃんとロジック分割して疎結合に書けなければ他のパラダイムでも酷いコード書きがちだよねとか、
そんな感じの事をゆるふわに語ってた記憶があります。たのしかったー

Chiba.pm#1でLTしました。 - arveltの技術メモ
Chiba.pm#1に行ってきた。 - ほっけの足あと
Chiba.pm #1 に行ってきた ( #chibapm ) - WebService::Blog->new( user => ’hide_o_55’ )
Chiba.pm #1 に参加してきました - その手の平は尻もつかめるさ
Chiba.pm #1 に行きました - ああああ
Chiba.pm #1に行ってきました - 日々の覚書
Chiba.pm #1に行ってきた - YellowStore
Chiba.pm #1 に参加してきました - その手の平は尻もつかめるさ
#chibapm #1に行ってきた。 - masasuzu 技術メモ


ぼくのすらいど

#2

もくもくたいむ

市川市民会館という場所でやったんですが、ぼくははやく行ってててきとーにもくもく出来そうなところ探してました。
そしたら市川市民会館に無料休憩所なるところがあって、そこは超静かにもくもくしながら待てて凄い良い感じで、そこで開始までずっともくもくしてました。
電源は使っていいのか分からないので使わなかったですが、たぶん言えば使わせてもらえたかもなぁ。
そんなかんじで待ってたらstudio3104さんと、masasuzさんがきて一緒にもくもくしはじめて、
時間が近づいてきてほっけみりんさんとかkaztrさんとかもきはじめてそろそろかーっておもってたら始まるぽかったので移動しました。

本編

ぼくのトークからはじまりましたが、DBI便利だよ!DBIx::Sunny使うともっと便利だよ!更にDBIx::Handlerとかの周辺モジュールと組み合わせるとだいぶいいかんじなので調べてみて!ってことをはなしました。
ぼくのemacsのsyntax highlightをそのまま使ったら全然みえなくて焦った。プロジェクタで出してるからかなと思ったけどふつーにディスプレイで見ても見づらいのでやっぱりemacsでの色の付け方見なおそうかなと思いました。
質問で出てたDBIx::SunnyとDBIx::Handlerの組み合わせですが、xaicronさんのHokkaido.pm#9のスライドと同様にRootClassでもできますが、kazeburoさんの記事によると

RootClass を使う場合は、connectionが確立するまでは DBIx::Sunny が呼ばれないので Print、RaiseError等は接続前に指定しておく必要がある。BKですね。

ということなので、DBIx::Handlerの$optsにdbi_classを指定するのが良いようです。documentにみあたりませんでしたが。。。


yoku0825さんはXtraDB Clusterの説明と実際に使ってみてどうなのかというお話をしていました。
残念ながらぼくの知識では付いていくことすらできず、とりあえずmasterを複数作ってmongodbぽいノリで使えるように出来るらしいということと、類似品にndbclusterというのがあるというのを覚えたので、これから勉強しようとおもいました!


このあとはLTなのですが、moznion氏が最初からかっ飛ばしてくれてvimの設定とかの自動切り替えツールを作っていてペアプロで便利って言ってたり(PC2台使えばいいよねというオチ付き)して会場を爆笑の渦に巻き込んで一気に会場に熱を入れたり、
vimのテンプレート機能が便利というお話があったり、XSモジュール書いてみたけどM::B::Pluggable思ったより使いやすかったというお話があったり、HyperLogLogというアルゴリズムを知れたり、某社がやってるようなbashでの開発みたいなのはユニケージ開発手法という名前が付いているという事を知れたり、Mac::Pasteboardでコピペした文字列を良い感じにIRCとかに投げてめもったりする話をがあったり、vagrantが便利というお話があったり、pt-query-digestが便利というお話があったり、Perlの学習がてらどういう感じの出力になるのかひとめで分かる感じのPerlのリファレンスサイトを作っているよというお話があったり、
これほんとにLTかってぐらい充実した内容で、どれも面白かったり便利そうだったり良さそうだったりしてすごい良い感じでした。
雰囲気も和気あいあいとしててゆるふわでよかった。
全員LTするとかなると、知らないひとでも懇親会で「ああ!あの発表してたひとか!」とかなるので便利だし、idと顔が一致するしとてもよかったです。
懇親会ではひたすらkenjiskywalkerさんとはなしてたりしてtypestarさんがpokeすごい送ってくるはなしとか、弊社と御社の勉強会どんなかんじなの情報交換とかしてた記憶があります。
勉強会おじゃまします!とか言ったのでこんど行くと思いますので、罠仕掛けたりバット振りかぶって待ち構えたりせずに暖かく迎えてくれないかなとびくびくしながら打診リプライを送ると思います。


すらいど

まとめ

たのしいのでみんなChiba.pmくるといいよ!

plenv.elを書いてみた。あるいはflymakeでplenvのperlを使うには

ちょびっと追記したり、書きなおしたりしました。

Hachioji.pm #26のハッカソンでなにつくろうかなーと思ってたので、
ちょうど旬だったplenvemacs lisp拡張でも書いてみようかなと思い至った。

plenv.el

(require 'plenv)

としておくと、だいたいperlbrew.elで出来た事は出来るようにしたりしたり、
plenv が良い感じ - unknownplace.orgとか読んでふーんってなったので、flymakeとの連携が楽になるようにバージョン検出の部分をemacs lispに移植したりしてる。


具体的にはguess-plenv-perl-pathとguess-plenv-versionというやつを提供していて、
guess-plenv-perl-pathを使うとplenvで使うperlのホンモノのpathが取れたり、
後者はカレントディレクトリで使われるperlのpathを返してくるようになっている。
これによってshell scriptを経由しなくなったのでそういう部分のオーバーヘッドはだいぶ減ったはず。
あとはFile::Basename#dirname的な処理をもうちょっとシンプルにするともうちょっと速くなるはずかなというところ。
正規表現でやるといいのかなこういうの。うまい方法だれか教えて下さい!


ところでplenvにはrbenv shellにあたるものがない。これはPLENV_VERSIONという環境変数で代替しているようで、

PLENV_VERSION=5.16.2 plenv exec hoge.pl

とかやって使うっぽい。shellに慣れてれば楽かもしれない。
しかし、emacsなどではそうも行かないのでplenv-shellというのをでっちあげて、setenvするようにしたりとかいうのを工夫したりした。

flymakeでplenvのperlを使うには

plenv.elがguess-plenv-perl-pathという関数を定義するので、
それを使って良い感じにflymakeのperlのパスを弄ってあげると良い。
ぼくのemacs lispでは以下のような感じになっている。

(require 'plenv) ;; for guess-plenv-perl-path
(require 'flymake)

(defconst flymake-allowed-perl-file-name-masks
  '(("\\.pl$"    flymake-perl-init)
    ("\\.pm$"   flymake-perl-init)
    ("\\.psgi$" flymake-perl-init)
    ("\\.t$"    flymake-perl-init)))

(defun flymake-perl-init ()
  (let* ((temp-file (flymake-init-create-temp-buffer-copy
                     'flymake-create-temp-with-folder-structure))
         (local-file (file-relative-name
                      temp-file
                      (file-name-directory buffer-file-name))))
    (list (guess-plenv-perl-path) (list "-wc" local-file))))

書いてみた感想

emacs lispほとんど書いた事なかったので完全に手探りだった。
基本的にはこれのperlbrew版に当たる、perlbrew.elをコピペしてコードを読みながら書き換えて行った。
というか、replace-string perlbrew plenvした。
思ってたほどemacs lispはむずかしくなくて、lispの流儀をなんとなく感じながら書き換えたらなんとなく動くものが出来た。
個人的にはflymakeするときに使うperlがよしなに切り替えられればゴールとしては十分だったので、
一旦そこまで作って、ブログを書くに至った。
あとは、defmacroするのが楽しかった。

Hachioji.pm #26の感想

Hachioji.pmは初参加だったのだけど、スライドのupに苦戦してメモ取れてなかったりでざっくりとしか覚えていなかったり、その2日後が引越しだったので焦ってたということもあり感想記事を書いていなかったので今ざっくり感想を書いておこうと思う。
ハッカソンも初参加でおどおどしてしまったけど、次からはなかなかもくもくできそう!って感じだった。
本編もid:moznionさんをひたすら弄ってたり、隣の個室の人となんか叫んでたり、すごくおもしろかった。

TengでRowObject自身を最新にする

以下のような、RowObject内の値が信頼出来なくなるようなupdateをかけたあとに、
masterからrefetchし直した値が引けると嬉しいのかなと思った。

say($row->xxx_pt); # 20
$row->update(+{ xxx_pt => \sprintf('xxx_pt + %d', 10) });
say($row->xxx_pt); # "xxx_pt's row data is untrusted. by your update query." で死ぬ

refetchした値を自分自身に適用して、ほげほげしてくれるといいのかなとおもった。

say($row->xxx_pt); # 20
$row->update(+{ xxx_pt => \sprintf('xxx_pt + %d', 10) }); ## 内部でrefetchして自分自身を更新する
say($row->xxx_pt); # 30? 40? ...

なんかRowObjectは不変であるべきな気もしてて、そういう意味では根本的に間違ってる気もするけど、
ScalarRefでなく普通の値を渡したときは新しい値がちゃんと取れる事を考えるとまあアリなアプローチなのかなとも思いつつ。

ユーザーに見せる場合はむしろ他の場所で行われたupdateは無視して30と表示させたほうが誤解されにくい気もしつつ、
SELECTしてくる前に他の場所でupdateが行われた場合にはそれでもあまり意味が無いなと思うと最新の値を表示させたほうがいいのかなと思いつつ。

もやもや。


実装自体はこんな感じで出来そうなきがする。

package Proj::DB::Row;
use strict;
use warnings;
use utf8;

use parent qw/Teng::Row/;

sub self_refetch {
    my($self, $teng) = @_;
    $self->{teng} = $teng if $teng;

    my $new = $self->refetch;
    %$self = %$new; ## shallow copy

    return $self;
}

sub update {
    my $self = shift;
    my $rows = $self->SUPER::update(@_);
    $self->self_refetch;
    return $rows;
}

1;