2013年11月2日土曜日

[Angular.js]factoryとserviceの使い分けについて

最近、プライベートのプロダクトでAngular.jsを勉強がてら使っている。
Yeomanでgenerator-angularを使う方法でやってたり。
で、結構悩んで調べたことがあったので、メモ。
※使い始めて間もないので、間違いがあったらご指摘ください。

サービスを作るやり方、Angular.jsだと色々あります。
yeoman-generatorのREADMEを参考に挙げると

service, factory, provider, value, constant
と、実にたくさん。

providerとvalueとconstantは、名前からなんとなく使い分けが判るんだけど、
serviceとfactoryの違いって何よ?って結構悩んだ。
簡単な実装例だと、「結局どっちも書き方が違うだけで同じ事してるやん」って思ってしまって…。

悩んだ時はソース嫁、という事で読んで見ました。
実装はこのへん。

factoryは、渡したfunctionをfunctionのままサービスに登録する。
serviceは、渡したfunctionをコンストラクタとしてインスタンスを生成し、そのインスタンスをサービスに登録する。

という違いがある様子。

それで何が変わるのか?というと。

serviceはインスタンスにしてから登録する。
JavaScriptのオブジェクトにはprivateが無いので、serviceで登録したコンストラクタで定義したメソッドは、何処からでも参照する事が出来てしまう。

一方、factoryはfunctionをそのまま登録する。
そして、factoryで登録したサービスから見えるのは、function内でreturnしたオブジェクトだけ。
なので、以下のように書いておくと、privateのような事が出来る。
angular.module('sampleServices', []).
    factory('nyanService', function() {
        var privateValue = 0;
        var privateMethod = function() {
            privateValue++;
        };
        return {
            publicMethod: function() {
                privateMethod();
                return privateValue();
            }
        };
    });
テストはこんな感じのが通る。
  describe('factory tests', function() {
      it('publicMethodがサービス経由で見えること', function() {
          expect(nyanService.publicMethod).toBeDefined();
      });
      it('privateMethodがサービス経由で見えないこと', function() {
          expect(nyanService.privateMethod).toBeUndefined();
      });
      it('privateValueがサービス経由で見えないこと', function() {
          expect(nyanService.privateValue).toBeUndefined();
      });
      it('1回実行すると1が帰ってくること', function() {
          expect(nyanService.publicMethod()).toBe(1);
      });
      it('2回実行すると2が帰ってくること', function() {
          nyanService.publicMethod();
          expect(nyanService.publicMethod()).toBe(2);
      });
  });
なので、使い分けとしては
  • service: ある程度まとまった関数群をまとめたUtilオブジェクト向け
  • factory: ビジネスロジックをしっかり書いたりするようなモデル向け
という感じでいいんじゃないかと。

Yeomanが作ってくれるfactoryのコードにも
return{}の前に"Public API here"ってコメント書いてあるし、この使い分けで間違いない…かな?

2013年10月18日金曜日

VirtualBox上のUbuntuでChromeが固まる問題の対処法

最近、Windows上で開発してたんですが、やっぱりイヤンな感じが消えなかったので、Ubuntu環境復活させることにしました。
とはいえ、専用マシンは家に無いので、VirtualBox上に入れてます。

その、VirtualBox上のUbuntu13.04で動いてるChrome、
サイトを開いて1秒程度で、描画領域が完全に固まってしまいます。

個人的にFireFox肌に合わないので、代替でFireFoxを使うという選択肢は無いので、
Google先生に聞いてみました。
解決はしたんだけど、日本語の情報がサクッと出てこなかったのでメモメモ。

VirtualBoxの3Dアクセラレーション関係?が原因らしい。
(この辺は真面目に調べてないので怪しい。)

とりあえず、起動オプションに

--blacklist-accelerated-compositing

を付けると、固まらなくなります。

Unityのランチャに登録する場合は

cp /usr/share/applications/google-chrome.desktop ~/.local/share/applications/google-chrome.desktop

こんなかんじで、ランチャを自分のhomeディレクトリにコピってきて、エディタで開いて、Execの所に起動オプションを追記。
(複数あるので全部追記してください。)
その後、chmod +xで実行権限を与えて、手動で実行。
実行後、Unityのランチャ上のアイコンを右クリックして、Launcherに登録とすればOK。

参考にしたのは以下のフォーラムの内容です。
http://ubuntuforums.org/showthread.php?t=1974800

いじょ。


