Author Archive

    はてなブックマーク - CakePHPでのSQLのログ
    このエントリーをはてなブックマークに追加

    Fusicの中野です。

    表題のとおり、CakePHPのログに関する小ネタを紹介します。
    ちなみに、使っているCakePHPのバージョンは1.3です。(CakePHP2.xなんて知りません)

    CakePHPでConfigure::read(“debug”) > 1の場合は画面にSQLのログが表示されます。
    ですが、運用中ではPHPのエラー表示などがされないように、Configure::read(“debug”) == 0の状態ではないかと思います。
    そうなると、すこしだけSQLの確認をしたいときなどちょっと面倒ですよね。

    CakePHPはConfigure::read(“debug”) > 1の場合、SQLのログはDboSourceの_queriesLogというメンバの配列に格納されていて、どうやらその配列の中身を画面に出力しているようです。

    そこで、こいつの中身が取れれば解決できるので・・・

    $db =& ConnectionManager::getDataSource($Model->useDbConfig);
    $db->fullDebug = true; // ここがfalseだとログを保存しない。$db->fullDebug = Configure::read() > 1みたいになってます。
    $db->_queriesLogMax = 200; // SQLがこの件数こえると配列に保存しないよ的なもの。

    みたいなことをfindを実行する前に書いてあげれば、_queriesLogに登録されるようです。

    だけど、これだと少々めんどいのでBehaviorにまとめてみましょう。

     
    class QueryLogBehavior extends ModelBehavior {
      var $__settings = array();
      var $_queriesLogMax = 999;
     
      function setup( &$Model, $settings = array() ){
       $this->__settings[ $Model->alias ] = array();
     }
     
      function make_query_key( $Model, $query ){ return get_class( $Model ) . "__" .  md5( serialize( $query ) ); }
      function add_query( $key ){
        return "'" . $key . "' = '" . $key . "'";
      }
     
      function beforeFind( &$Model, $query ){
        if( array_key_exists( "log", $query ) and $query["log"] ){
          $db =& ConnectionManager::getDataSource($Model->useDbConfig);
          $db->fullDebug = true;
          $db->_queriesLogMax = $this->_queriesLogMax;
          $key = $this->make_query_key( $Model, $query ); // いらないかも。
          if( array_key_exists( "conditions", $query ) ){
            $query["conditions"][] = $this->add_query( $key );
          }
          else {
            $query["conditions"] = array( $this->add_query( $key ) ); 
          }
          $this->__settings[$Model->alias][] = array( $key, $query["log"] );
        }
        return $query;
      }
     
      function afterFind( &$Model, $results, $primary ){
        $db =& ConnectionManager::getDataSource($Model->useDbConfig);
        $db->fullDebug = Configure::read() > 1;
        $db->_queriesLogMax = 200;
     
        $hit_sql = null; $level = LOG_DEBUG;
        foreach( $this->__settings[$Model->alias] as $keypair ){
          list( $key, $level ) = $keypair;
          foreach( $db->_queriesLog as $index => $query_log ){
            if( preg_match( "/^(.*)" . $this->add_query( $key ) . "(.*)$/", $query_log["query"], $regexp )) {
              $hit_sql = $regexp[1] . "" . $regexp[2];
              break;
            }
          }
        }
        if( !is_null( $hit_sql ) ) $this->log( $hit_sql, $level );
        return true;
      }
    }

    なんかこんな風に適当にBehaviorを追加して

    $this->Hoge->find( "all", array( "contain" => array(), "conditions" => <なんかの条件>, "log" => LOG_INFO ) );

    みたいにしてあげたら、app/tmp/log/debug.logにSQLが記述されます。これで任意のfindの処理のSQLをログにだしたりできます。
    データベースの設定でSQLのログだしとけよとかいうツッコミはなしで。

    { 2010.12.15 }

    無限ループのRSpec

      はてなブックマーク - 無限ループのRSpec
      このエントリーをはてなブックマークに追加

      はじめましての人ははじめまして。
      たぶん、Fusic社員の中野です。

      Advent Calendarに当たってない社員はいるのに、もう2回も当たってしまいました。ルーレットバグってんじゃね?
      さて、今回は実際に活用したRSpecのTipsについて書いていこうかと思います。

      RSpecはRubyのテスト用フレームワークです。(RSpecを知らない人はごめんなさい。詳しい説明は省きます。)
      例えば、こんな感じでテストを書いていきます。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      
      class Hoge
        def run
          puts foo
        end
        def foo; "foo" end
      end
       
      describe Hoge do
        it "#run" do
          hoge = Hoge.new
          hoge.should_receive(:foo).and_return("foo") # 一度だけHoge#fooがコールされるよ                                                                                                                                                           
          hoge.run
        end
      end
      # >> foo
      # >> .
      # >>  
      # >> Finished in 0.00048 seconds
      # >> 1 example, 0 failures

      それでは、Hoge#runの中に無限ループがあるときはどうするのか?

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      
      class Hoge
        def run
          while true do 
            puts foo
          end
        end
        def foo; "foo" end
      end
       
      describe Hoge do
        it "#run" do
          hoge = Hoge.new
          hoge.should_receive(:foo).and_return("foo")
          hoge.run
        end
      end

      もし、これをそのままテストすると、Hoge#runの中の無限ループが実行されて処理が帰ってきません。
      無限ループを終わらせる判定があれば、テストは可能なので・・・。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      
      class Hoge
        def run
          while bar do
            puts foo
          end
        end
        def foo; "foo" end
        def bar; true end
      end
       
      describe Hoge do
        it "#run" do
          hoge = Hoge.new
          hoge.stub!(:bar).and_return(true, true, true, false)
          hoge.should_receive(:foo).exactly(3).times.and_return("foo")
          hoge.run
        end
      end
      # >> foo
      # >> foo
      # >> foo
      # >> .
      # >>  
      # >> Finished in 0.00085 seconds
      # >> 1 example, 0 failures

      という感じにしてあげたら、(擬似的な)無限ループを使ったテストができると思います。

      それでは、次の人にバトンタッチ〜。

        はてなブックマーク - Xcodeを(あまり)使わないでiPhoneアプリの開発
        このエントリーをはてなブックマークに追加

        こんにちは、そしてはじめての人ははじめまして。
        たぶん、Fusic社員の中野です。

        Fusic Advent Calendarということで、何か記事を書かなければいけなくなりました。他の人の記事はなんか難しそうな記事ばかりなので、軽くプレッシャーです。

        さて、iPhoneアプリを作っている人は、おそらく”Xcode”を使っている人がほとんどではないかなと思っています。弊社でも数人iPhoneアプリを作っている技術者がいますが、ほぼXcodeを使用しています。

        XcodeといえばMacについてくる無料の開発環境でとても便利なツールなんですが、「Emacs・Vimで開発したい!」もしくは「Xcodeを立ち上げるのもめんどくさい!」という人もいるかと思います。そこでそんな人たちのために、Xcodeを(あまり)使用せずに行うiPhoneアプリの開発のTipsをご紹介できたらとおもいます。

        検証環境

        • Mac OSX 10.5.8
        • iPhone Simulator SDK 3.0

        ※ OSのバージョンや環境によって違います

        iPhoneアプリ作成

        まず、今回の例として使用するiPhoneアプリの雛形をXcodeで作成します。Xcodeを起動して、「ファイル」->「新規プロジェクト」で「新規プロジェクト」ダイアログが表示されます。そのダイアログで「View-based Application」を選択してプロジェクトをどっかに保存してください。作成すると、以下のようにiPhone用テンプレートが作成されると思います。

        作成したら、メニューの「ビルド」->「ビルド」を選択して、一度、iPhoneアプリを作成します。

        iphonesimを作成

        次に、コマンドラインからiPhoneシミュレータを立ち上げるために、ここからソースコードをダウンロードして、”iphonesim”というツールを作成します。(ダウンロードして解凍すると、Xcodeのプロジェクトが入ってますので、お使いの環境にあわせてビルドをしてください。)

        コマンドラインからiPhoneシミュレータの起動

        ビルドしてできたiphonesimを利用し、はじめに作成したテスト用のiPhoneアプリをiPhoneシミュレータ内で起動させます。

        # iphoneアプリのプロジェクトのパスが"~/Desktop/Fac/Test"だった場合
        $ pwd
        /Users/nakano/Downloads/iphonesim/build/Debug
        $ ./iphonesim launch ~/Desktop/Fac/Test/build/Debug-iphonesimulator/Test.app
        [DEBUG] App Spec: 〜
        [DEBUG] SDK Root: 〜
        [DEBUG] Session started

        何もエラーがなければ、iPhoneシミュレータが立ち上がりiPhoneアプリが表示されると思います。

        いろいろ

        Q: Xcodeしか使ってないじゃん!ソースコードを編集してビルドするときはどうすんのよ?
        A:xcodebuildというコマンドでビルドできます。

        $ xcodebuild -configuration Debug -sdk iphonesimulator3.0
        Q:デバッグは?
        A:iPhoneアプリのPIDが取れますので、gdb –pid=PIDでデバッグできます。iphonesim+gdbを使ってのデバッグはちょっとしたコツが必要です。今回はデバッグ方法については割愛しますが、要望があれば次の回にでも補足できたらなと思います。
        Q:Interface Builderを使いたい!
        A:お察し下さい。

        Emacsをお使いの方は”emacs-xcode.el”やAutoCompleteの”ac-source-company-xcode”など利用すれば、開発自体も便利になると思います。 そして、これらをうまく利用すれば、Xcodeに負けないぐらいのiPhoneアプリの開発ができるelispができそうだなーなんて考えてます。

        それでは、次の担当の人にバトンタッチ〜。