Ubuntu専用マシン欲しいなぁ…(・ω・`

2013年8月31日土曜日

Ubuntu13.04 で usermod -G やらかした後の復旧まとめ

Ubuntuでやりがち(だよね?)なミス、usermod -Gでユーザグループ追加しようとして、管理グループからユーザを外してしまいsudo出来なくなるアレ。
最近、自宅の仮想環境上のUbuntuに触ってなかったんですが、久々にログインしたらsudo権限が無く……。
前に吹っ飛ばしたままだったようなので、復旧させました。

せっかくなんで手順メモ。
SSはとり忘れたッス。


  1. 再起動してリカバリーモードで起動しなおす。
    Shift押しっぱなしにすると、GRUBが開くので、
    Advanced options for Ubuntuを選択。
    一覧から(recovery mode)とついたOSを選択。たくさん出たら上にあるやつでいいです。
  2. fsckを選択して、Read/Writeモードでマウントしなおす。
    リカバリーモード起動時は、ファイルシステムがリードオンリーなので、
    このままだと/etc/groupに書き込みすることが出来ず、グループの追加が行えません。
    なので、書き込み可能状態で再マウントする必要があります。
    本来、fsckコマンドは、ファイルシステムをチェックするコマンドですが、
    リカバリーモードの場合、実行後にファイルシステムを再マウントしてくれます。
  3. rootを選択して、ターミナルを起動。
  4. gpasswdコマンドでsudoグループにユーザを追加。
    gpasswd -a user_name sudo
    コマンドは上記のものを使いましょう。
    usermod -GはUbuntuで利用すると、指定したグループ以外を消してしまうようなので。
    普段使うときもgpasswdのほうがいいです。Ubuntuだったら。(Debianはどうなんだろう…)
  5. resumeで再起動。
  6. sudoして確認してみる。
こんな感じ。
ポイントをまとめると、
  • リカバリーモード起動直後は、リードオンリーで書き込みが出来ない。
    書き込み可能で再マウントするにはfsckを実行する。
  • Ubuntu 13.04の場合、sudo可能なグループはsudo。
    昔はadminだった気がする…。いつ変わったんだろう?
こんな感じですかねー。

Ubuntu、日本だけかもしれないけど、9~11位のバージョンのときが流行のピークで
Google先生に聞いてもそれくらいのバージョンのときの解決方法が上のほうに出てくるんですよね。
すると、今の最新とは結構違った内容が出てきちゃったりします。

まぁ、GnomeからUnityになったあたりと、Macが開発用端末として流行り始めた時期から、
ユーザが減ったんだろうけど…。

出始めは悪評多かったUnityですけど、最新だと結構きれいに動くし、
VirtualBox上でも設定さえしっかりすればきれいに動いてくれるので
個人的には、これからも最新バージョンを追って使い続けたいディストリだったりします。

2013年8月28日水曜日

Symfony2のblog tutorialを2.3に対応させる。

Symfony2、これまでほぼノータッチだったんですが、触る必要が出てきたので、
ざっと基本的な部分を把握するために、blogチュートリアルをやってみました。

URLはこちら。
※海外有志の方が作成した、symblogチュートリアルじゃない方です。

こちら、ヘッダ部分に記載されているとおり、バージョン2.0.7向けの内容です。
ですが、現在最新のSymfony2は2.3.4。
deprecatedになっているメソッドがあったり、そもそも存在しないAPIがあったりするので、
いくつか、気づいた変更点をまとめておきます。

  1. getEntityManegerメソッドがdeprecated。
    DB操作を行う、EntityManagerを取得するメソッド名が変更になっています。
    使い方はそのままなので、単純にメソッド名を変えれば良さそう。
    http://docs.symfony.gr.jp/sf2-blog-tutorial/05-list-page.html
    例えば、上のページのDefaultController.phpのindexActionは以下のようになります。
    public function indexAction()
    {
    //    $em = $this->getDoctrine()->getEntityManager();  // 旧
        $em = $this->getDoctrine()->getManager();  // 新
        $posts = $em->getRepository('MyBlogBundle:Post')->findAll();
  2. FormのbindRequestメソッドが廃止。
    リクエストをバインドする時は、単純にbindメソッドを使えば良いよう。
    http://docs.symfony.gr.jp/sf2-blog-tutorial/07-create-form.html
    例えば、上のページのDefaultController.phpのnewActionのバリデーション周りは以下のようになります。
    // バリデーション
    $request = $this->getRequest();
    if ('POST' === $request->getMethod()) {
    //    $form->bindRequest($request);  // 修正前
        $form->bind($request);    // 修正後
        if ($form->isValid()) {
    
  3. フラッシュメッセージ関連
    Session周りのAPIが変更になった影響で、フラッシュメッセージの設定方法、表示方法共に変更となっています。
    まず、大きな変更として、フラッシュメッセージは常に配列で扱われるようになった、という所があるようです。
    ですので、表示の際は配列であることを意識したビューの書き方をする必要があります。

    フラッシュメッセージを設定するController側の変更です。
    http://docs.symfony.gr.jp/sf2-blog-tutorial/customize/03-session-flash.html
    上のページのDefaultController.phpのnewActionは以下のようになります。
    public function newAction()
        {
            // ...
    //        $this->get('session')->setFlash('my_blog', '記事を追加しました');  // 修正前
            $this->get('session')->getFlashBag()->add('my_blog', '記事を追加しました');    // 修正後
            return $this->redirect($this->generateUrl('blog_index'));
            // ...
        }
    ちなみに、FlashBagにmessageを設定するメソッドは、add以外にsetもあります。
    違いは何かというと、FlashBag.phpのソースを直接見るとわかりやすいのですが、
    add: $this->flashes[$type][] = $message;
    set: $this->flashes[$type] = $messages;
    と、addは指定したtypeの配列にメッセージ(文字列)を追加するのに対し、
    setは指定したtypeの配列そのものを上書きする形になります。
    なので、普通に使う分にはadd使ったほうが良いんじゃないかと思います。

    次、テンプレート側の変更。
    http://docs.symfony.gr.jp/sf2-blog-tutorial/customize/03-session-flash.html
    同じく上のページのindex.html.twigのフラッシュメッセージ周りは以下のようになります。
    {% for type, messages in app.session.flashbag.all() %}
        {% for message in messages %}
            
    {{ message }}
    {% endfor %} {% endfor %}
    ちょっとこれは、変更前/後の比較表示が難しいので、変更後だけ。
    一見するとコードが増えて複雑なように見えますが、システムが複雑化してきて、
    フラッシュメッセージの量や種類が増えてくると、便利なんじゃないかな、と。
    (とはいえ、一度に大量のフラッシュメッセージが表示されるサービスは、微妙なようにも思いますが…ww)
  4. FormTypeのgetDefaultOptionsメソッドが廃止。
    変わりに追加となった、setDefaultOptionsを利用することになります。
    http://docs.symfony.gr.jp/sf2-blog-tutorial/customize/05-create-form-class.html
    上のページの"PostType.php"のgetDefaultOptionsは以下のようになります。
    // use周りも追加・変更があるので注意
    use Symfony\Component\Form\AbstractType;
    // use Symfony\Component\Form\FormBuilder; // 削除
    use Symfony\Component\Form\FormBuilderInterface; // 追加
    use Symfony\Component\OptionsResolver\OptionsResolverInterface; // 追加
    
    // 中略
    
        public function setDefaultOptions(OptionsResolverInterface $resolver) {
            $resolver->setDefaults(array(
                'data_class' => 'My\BlogBUndle\Entity\Post',
            ));
        }
    戻り値として返すのではなく、$resolverのsetDefaultsに設定する形になった、と覚えればOKでしょう。たぶん。
    ちなみに、同時に廃止となったgetAllowedValuesメソッドの役割も$resolverに対して行うようです。
気づいたのは大体こんな感じですね。
deprecatedなメソッドは、もしかしたら見落としがまだあるかもしれません。
詳しくは、SymfonyのUPGRADE-2.x.mdを見るのがいいと思います。
2.1, 2.2までは有志の方が日本語に訳してくださったものがあるのでそれを参考にするとわかりやすいと思います。
上で書かれている内容は、2.1のタイミングで変更になったものばかりでした。

2013年4月23日火曜日

Highcharts 3.0で追加された Axis.toPixels() が色々捗る件


Highchartsという、jQueryベースのグラフ描画ライブラリが有ります。
グラフ描画に特化したライブラリの中では、一番、かっこよくてダサくなく実用的なライブラリだと個人的には思っています。
商用利用時は有料ですが、個人利用であれば無料で使えます。
http://www.highcharts.com/
公式サイトはこちら。

最近?だと、PHPMyAdminのグラフ描画なんかでも使われてますね。
※データベースの「状態」や、クエリのプロファイリングで見れます。

ライセンスがどうしても気に食わない、とか
もっとスゲー事やりたい!って人は
D3.jsをどうぞ。
グラフ化よりもう少し上、データビジュアライゼーションのためのライブラリです。
http://ja.d3js.node.ws/
※jQueryに依存してません。

さて。
Highcharts、2013/03/22に3.0にバージョンがあがりました。
パッと見一番解りやすいのは、デフォルトのグラフの色が変わったことなんですが
その他にも、バブルチャートが追加されたり、色々追加されています。
http://www.highcharts.com/component/content/article/2-news/54-highcharts-3-0-released
大きな変更点はこちらの公式ニュースを読んでください。

細かいところでも、いくつかAPIに追加があるのですが、
今回は、タイトルに有るとおり、3.0で追加されたAxis.toPixels()のお話です。

http://api.highcharts.com/highcharts#Axis.toPixels()
公式APIドキュメントが上記です。
APIドキュメント上にサンプルが無いので、
公式デモの一番最初のグラフのソースをベースに紹介しますね。

グラフの下に、謎の数字が出ているかと思います。
これが、「X軸が3の値をプロットするときのX座標」を表しています。
このグラフだと、X軸にcategoryで名前を与えているので、ちょうどAprの所になります。
2にするとMarの所のX座標になります。
小数点でもOKなので、例えば2.5なんて入れると、MarとAprの中間点の座標が取れます。

コレが、もー本当に捗るんですよ!
こいつが後1ヵ月遅ければ、今の私はデスマっていたかもしれないレベルで。

…先ほどのサンプルだけだと何が捗るかよーわからんと思うので、説明しますね。

例えば、先ほどのグラフで、東京の5月の気温をうまくとることが出来ず、データ上0になってしまったとします。
グラフ上すごいV字になってしまいます。
大体の人は「なんか障害があって取れなかったのかな?」と思ってくれるでしょうが、気持ち悪いので、
ちゃんと理由を吹き出しで明示したくなりました。

グラフの下に、HTMLで普通に記述しても良いのですが、
せっかくなので、Excel(笑)のオートシェイプみたいに吹き出しを上に乗せたい!

という時に、こんな感じで書けるんです。

重要なポイントは、chart.events.loadとchart.events.redrawで描画ファンクションを呼ぶ事。
特に、redrawが重要で、ここで再描画するようにしておく事で、
画面のサイズ変更時にも適切な位置に配置してくれます。
Edit in JSFiddleをクリックして、新しいウィンドウででデモ表示して横幅を変えると解りやすいです。
jQueryのwindow resizeのイベントにバインドして再描画しようとしても、上手くできないので注意。

後、y軸の値、今回のケースでは0だと解り切っていましたが、
場合によっては変動する可能性もあるので、4行目のようにして取得すると良いです。

今回、吹き出しのCSSが面倒だったんでAAにしちゃいましたが、
#textのCSSを弄れば、イケてる吹き出しにももちろん出来ます。
widthとかmargin-leftも、動的に取るようにすれば本当に自由に吹き出しが表示出来ますね。
また、#textはただのDIV要素なので、onclick等のイベントも設定出来ます。
「吹き出しをクリックしたら、具体的な障害内容が出る」とかも、jQueryの知識だけで簡単に出来ますね!

というわけで、Highcharts 3.0で追加されたAPIがすごく捗るお話でした。

2013年4月10日水曜日

CakePHP 2系で、独自カラムに対してPaginatorを使ってソートする方法

 
 このやり方が正しいかは解りませんが、
 そこそこ綺麗に解決したような気がするので、覚書として載せておきます。
 
 今回、割と特例的な対応だったので、全部MVCの中で完結させていますが、
 頻繁に使う用であれば、ComponentにするとかPaginator拡張するとかしても良いと思います。
 
 
 さて、まずは簡単な仕様の場合。
 
 CakePHPにはバーチャルフィールドという便利な仕組みがあります。
 「カラムaとカラムbをくっつけてカラムcにしたい!」なんて時にはこう書きます。
public $virtualFields = array(
    'column_c' => 'CONCAT(Hoge.column_a, " ", Hoge.column_b)'
);
参照:http://book.cakephp.org/2.0/en/models/virtual-fields.html
 
 見ての通り、SQL文をそのまま書く事になります。
 具体的には、SELECT句のフィールドに、そのまま
 SELECT CONCAT(Hoge.column_a, " ", Hoge.column_b) AS Hoge__column_c ...
 と記述されるようなイメージです。
 
 SQL文そのままなので、RDBMSが変わると動かなくなってしまうという欠点もあるのですが、
 バーチャルフィールドで定義したカラムは、通常のカラム同様に扱う事が出来るというメリットが有ります。
 なので、find時のorder句に、'column_c DESC'とか書いても、ちゃんとソートしてくれるのです。
 
 便利!
 
 
 さて、ちょっと複雑な仕様のカラムを追加する必要が出てきたとします。
 
 SQL文をそのまま書く、という事は、SQL文で解決できないような値はバーチャルフィールドで扱えません。
 例えば、MySQLの場合、順位を取得するクエリって簡単にはかけないんですよね。
 「複数のカラムが有り、それぞれのカラムの順位を合計した値を「ランク」として表示したい」
 なんて仕様は、さすがにバーチャルフィールドでは荷が重いわけです。
 
 そういう場合、どうするか?というと、
 Modelのコールバックメソッドの、afterFindの中で計算して設定します。
 
 諸々省略して雰囲気だけ伝えるコードを書くと
public function afterFind($results, $primary = false) {
 foreach($results as $key => $result) {
  // なんか計算したりして追加するカラムの値を作る
  $hoge = $aaa + $bbb;
  $results[$key][$this->alias]['hoge'] = $hoge;
 }
 return $results;
}
こんな感じで、hogeってカラムを増やします。
 参照:http://book.cakephp.org/2.0/en/models/callback-methods.html
 
 さて、ここで増やしたカラムに対して、Paginatorでソートしたい場合はどうすれば良いでしょうか?
 
 Helperはさすがにここまでやってくれないので、コントローラ辺りで自前でソートすることになります。
 幸い、CakePHPにはSet(2.2以降だとHashですね)という優秀なコアライブラリがあり、
 上記のafterFindが'Fuga'というモデル内の物だったとすると
$data = $this->Fuga->findAll(/* 省略 */);
$data = Set::sort($data, '/Fuga/hoge');
って記述で簡単にソートしてくれます。
 参照:http://book.cakephp.org/2.0/en/core-utility-libraries/set.html
 
 Paginatorでソートする場合、ソートのパラメータはデフォルトだとnamedに入るので、
 $this->request->named['sort']の値が特定の物だった場合に、
 Set::sortでソートしてやればよい事になります。
 
 先ほどから例に出している、Fugaモデルのhogeカラムで独自ソートしたい場合は、
$data = $this->paginate();
if (isset($this->request->named['sort']) && $this->request->named['sort'] == 'Fuga.hoge') {
 Set::sort($data, '/Fuga/hoge', $this->request->named['dir']);
}
$this->set('data', $data);

 とすればソートされて表示されます。
 
 
 ヤッター!これで独自カラムでソートができたぞー!
 
 と喜び勇んでいると、「デフォルト時にhogeでソートしておいてほしい」
 なんて要求が来たりするわけです。
 
 Paginatorでソートする場合の、クエリの条件は、Controllerの$paginateプロパティに記述します。
 
 これが、もし、fugasテーブル上のpiyoという、存在するカラムに対するソートであれば、
$paginate = array(
 'order' => array('piyo' => ASC)
);
と記述してしまえば良いのですが、こちらのパラメータ、そのまま、SQLのORDER BYに入るので、
 hogeカラムのように、テーブル上にもバーチャルフィールド上にも存在しないカラム名を指定すると
 「そんなカラムねーよばーか」ってMySQLに怒られてしまいます。
 
 仕方がないので、独自ソートするときのif文条件を変更して、
 sortパラメータが無い場合も、hogeでソートする
 というように変更するのが、手っ取り早い対応になるのですが、
 これを行うと、HTML上のasc/descの表示が狂ったり、クリック時の挙動がおかしくなります。
 
 何故か、というと、CakeRequestのparamsに格納された、pagingという値(連想配列です)を元に、
 「今ソートされているキーはどれか?」を判定して、表示上に取り入れているからなんですね。
 具体的には、PaginatorHelperのsortKeyというメソッドで判定しているので、その辺を読んでみてください。
 参照:http://book.cakephp.org/2.0/en/core-libraries/helpers/paginator.html#PaginatorHelper::sortKey
 
 (書いていて気づいたのですが、デフォルト時のソート云々抜きにして、
 Set::sortでデータだけソートしている状態だと、挙動がおかしくなっていた可能性は高いですねー。
 検証してないので、一見うまく動いてくれてる可能性もありますけども。)
 
 ここを解決するためには、Set::sortで独自ソートした後で、$this->request->params['paging']['order']の値を書き換えます。
 paramsの値は直接アクセスして書き換えることが出来ないので、CakeRequestのoffsetSetを使います。
 纏めると、先ほどのif文の中身は、こうなります。
if(/* 省略 */) {
 Set::sort($data, '/Fuga/hoge', $this->request->named['dir']);
 // 現在セットされているpagingパラメータを取得、値を上書きしてから再セット
 $pagingParams = $this->request->offsetGet('paging');
 $pagingParams['Fuga']['order'] = array('Fuga.hoge' => $dir);
 $this->request->offsetSet('pagin', $pagingParams);
}
これにて、無事、独自追加したフィールドに対するページングがうまく働くようになりました。
 
おわり!

2013年3月22日金曜日

Jenkinsの「定期的に実行」で地味に感動した

ラベルの通り完全に小ネタ。


皆さんご存知の通り、Jenkinsのビルド・トリガの「定期的に実行」は、crontabの書式で、スケジュールを記述します。
crontabの書式はWikipediaでも見ておいてください。この辺。
http://ja.wikipedia.org/wiki/Crontab

さて、この入力欄ですが。
良く見なくても、複数行入力出来ますね?

複数行入力すると、ちゃんと、全部の行実行してくれます。
どういう事かというと…。

例えば、「平日毎日、朝9時にレポートを送りたい。でも、金曜は夕方の17時にもう1回レポートを送りたい」みたいなのが有った時、
レポート送信に/home/hoge/fuga.shなんてシェルを使うとすると、crontabに

0 9 * * 1-4 /home/hoge/fuga.sh
0 9,17 * * 5 /home/hoge/fuga.sh

なんて、書く事になるじゃないですか。

これが、Jenkinsだと、1つのジョブの定期的に実行のスケジュールの所に

0 9 * * 1-4
0 9,17 * * 5

って書くだけで良いわけです。
同じ事やってんのに、スケジュールのためだけに複数設定する必要が無いので
整理出来て見やすいし、混乱も少ない。

良いですね。


単純に便利で感動したのでメモでした。


(´-`).oO(混沌としたcrontab、全部Jenkinsに移したくなるわぁ…)

2013年2月22日金曜日

CakePHPのSet::apply / FWを使った時の可読性

久々の記事が、割と具体的な内容ですね。
今やってる仕事が仕事で、どこまで書いていいかわからないので
こういう小ネタで攻めることにしました。

さて、最近はCakePHPを使うことが多いので、その話題。
CakePHP2系のお話です。(たぶん1.3でも一緒だと思う)

hogesってテーブルに、何らかの値が入ってて
それをまとめてfindAllしてきた後

  • 特定のカラムの合計値がほしい
  • 最大値がほしい

ってことはよくあって、
そのたびにわざわざCOUNTやらMAXやらのクエリ投げるのも非効率じゃないですか。

そういう時に、PHP慣れててCake慣れてない人とかだと

$hoges = $this->Hoge->find('all');
$fugas = array();
foreach ($hoges as $hoge) {
    $fugas[] = $hoge['Hoge']['fuga'];
}

$max = max($fugas);
$sum = array_sum($fugas);

って書く人が居るんですよ。

後、多言語から来た人で、PHPのリファレンスをあんまり読まない人が、
maxとかarray_sumとか使わずに、foreachの中で計算したりする事もあったりして
後で読むときに非常に面倒なコードになっていたりすることもあります。

Cakeには便利なSetってコアライブラリがるので

$hoges = $this->Hoge->find('all');
$max = Set::apply('/Hoge/fuga', $hoges, 'max');
$sum = Set::apply('/Hoge/fuga', $hoges, 'array_sum');

って感じで、スマートに記述できます。
※Cake2.2以降だと、SetはHashってライブラリに置き換えられているので、そっち使ってくださいね。

詳細な使い方は公式を参照してください。
http://book.cakephp.org/2.0/ja/core-utility-libraries/set.html#Set::apply

Cakeの場合、Web系で必要になる煩雑になりがちな処理は
だいたい、コアライブラリにあるので、
自作する前にコアライブラリ探しましょう。
(文字列操作とか…ネ)

-----

コアライブラリを使わない結果、
非常に多数のネストが発生した上行数が多く、読みづらいコード
とかいうものを見てしまったので、こんな記事を書きました。

Set::applyを使ったところで、
コアライブラリの中で結構な量の処理が走るハズなので、
計算量的に得をするとか、メモリ使用量が少ない、とか、そういうことは無いと思います。
(実測までしてないケド)

私は、シビアにパフォーマンスを求められている環境でない限り、
実行速度よりも可読性を優先してコードを書くべきだと思っています。

パフォーマンスが必要になれば、後で、リファクタリングすればよいだけです。

可読性の高いコードの可読性を低く変えるのは簡単ですが、
可読性の低いコードの可読性を高くするのは大変デショ?


では、この場合、最初に紹介した、foreachとPHPの配列操作関数を使うのと、
CakePHPのAPIを利用したコード、
どちらのほうが可読性が高いと言えるのでしょうか?

確かに、CakePHPを知っている人であれば、後者のコードはすぐにわかりますが、
慣れていない人が見る場合前者のほうがわかりやすい可能性だってありますよね…。

※そもそも、コアライブラリにある物を書き直す=車輪の再発明ジャンって話もありますが。


ここで、CakePHP…というか、FWとは、言語をさらに抽象化する物ととらえます。

イメージ的には

下層 <-     -> 上層
機械語 ->C言語 -> PHP -> CakePHP

って感じで、よりWebに特化したAPIのレイヤーをPHPにかぶせているイメージです。


FWを使う、ということは、FWによりPHPを抽象化するということです。
であれば、無理に下層のレイヤーを触ること無く記述したほうが自然ではないでしょうか?

また、上層のレイヤーで記述できる物は極力上層レイヤーで書く、というルールを徹底すると、
「下層レイヤーの記述がある=上層レイヤーでは足りない何かを行うためのコード」
という意図がコードに生まれる、という利点もあります。

ですので、私は、FWの用意するライブラリで記述できる内容であれば、
なるべく、FWが用意した物を使ったほうが良いと思います。

2012年7月18日水曜日

JavaScriptのDateオブジェクトの挙動

ハマりメモ。

Dateをnewするときに渡す日付の書式でハマってしまった。

再現コード。
以下すべて、各ブラウザの開発ツールのコンソール上で実行。

Chrome 20.0.1132.57 m

new Date('2012/01/01 00:00:00');
Sun Jan 01 2012 00:00:00 GMT+0900 (東京 (標準時))
new Date('2012-01-01 00:00:00');
Sun Jan 01 2012 00:00:00 GMT+0900 (東京 (標準時))

FireFox 12.0

>>> new Date('2012/01/01 00:00:00');
Date {Sun Jan 01 2012 00:00:00 GMT+0900}
>>> new Date('2012-01-01 00:00:00');
Date {Invalid Date}

Safari 5.1.7

new Date('2012/01/01 00:00:00');
Sun Jan 01 2012 00:00:00 GMT+0900 (“Œ‹ž (•W€Žž))
new Date('2012-01-01 00:00:00');
Invalid Date


InternetExplorer 9

>> new Date('2012/01/01 00:00:00'); 
Sun Jan 1 00:00:00 UTC+0900 2012
>> new Date('2012-01-01 00:00:00');
Invalid Date



とある、JSライブラリを使ったちょっと凝った見た目の画面にて、
Chrome以外のブラウザで正常に表示できないという不具合が発生。

原因は、ライブラリに受け渡すXMLデータ上で、日付のフォーマットが
ハイフン区切りになっていたこと。
MySQLを扱ってる人には見なれたフォーマットですね。
JavaScript的には、スラッシュ区切りが正解なので、
うっかりそのまま出力した僕のミスです。

ただ、人の作ったライブラリ&日付の解釈が問題になるのがかなり奥の方のコードだったので
原因に気づくまで相当時間をかけてしまった…。

あと、「Chromeで見れればほかのブラウザでも見れるだろう」なんて安易な考えをしたのも間違い。
普段なら4ブラウザで確認はとるんだけど、
このライブラリの稼働実績がすでにあったので、
1つのブラウザで表示できれば問題ないと思ってしまった。

確認、大事。

ちなみに、Chromeはこんな書式でも正常に解釈したりする。


new Date('2012-01/01 00:00:00');
Sun Jan 01 2012 00:00:00 GMT+0900 (東京 (標準時))

日付の区切りは、-か/であればよい、という判断なのかな…?

2011年12月8日木曜日

Symfony2を試してみて[一人Web系AC2011]

連続でお送りする、PHPの有名FWお試し&比較記事シリーズです。
今日は、Symfony2についてです。

まずは、これまでと同じ仕様のサンプルを作成しました。
「同じ仕様って、どんな仕様なんじゃい?」
と言う方は、こちらの記事をどうぞ。最初のほうに書いてあります。

内容的にですが、Symfony2もblogチュートリアルがあるので、そちらを参考に一部手を加えました。
ページャ追加したり、formをせっかくだからクラスで作ってみたり、ですね。

そのソースが、こち…ら…。
すんませんまた上げ忘れて手元にありません何やって(ry
後日上げなおします。

■大体の流れ
前回と同様、Symfony2を読むための流れです。
  1. app/config/routing.ymlから、利用するバンドルを調べる
    今回のサンプルの場合、以下のような形になっています。
    sample:
        resource: "@MySampleBundle/Resources/config/routing.yml"
        prefix:   /sample
    resourceが挿す先は、バンドルのrouting設定ファイルです。
  2. バンドルのrouting.ymlを見て、コントローラと対応するメソッドを調べる。
    バンドル自体はsrcディレクトリの先にあります。
    今回の場合、パスは以下になります。
    src/My/SampleBundle/Resources/config/routing.yml
    patternにURIのパターンと、そのパターンでリクエストが来た場合に呼び出される内容がdefaultsに記載されています。
    sample_index:
        pattern: /
        defaults: { _controller: MySampleBundle:Default:index }
    この場合、MySampleBundleのDefaultControllerのindexAction()が呼ばれる事になります。
    {}で囲まれたものがある場合、それはアクションの引数になります。
  3. 対応するコントローラの処理を読む
    この辺の内容は、基本的なPHPです。
    MVCでいうMにあたる、DB操作についてはdoctrineを利用します。
    "MySampleBundle:Data"の場合、接続先DBの"Data"テーブルを読むと言う事さえ判れば、なんとなく読めると思います。
    ビューについては、$this->render()にて指定しています。
    最初の引数がテンプレート、次の引数がテンプレートに引き渡す値の配列ですね。
  4. テンプレートを読む
    テンプレートは[バンドルルート]/Resources/viewsにあります。
    コントローラ上で
    'MySampleBundle:Default:index.html.twig'
    として呼び出されていた場合、テンプレートのパスは以下になります。
    src/My/SampleBundle/Resources/views/Default/index.html.twig
    Symfonyの場合、テンプレートはTwigと言う物を使っています。
    書式的に難しい物では無いので、見れば大体は判ると思います。
    Twigは継承タイプのテンプレートなので、{% extends %}により他のテンプレートを継承している場合、まずそちらの内容が読み込まれます。
■実装しての感想
  • JavaのStruts/SpringFrameworkの組み合わせを思い出した。
    書式自体はぜんぜん違うけど、この、設定ファイルありきの書き方は、本当にソレっぽいなと思います。
    設定ファイルの書式自体は簡単なので、Springほど設定ファイルがゴミゴミすることもありませんが…。
  • 癖が強い。
    慣れの問題もあるかとは思うのですが、中々に癖が強いように感じられました。
    doctrineやTwig等、他のFW利用時にはあまり使わないものが多くあるからかもしれません。
  • Pagerにあたる機能はコアに無い。
    らしいです。
    今回は、本当に簡単なページャだったので、直接一覧ページのコードに埋め込む形で実装してしまいました。
    実際にサイトを実装する場合は、使い回しが出来るよう、独自バンドルを作ったりするんじゃないかと思います。
今日はここまで。
次回はCodeIgniterで実装してみようと思います。

2011年12月7日水曜日

PHPのボトルネック調査方法[Web系一人AC2011]

昨日は、ツレと布団の取り合いに負けた結果、体調不良で死にました。OMG...。
いい年して何たる失態。すみませんでした。

気を取り直して今日の記事。
今日の記事はSymfony2...ではなく、PHPのボトルネック調査方法について、です。
検証用のコードが一部間に合わなかったため、予定を変更してお送りさせていただきます…。

皆さん、PHPで作ったWebシステムのボトルネックって、どうやって調査してますか?
割と「コレ!」と言う手段を聞かなかったので、ちょっと自分が調査するときのポイントをまとめてみました。
まずは、全体的にざっくりと、どんなツールを使って見ているのか?

それでは、本題に参りましょう。


■どのリソースが重たいのかチェック
ブラウザ上からどのリソースの取得に時間がかかっているのかを調査します。
ここで利用するのが、FireFoxならFireBug、Chromeならデベロッパーツールです。
(デベロッパーツール=右クリックして「要素を検証」を選ぶと出てくるアレ)
得に、Networkタブを良く利用します。
実際に読み込んだすべてのリソースを、どの順番でどのくらい時間をかけて読み込んだのか?等が見れます。
ここで、実際に読み込みに時間がかかっているリソースを絞り込みます。

■関連するログを追跡
基本ですが…。
サーバ上で取っているログを一通り追います。
とくに、MySQL環境にてマズいSQLが問題になっている場合、slow.logはとても参考になります。
まれに、slow.logにたくさんクエリログが残っているのに放置しているプロジェクトなんてものもあります…。オソロシイ。
SQLに問題がある場合、大抵ログからもはっきりと追えるので、ここで原因がはっきりすれば、DBまわりのチューニングを行い、実際の効果を一度見てみても良いと思います。
時間帯によって重い時間帯がある、と言う場合、munin等リソースをグラフ化して見れるツールを入れておくと役に立ちます。

■試験環境で再現する場合は、本番を想定した負荷をかけながらやる
処理の内容や使っているライブラリによっては、一定以上の負荷があって始めて再現するようなボトルネックも多くあります。
あらかじめ、JMeter等のツールを利用して、本番を想定した負荷をかけながらチューニングしていきます。
※試験環境によっては、JMeterで負荷をかけすぎると同じ回線/サーバの利用者に迷惑がかかる可能性もあるので、気をつけてくださいね。
※くれぐれも、本番稼働中のサイトや他所のサイトに打ち込まないように。

■Xdebugのプロファイラを利用する
PHPスクリプトに対するプロファイラです。
これで、実際のどのメソッドのどのコードで時間がかかっているのか?を調べることができます。
ボトルネックがありそうなコードを見つけたときに、すぐに生のソースを見て重たそうなコードを目視で調べて直す、という行動を取ってしまう人も結構見てきましたが、
実際のボトルネックは目に見えない部分にあるケースもあるので、ちゃんとプロファイラでチェックしたほうが、経験上回り道になりません。
Xdebugが導入済みなのであれば、php.iniのxdebug.profiler_enableをOnにすると利用できます。
※プロファイリングするときは当然、普段以上に負荷がかかるので、本番サイトでONにしないように。
また、プロファイラの吐いたログは、単体では可読性にかけるので、GUIで表示してくれるツールを利用します。
私は、Linux環境だということと、コールグラフ等がグラフィカルに表示されるのがうれしいので、KCachegrindを使っています。



JavaやObjective-c(これはWeb系ではありませんが…)だと、割とボトルネック調査やプロファイラの使い方についての記事が多いのですが、いまいちPHPには無かったので挙げてみました。
自己流の部分が多く、当然これが最善とは言えませんが、参考になれば、と思います。

2011年12月5日月曜日

[CakePHP]Model名’Datum’、テーブル名'data'の罠[Web系一人AC2011]

土日は、YAuth的な理由(僕の場合はDですが。)により、blogを書けないことが判明しました。
と言うわけで、平日のみの更新とさせていただきます…。すいません。

さて。
今回は、前回話をした、CakePHPにてDatumというモデル名(テーブル名はdata)を使うとハマる、と言う件についてです。

■まえがき。
実はこの問題、「CakePHP Datum」というキーワードで検索すると、@uechocoさんのblogがクリティカルヒットします。
http://labs.uechoco.com/blog/2010/12/cakephp-model-lovers.html
こちらの記事です。
しかし、当時の自分は
「CakePHP data テーブル名」
なんて、ノイズが多そうな検索ばっかり試していて、自力で気づくまで延々と悩むと言う恐ろしい失態をしてしまいました。OMG....

せっかく自分でもソースを追ったので、記事にしておきます。

べ、別にネタが無いわけじゃないんだからねっ

■きっかけ

  • 最初に作った、ただのPHP版のサンプルでは、テーブル名を「data」にしていた。
  • CakePHPは、モデル名とテーブル名の命名に「モデル名は単数形、テーブル名は複数形の英単語」というルールがある。
    このルールを守ることにより、コーディング時に設定ファイルを書かなくても良い。
    モデル名/テーブル名とした場合の例):Sample/samples, User/users, Woman/women
  • 例を見て解るとおり、こちら単純に英単語に「s」をつければ良いわけではなく、ちゃんと英語のルールに沿っている。
    具体的には、以下のソースで判別しているので、詳細が気になるなら参考にするとおk
    https://github.com/cakephp/cakephp/blob/master/lib/Cake/Utility/Inflector.php
  • 「じゃあ、テーブル名を'datas'に変えないといけないのかな?」と思ったが、「'data'って実は複数形だったらしいよ」と最近話題になっていたのを思い出し、調べる。
  • 調べた結果、dataの単数形はdatumであることが判明。
    CakePHPでも問題なく利用できるルールのようだ。
    ちなみに、このサイトで、規約に沿ったワードを調べられます。
    http://www.cpa-lab.com/tech2/inflects/
  • よーし、じゃあ、「Datum/data」の組み合わせでやろう!
  • 一覧表示、データ登録、削除はうまく動いたぞ、さて後は更新…あれ?FatalErrorが出るよ?あれ?
    Fatal error: Call to a member function getColumnType() on a non-object in /var/www/php_samples/cakephp/lib/Cake/Model/Model.php on line 1258

■原因調査
エラーが発生したコードはこちら。
https://github.com/cakephp/cakephp/blob/master/lib/Cake/Model/Model.php

getColumnType呼び出し時の引数$columnに渡される値は、多くの場合
「モデル名.カラム名」
となっています。
例えばDatum/dataの場合なら
Datum.id
Datum.title
Datum.body
等となります。

しかし、こちら更新時には、更新対象列の絞込みのため
「テーブル名.カラム名」
という値で呼ばれる事があります。
(おそらく、更新以外にも複雑なことをやろうとした場合、同じ呼ばれ方をします。今回発生はしませんでしたが。)
具体的には、プライマリキーのカラムのみそのような呼ばれ方をするので、
data.id
となります。

さて、問題のコードを見てみましょう。
Datum.id や Datum.title にて呼ばれた場合、問題の1257行目のIF文はFalseとなり、
その先の$colsよりカラムの型を取得し、返します。
しかし data.id にて呼ばれた場合、Modelクラスは$dataというメンバ変数を保持しているため、問題のIF文がTRUEとなり、
$this->data->getColumnType($column);
という呼び出しを行おうとします。
しかし、実際にModelが持っている$dataは配列なので、メソッドを呼び出せずFatal Errorとなるわけです。

■解決策
モデル名をSample, テーブル名をsamplesに変更

■まとめ/教訓

  • フレームワーク利用時に一般的過ぎる単語を使うときは、注意する。
    というか、実際にサイトを作るときに、dataなんて一般的過ぎる単語は誤用が発生する可能性が高いので、NG。
  • 予約語リストがあれば、真っ先に参照するべき。
    CakePHPの場合、僕が探した限りでは見つかりませんでした。
    あったら是非教えてください。
  • 予約語リスト等が見つからない場合、関係するクラスのメンバ変数名はチェックしておいたほうが良い、かも。
    頻度的には、問題が起きたら疑うレベルでも良いけど、一度見ておくとそのフレームワークの命名のルールやよく使われる単語が見えてくるので。
  • Google先生に聞くときは、なるべくノイズが混じらない検索方法を行う。
  • 迷ったら、机上で考える前に一通りソースを舐めろ。デバッガ使え。
    (実は「あれー?おかしいなぁ、あってるはずなのになー。」と、コードと公式ドキュメントを見比べる時間が30分以上あったり…orz)


今回は以上になります。
次回は、予告どおりSymfony2にてサンプルの実装を行ってみます。

2011年12月2日金曜日

CakePHP 2.0 を試してみて。 [Web系一人AC2011]

PHP各種FWとただのPHPの比較シリーズ
開催です。

■前書き
さて、比較と言うからには、条件が必要になります。
シンプルなPHPによるWebサイトってどんなのだろう?と考えた結果、以下のような仕様のサイトとしてみました。
  • MySQLと接続し、テスト用のテーブルに対して以下の基本的な操作を行う。
    • データの一覧(10行でページング有り)
    • データの登録
    • データの更新
    • データの削除
  • レイアウト等の機能外の部分については、基本的にこだわらない。
  • バリデーション等も、基本的に無し。
  • まずはなるべくシンプルに実装する。早くするための細かいテクニック等はナシ。
  • セキュリティについても、最低限のみ考慮する。実際に運用するサイトではないので細かい部分まで調整はしない。
  • フレームワークのバージョンについては、最新の安定板を利用する。1系、2系と分かれているものについては、基本的に2系のみ。
まずは、ただのPHPによって比較用の仕様を満たすサイトを作りました。
比較用なので、
「クラスや関数に処理を分けず、一直線に書く」
という縛りを追加して書きました。

そのソースが、こちらです。
https://github.com/FAL/php_samples/tree/master/normal
なんという、前時代のコード!!!
CGIなんて単語が日常的に使われていた時代を思い出しますね!



実は、昨日の記事はこのノーマル版の解説にしようかな?と考えていたのですが、
自分がこんなイケてないコードしか書けないって言っているような気がして悲しかったのでやめました。
動作中のサンプルは出しませんが、ソースコード的にも単純なので、見ていただければ大体の動作はわかるはずです。

■CakePHP2.0版
ようやく本題。
CakePHP2.0にて、同様の仕様のサイトを実装してみました。
それがこちらです。
https://github.com/FAL/php_samples/tree/master/cakephp
…と、やろうと思ったんですが!
リポジトリに反映漏れがあって、ちょっと足りないファイルが多いので、後々あげなおします。

内容ですが、基本的にblogチュートリアルの内容をベースに、
テーブル名を変えて、
画面構成をちょっと変えてページングを実装して、
defaultレイアウトを簡素にしたりしたくらいです。

■大体の流れ
サンプルコードを読む際に、大体こんな感じで追って行けば、7割程度はすぐにわかるよ、という、大体の流れです。
単純なCakePHPのアプリであれば、同様の流れで追えると思います。
コーディングする側が手を入れないような、コアの部分でのデータの流れについては割愛します。

  1. /app/config/route.php
    mod_rewriteで飛んできたリクエストの、ルーティングのルールが書いてある。
  2. /app/Controller/[コントローラ名]Controller.php
    コントローラ名は、route.phpを参考に。ただし頭は大文字になる。
    最近流行の形式のとおり、"/"はindex()。"/[アクション名]/"は[アクション名]()にマッピングされる。
  3. /app/Model/[モデル名].php
    モデル名は、コントローラ名を単数形にしたもの。
    (コントローラクラス内にて$usesで定義してある場合は、そちらも。)
    今回のサンプルの場合、データ入力時のバリデーションルールがここにあります。
  4. /app/View/Layouts/default.ctp
    全てのページの基本的なレイアウトが記述されている。
    $content_for_layoutに、後述のビューの内容が展開される。
  5. /app/View/[コントローラ名]/[アクション名].ctp
    各アクションに対応したビュー。内容はPHP。
■実装しての感想
最終的な比較まとめは、一通りコードがそろった時点でやります。
まずは、今回CakePHP 2.0で実装していて感じた率直な内容です。
  • CakePHP1.x系に比べて、命名規約が感覚的にわかりやすくなった。
    1.x系はかつて触った事があったのですが、ファイル名の命名規約が結構独特な部分が多く、慣れるまで面倒だった記憶があります。
    2系になってから、命名規約が普通っぽく(Javaっぽく?)なったので、あまり戸惑う事なく決めれるようになりました。
    逆に、1.x系の記憶があって「え!?こんな命名でいいの?」とびっくりしたくらい…。
  • モデル周りが楽!
    規約に沿った名前付けさえしていれば、何も記述しなくてもデータ取得や更新が出来るのは楽です。
    今メインで使っているフレームワーク(職場伝統の俺俺…)は、単純なデータ取得でもいちいちモデルにメソッドを追加しなくてはいけないので、かなりうらやましいです。
  • "data"の罠。
    テーブル名を"data"でやろうとすると、データ更新でFatal errorになります。
    詳細を追った内容を書こうとすると、それだけで1記事になっちゃいますので今は省略します。(後日のネタになりますw)
  • ちょっとアクが取れた?
    Cake1.x系は、どこか「これがCakeのルールだ!」って感じのルールが多かったように感じたのですが、2系は結構素直に理解できる記述が増えたように思います。
    PHP5.2以降対応となった事で、自然な記述が出来るようになったのかもしれません。
    (単純に、私のコーディングスタイルに合っているだけ、のような気もしますが;)
今日はここまで、です。
まずはCakePHPのインプレッションという形で。

明日はSymfony2…の前に、dataの罠についてちょっとだけ書こうと思います。

2011年12月1日木曜日

Web系一人Advent Calendar 2011

12月になりました。
早速、宣言どおり一人ACが始まります。
今日は正直時間が無かった初日なので、基本的な内容の説明にしておきます。

技術系全般、というゆるーい縛りで行こうと思っていましたが、さすがにソレもどうかな?と思ったので、Web系、という縛りを入れてみました。
Web系といってもひろーくなっていますが、私個人のスキル的なものもありまして、PHPの話題が基本です。

PHP>MySQL=Linux>その他言語いろいろ

という頻度を想定しています。

PHPといってもひろーい範囲になりますが、具体的には
「各種FWと生PHPの比較」
というテーマで進めていこうと思います。

各FWごとの特徴をまとめた記事や、HelloWorldくらいのサイトに対するベンチマークの比較という記事は結構あるのですが、
もうちょっと実用的な範囲での、実装コストやパフォーマンスの比較記事があってもいいんじゃないかな?
というのが発想の元です。

当然、いろいろなFWで同じ機能を実装していく事になるのですが、その中で気づいた話題があれば別途あげていきます。
また、ネタ切れという事態が発生してしまった場合は、PHP以外の言語での比較記事もあげるかもしれません。

----------

さて、ここからはちょっと与太話。
上記の準備のために、今日はただのPHPによる簡単なDB操作ページを作ってみました。
クラス構造を考えたり、データ操作を関数として切り分けたりすると、俺俺FWになってしまいそうなので、「手続き型」っぽい書き方になるよう、気をつけて書きました。
(具体的には、既存のクラスは利用するけど、自分でクラスや関数を作らない、という方針です。)

めんどっくさいですね!ほんと。
ソースは後日Githubあたりに公開しますが…。涙が出そうでした。
出来上がったソースも、「これはいったい何年前のCGIだろう?」という。中々に懐かしさを感じる構成に…。

書こうと思えば、かなりOOPなカッコイイ(?)書き方が出来る一方で、
個人的センスで言えばかなーり「イケてない」書き方でも、ちゃんと動いてしまうあたりが、PHPという言語のカオスさと自由さを表しているように感じました。
だからこそ、人に見られ、参考にされても恥ずかしくない、誇れるようなきれいなソースを書いていく必要がありますね。
面倒だからといって、うっかりイケてないコードを書いて、ソレを後輩が真似して、スパゲッティ、なんて事件の原因にはなりたくないですし。
余裕があれば、OOPの利点を語るための比較なんてのも、やってみよう。

2011年11月29日火曜日

Linux Mint 12 ON VirtualBox

最近流行ってるので、Linux Mint 12をVirtualBox上に入れていろいろ触ってみたので、メモ。
カスタム版Ubuntuと思って作業すると、結構何とかなるので、そんな感じで。

■インストール
 最初に「日本語」を選ぶ。後は特に詰まるところは無し。
 ただし、パッケージのDLが重たい。時間がかかるのでのんびり待つ。

■VirtualBox上の設定
 グラフィックメモリはしっかり割り当てておく。一応、3DアクセラレーションもONにしておく。
 多分この辺しっかりしてないと、デスクトップがMATEで立ち上がらないかも。
 VirtualBox用のGuest Additionのインストールは、Ubuntuと一緒でおk

■日本語入力ができない
 デフォルトでInput Methodは入っていないので、
 ibus, anthy, ibus-anthyを入れる。
 変換候補が変な位置に出てくる問題が発生。
 →ibus-gtk3を入れると直った。

■MATEの「メニューの編集」が反応しない。
 alacarteを入れたら出来るようになる

■各種ディレクトリ名を日本語→英語に
 Ubuntuと一緒だけど、一応。以下のコマンドをたたく
 LANG=C; xdg-user-dirs-gtk-update

後は、vimの設定とか諸々をUbuntuから引っ張ってきて、開発に必要なものをインストールして、終わり。

Google+にあげる用に、いくつかSSをとったので、あげてみる。

1.インストール中の画面

2.MATEメニュー
3.壁紙。見事に緑色。G+でもツッコミがあったけど、ミントが無いよね。
4.ターミナルの色。まぶしっ。



2011年11月28日月曜日

メモと俺俺Advent Calendar(仮)について。

実は転職が決定いたしまして。
年明けから、心機一転新しい会社にて働くこととなりました。
あ、転職といってもやっぱりエンジニアですよ?

で、転職のための諸々とか、今の業務がようやくエンジンかかって来たとか、なんやかんやありまして、blogがかなーりおざなりになっておりました。
書きかけで下書き状態の記事はたくさんあるんですが、どれも途中で止まってたりとか、そんなのばっかりです。
後、年明けからは更に忙しくなると思われるので、今のうちに勉強もたくさんしておきたい。

というわけで、今後の予定について、軽く自分用のまとめとしてメモです。

■いい加減しっかり勉強しなおしたい物。
  • Hadoop
  • Cassandra
  • Node.js
  • MongoDB
  • CakePHP その他PHPフレームワーク
  • インフラ系色々
  • Androidアプリ、OpenGL ES、Unity3D
  • Python
  • Sphinx
  • vim
  • Fluent
  • アルゴリズム基礎やり直し
■完成させたいもの
  • ちっこい画像処理Webサービス
  • PythonでJPEGデコード
  • コードネーム:ちゃがしー
絶対に、年明けまでに終わらない量じゃないですかー!俺のばかー!
ちゃんと会社に行ってないと、自分がダメ人間になる予感しかしなかったので、有給は使わずに28日までしっかりと働くことになってるんですよね。
まぁ、仮に12月1日から1ヶ月仕事が無かったとしても、間違いなく終わる量ではありませんが…。

しかし、リストアップした以上、潰していくことが可能になったと言う事なので、地道に潰していきたいと思います。

はい、そして!
「俺俺Advent Calendar(仮)」についてですが。
せっかくリストアップしたことだし、gihyoDPさんでなにやらこんな企画
があるようなので、
一人Advent Calendarでもやってみようかなと思います。
(あ、完全に個人のAdvent CalendarだとNGだったらどうしよう…。)
内容は技術系のものならなんでも、とりあえず1日1個記事を公開する、ということで。
途中で逃げ出さないための意思表示をしておきます。

さて、12月から本気だすぞー!

2011年10月28日金曜日

WubiでインストールしたUbuntu環境のディスク容量を増やす

自宅の開発環境に入れていたUbuntu11.04を、11.10にアップグレードしようとしたら
「容量足りて無いから出来ないでござる」(要約)
って怒られたので、対処法。

タイトルにも書いたとおり、開発環境はWubiで入れたUbuntu11.04。
開発でしか使わないだろうしってのと、SSDに換装したノートPCで容量にあまり余裕がなかったので、12GBだか13GBだか位の割り当てにしてました。
実際、自身の成果物程度であれば問題ない容量だったのですが、Androidの開発環境が思いのほか容量をとってまして、気づけば95%程度のディスク使用量に…。
本当は、余裕を見て32GBくらいにしたかったのですが、全体の容量にそこまで余裕がなかったので、16GBにディスク容量を増やすことにしました。

1.LVPMのインストール
 URL:http://lubi.sourceforge.net/lvpm.html
 上記公式配布先から、LVPMをDLする。
 併記されているバージョン情報が少し古いけど、問題ない。
 debパッケージなので、普通にインストール。

2.LVPMを起動、仮想ディスクのリサイズを行う。
 Dashに「lvpm」と入れるなりなんなりして、LVPMを起動。
 メニューで「resize」を選択。
 リサイズ後のディスク容量をMB単位で入力。
 今回は16GBなので、16384と入力し、実行。
 仮想ディスクのリサイズが開始される。
 リサイズといっても、実際には新しく16GB分のサイズの仮想ディスクを作成し、そこに現ディスクの内容をコピーしているだけのようなので、結構時間がかかります。
 ので、私は一度ここで放置して一度寝ました…。

3.仮想ディスクファイルをリネーム。
 再起動し、Windowsを起動します。
 Wubiのインストール先(判らなければ「Ubuntu」でファイル検索すれば出ます。たぶん。)の「disks」フォルダ内に、「new.disk」というファイルが出来ているハズ。
 「root.disk」というのが、リサイズ前のディスクなので、こいつをリネームするなりバックアップとって削除するなりしてから、新しく出来た「new.disk」を「root.disk」にリネームする。

4.Ubuntuを起動してみる。
 再び再起動し、Ubuntuを起動。
 正常に起動し、ディスク容量が増えていれば大成功。

この後、無事11.10にアップグレードできました。

Google+と連携しました。

プロフィールをGoogle+のものと連携させました。

ログインしてもバルーンが出てこなかったので、Bloggerログイン後、直接以下のURLにアクセスして対応。
http://draft.blogger.com/switch-profile.g

G+はかなりアクティブに利用しているので、個人的にはすごく便利です。
結構プライベートな話題も多いので、基本的に限定公開(サークルメンバーのみ)ですが…。
サクられたら取りあえずサクり返して、スパム等々の不要なユーザだったらアンサクする、という運用なので、気になる方は遠慮せずサクっちゃってください。

そのうち、気が向いたら自分のG+の使い方でもまとめてみようと思っています。
とりあえず、連携したよーという報告だけ…。

2011年10月12日水曜日

Access-Control-Allow-Origin

 ひっさびさにjQueryを使ってajaxであれこれやってたら、
 他サイトAPIはたたけても、自作したテスト用のサイトのJSONが取得できないという謎に躓いたので、メモ。

 ドメインが異なっている場合、表題のヘッダ(Access-Control-Allow-Origin)を設定していないと、データを読むことが出来ない。
 特定のサイトからのみ読み込みOKとする場合、サイトのアドレスを、全サイトからアクセスOKとする場合、*を指定する。
 とりあえずPHPだったので、以下の1文を追加して、解決。

header("Access-Control-Allow-Origin:https://**********.jp");

 さて、上記ヘッダが正しく設定されていない場合、JSON出力側のサイト(サーバ)はどうなるのかというと
 これ、ヘッダを見て、JSON取得側が表示を拒否っているだけなので、出力側の処理は正常終了扱いになっています。
 PHP等々が動いていて、サーバ上でデータの処理やメール送信を行っている場合、そこまでは正常に動くわけです。
 ブラウザからURLを直でたたいても、正常出力されちゃうわけです。

 ブラウザのエラー出力をしっかり読めばすぐわかる話なのですが(苦笑)結構解決に時間がかかったので、メモ代わりに載せときます。
 

2011年10月7日金曜日

Cassandra Conference in Tokyo 感想

 昨日行ってきた「Cassandra Conference in Tokyo」について、あまりきれいにまとまっていませんが、感想を上げておきます。
 公式サイト:http://ec-cube.ec-orange.jp/lp/cassandra-conference-in-tokyo/
 しっかり文章にまとめられればと思ったのですが、今週は少々忙しく…。文章の精査をやるくらいだったらさっさと内容だけあげちゃえ!という事になりました。
 読みづらい文章が多々出てくるかと思いますので、先に謝っておきます。ごめんなさい!

 最初は時系列順に、各プレゼンの感想。最後に全体の感想デス。
 後半、内容が薄くなっているのは、諸事情により集中力が切れてしまいあまり内容を把握できなかった所為です…orz。体調は万全に!これ大事。

!注意!
 各講演を聴いて、自分がどう感じたか?を主題に書いています。
 スピーカーの方々が実際に伝えたかったことや、プレゼンの主題とは離れた部分に着目している事も多々あります。
 実際の講演の内容そのものが知りたい方は、別途ほかの方々のまとめや、後日スライドがアップされることがあれば、そちらのスライドを参照して補完してください。
 この記事を書いている人(=私)は、インフラ関係は見習いレベルの、Webサービスのサーバ側をメインに書いているプログラマ、です。

 Cassandra関係のイベント・勉強会に参加するのは、今回が初めてでした。


・基調講演: Cassandra 1.0 and the future of big data
 私が遅刻して最初少し聞けなかった。もったいないorz
 内容としては、Cassandraのこれからについての話、かな?
 近いうちに公開される、1.0の話題も。
 これからは「もっと判りやすく、使いやすいCassandraにしていきたい。」という主旨の発言があったのが印象的。後、ひたすら「リアルタイム」という単語を使ってました。
 それに対して、ほかのスピーカーの方が「そこまでリアルタイムじゃないよね」なんて突っ込みを、後々の講演でしたりすることも…w

・ハイパフォーマンス・スケーラビリティーデータベース Apache Cassandra
 Cassandraの仕組みとか、具体的に運用したときのノード間の通信のイメージとかをわかりやすく紹介。
 特徴的な多次元KVSのデータ構造についても、簡単な例を交えて紹介。
 後、自社で行っているサポートと、コミュニティへの貢献のお話。
 基調講演の次にこのプレゼンが行われたことで、
「Cassandra気になって検討はしてるんだけど、まだ深いところ何も調べてないのよネ」 というレベルの自分でも、以後のプレゼンが判りやすくなりました。

・「Cloudian(クラウディアン)」におけるCassandra
 あー、その、ごめんなさい。
 英語力に自信が無いので、同時通訳を聞きながら見ていたのですが、通訳が…その、ちょっと微妙で。あまり頭に入ってきませんでしたorz
 通訳していただいている以上文句は言えないのですが!えぇ、ほんと、すいません。英語勉強シマス…。
 後日、違う方のまとめを見て、脳内の断片的な情報を補完する、予定。

・スマートフォン×Cassandraによるハイパフォーマンスサービス基盤の構築事例
 GeQuuはげくーじゃなくてじくー。スマホベースのユーザ向け各種ロギングシステムの事、らしい。
 自分が過去にどこ歩いてたかの履歴を地図上でアバターで動かしながら確認するデモがありました。
 コスモルートさんは名古屋の会社なのですが、名古屋ではCassandra等々の経験を持っている企業がほぼ皆無だと。関東方面に偏りすぎなんですかね、やはり。
 GeQuuのシステムとしては、検索関係がどうしても弱いので、最終的に検索用に自作のKVSをかませている、という話もありました。

・Cassandra上でトランザクションを操る”NanaHoshi”とその展開例
 てんとう虫のロゴかわいい!ハァハァハァハァ。
 NanaHoshiの開発にいたるきっかけとして、S-cubismさんが行っているEC CUBEというECサイトに特化したCMSのカスタマイズ版の経験。
 商品数がある一定ラインを超えてくると、MySQLやPostgreSQLでは耐え切れなくなるため、Oracleという選択肢になる。でもOracleはOSSじゃない。お金かかる。
 莫大なデータを扱うことのできるOSSのDBがほしい!→そうだ、Cassandraにトランザクションをつけよう!という流れ。すごく同意できた。
 NanaHoshiについての概要は、前にpyconJP 2011のLTで聞いた内容と似ていたので、二度目だったこともありすんなりと入ってきました。
 また、NanaHoshi自体のコードは、Pythonで書かれている事もありそんなに大きなものではない、との事。
 擬似コードでの、NanaHoshi利用例とかもあって、プログラマである自分にはわかりやすい話でした。

・Webアプリケーションから見たCassandra
 Webmailという、Webアプリケーションのメールシステムを開発・運用してきてのまとめ。
 実際にどれくらいの勢いで、負荷と容量が増えているのか。
 AWSで運用した場合、どれくらい予算が必要なのか。
 運用開始時の規模と、それから1年半運用した結果どれくらいの規模になったのか、等。
 実際の規模感や予算がまとめられているので、とても参考になりました。
 おまけで語られていた、全ノードが落ちたとき、バックアップから読むというのは、目から鱗でしたね。
 後、Cassandra用のORマッパーとかも出てきました。すごく魅力的!
 多言語向けのORマッパーとか出てくると、さらに利用実績が増えたり、するんじゃないかなぁ?

・KVSを活用したログ保管・解析基盤構築の実例
 ログの保管・解析のためのシステムを、HadoopとCassandraを組み合わせて構築した際の、実例と問題点についてでした。
 HadoopにはHBaseがあるのに、なぜCassandraなんだろう?とか。HadoopとCassandraの連携をもう少し詳しく見たかったなー、とか、自分の脳みそでは少々理解できない部分もありました。
 ただ、BigDataの解析の実例・構成が見たい!という人には良い内容だったと思います。

・Cassandra を使った大規模データ保存の事例紹介(Issues and Tips for Big Data on Cassandra)
 楽天さんなので、スライドの内容はすべて英語でした。しゃべる内容は日本語。
 内容的には、とあるシステムをこれまでMySQLでレプリケーション等々でがんばって運用してきたけど、さすがにもうムリだから、Cassandraに移行しようって話。
 Cassandra自体かれていないのと、楽天という大きなモール系ECサイトで扱うような膨大なデータを扱った事例が少ないこともあり、バグを見つけることも何度かあった、との事。
 楽天としてはCassandraのコミュニティに積極的に貢献していくとか。

■全体の感想
 Cassandraはまだまだ若く、伸びしろがたくさんあるのだな、と感じました。まぁ、まだ1.0にもなってないわけだし、当たり前といえば当たり前なんですが。
 でも、基本となる思想やアルゴリズム自体はほぼ完成していて、使いやすさを補助する機能が足りていない。そんな印象です。
 ジョナサンも、もっと使いやすくしていきたいと言ってましたし。
 また、NanaHoshiみたいな、Cassandraを補助するライブラリがもっと充実してくると嬉しいな!と思います。

 これからの進化に伴い、Cassandraがエンジニアにとって身近な存在となってくると、もっともっと小粒のプロダクトでも利用されるようになるのではないでしょうか。
「このシステム、ユーザが増えてくるとDB負荷が厳しい設計だけど…でも、そこまでユーザが増えることって滅多になさそうだし、このままでいっか。」
 なんて理由でRDBを使っているようなシステムが、
「このシステム、ユーザが増えるとDB負荷ヤバいよね。実際にそこまでのアクセスがあるかどうか分かんないけど、先の事を見越してCassandraで実装しとこうかな。」
 という流れにシフトするのも、そう遠くないと感じています。