個人グループで「まるも開発合宿プラン」を利用してみた!

週末に前職の同僚エンジニア3人で、千葉に泊まり込みでワーキングスペースを借りて おのおの好きなものを作る/勉強するという会をやってきました!

今回は千葉県の「金谷」にある、コワーキングスペース「まるも」の開発合宿のプランを利用させてもらいました。

marumo.net

合宿プランというと、会社などで利用することが多そうですが 友達同士のエンジニア3人でも、問題なく利用できました!

さっそく詳しいレポート。 最寄り駅の金谷に到着! 降りるともう、都内とは雰囲気が違います。

f:id:nori0620:20170429152454j:plain

そして、さっそくコワーキングスペースに移動。

駅から3分ほどの距離ですぐに移動できました。 そしてワーキングスペース

入ってみると・・・!入り口はカフェのようなオシャレなスペース!

f:id:nori0620:20170429152724j:plain

そして中は素敵な雰囲気かつ、むちゃくちゃ広いです!

f:id:nori0620:20170429152850j:plain

机だけで10テーブルと20席ぐらいあって、3人だと圧倒的の余裕のある空間づかい! 3人だけでも十分、安いぐらいの値段だったのですが、10人とかだとほんとお手頃な価格ですね。

さらにコーヒー完備!

f:id:nori0620:20170430123339j:plain

水も飲み放題!

今回やったことは各人、バラバラで

  • Chrome Cast」のAPIを使ったツール作り
  • ReactNativeを使ったアプリ開発
  • GoLangの勉強をひたすらする

などなどをそれぞれ自由にすすめました。

お昼は、スペースの向かいの、おしゃれなピザ屋さんでいただきました。 地元でも人気のお店らしく、たくさんの人がいました。

f:id:nori0620:20170429153227j:plain

さらに夜の宿は合宿プランを予約する際に、伝えておけば手配してくれます! 今回は、スペースの近くの「かぢや旅館」という場所を手配していただきました。

www.kajiyaryokan.com

旅館も温泉付きで、快適に泊まれました!

そして翌朝、近所のガストで朝食を取った後、ひたすら作業開始。

f:id:nori0620:20170430171727j:plain

もくもくとカフェを貸し切ったかのような環境で作業をすすめていきます。

途中で近所の回転🍣屋さんで、海辺の新鮮な🍣を食べたりしながら、さらにひたすら作業を進めます。

f:id:nori0620:20170430171932j:plain

そんな、こんなで時間いっぱい集中して作業をすすめることができました!

まとめ

合宿プランというと、法人などで使うイメージですが、個人グループで使っても、十分快適に利用させていただくことができました。 みなさんも周りのエンジニア同士などでも、気軽に利用してみると良いと思います! ちょっと集中して◯◯を勉強したい!とかなどなどでも、家でやるよりも、みんなで違う環境でやると集中して一気に 進めることができたりするはずです!

moshを導入しようとして凡ミスでハマった

moshを導入しようとして凡ミスしてたのでメモしておきます.

クライアント側に出ていたエラー

http://tukaikta.blog135.fc2.com/blog-entry-210.html

上記のエントリを参考にさせていただきながらサーバ側( sakuraのVPS )とクライアント側( mac )の設定を行いました。 無事設定完了して、クライアント側のmacからmoshを起動しようとすると下記のようなエラーがでて起動できませんでした。

mosh: Nothing received from server on UDP port 60001.

Firewallの確認

サーバ側のFirewallはiptablesに以下のように設定していたのでポートは開いているような気がしていました。

-A RH-Firewall-1-INPUT -m state --state NEW -m udp -p udp --dport 60000:61000 -j ACCEPT

そこで、実際に通信が行われているか確認するためにサーバ側でtcpdumpを実行して確認してみました。

tcpdump host (クライアント側のhostname) and not (SSHのポート) # sshのpacketが邪魔なのでフィルタリングしておく
22:53:19.477223 IP (クライアント側のhostname).60950 > (サーバ側のhostname).60001: UDP, length 71
22:53:19.477271 IP (サーバ側のhostname) > (クライアント側のhostname): ICMP host (サーバ側のhostname) unreachable - admin prohibited, length 107

あぁ。。。と思いiptablesの設定を見直してみると icmp-host-profibitedの設定がmoshのポート設定の上にきてしまっていました。。
iptablesは上からフィルタが適応されているので、これでは当然動かないですね。凡ミス。

-A RH-Firewall-1-INPUT -j REJECT --reject-with icmp-host-prohibited
....
# 先にicmp-host-prohibitedの設定が適応されてしまているので動かない
-A RH-Firewall-1-INPUT -m state --state NEW -m udp -p udp --dport 60000:61000 -j ACCEPT

以下のように順番を逆にすると無事動いた。

-A RH-Firewall-1-INPUT -m state --state NEW -m udp -p udp --dport 60000:61000 -j ACCEPT
...
-A RH-Firewall-1-INPUT -j REJECT --reject-with icmp-host-prohibited

凡ミスでタイムロスしないように気をつけます。
それにしてもmosh、快適ですね・・・!
回線がe-mobileしかない貧民なので、感動しました。

■hoge

コメント欄でJSが利用できますので、どうぞご利用ください^^

 # 酷いコメントが多いのでやめました^^

cakePHP内でPEAR Services_Amazonのキャッシュ機能を使おうとするとエラー

気がついたら1年ぶり以上のCake関係の記事。
さて、最近またCakePHPを使ってWEBアプリを組んでいるので久々に更新します。


最近、Product Advertising APIと名前が変わったAmazonのAPI
これを使うのに便利なライブラリとしてPEAR Services_Amazonがあります。
このライブラリを使用すると、通信結果結果を配列で取得、Amazonから取得したデータのキャッシュの実装などなどが簡単に行えます。


が、CakePHP内部で、このライブラリのキャッシュ関係の部分が動かずハマってしまったので、かつ同じようにハマる人がいそうなのでメモしておきます。

発生したエラーについて

CakePHPの、あるmodelの中で、次のようなコードを書きました

<?php
  require_once("Services/Amazon.php");
  $amazon = new Services_Amazon($amazon_access_key, $amazon_secret_key, $amazon_associate_key);
  $amazon->setLocale("jp");
  $amazon->setCache('file', array('cache_dir' => '/path/to/cache/dir/'));
?>

すると以下のようなエラーが発生
Fatal error: Call to undefined method Cache::get() in /usr/share/php/Services/Amazon.php on line 1361

エラーが起こっているServices/Amazon.phpの処理を見て、原因特定

1361行目のクラスの_sendRequestメソッド内の_cacheというプロパティからgetメソッドを実行しようとしているところでエラーが起こっています。

<?php
$cache = $this->_cache->get($cache_id);
?>
・$this->_cacheの初期化

ということで、この_cacheプロパティに、どのようなインスタンスが設定されるのかをチェック。
397行目から、始まるsetCacheメソッド内で初期化されています。

<?php
 397     function setCache($container = 'file', $container_options = array())
 398     {
 399         if(!class_exists('Cache')){
 400             @include_once 'Cache.php';
 401         }
 402 
 403         @$cache = new Cache($container, $container_options);
 404 
 405         if (is_object($cache)) {
 406             $this->_cache = $cache;                                                                                     
 407         } else {
 408             $this->_cache = null;
 409             return PEAR::raiseError('Cache init failed');
 410         }
 411 
 412         return true;
 413     }

エラー処理も、きちんとしてるし問題なさそうです。
今回、発生したエラーはCache::getというメソッドが存在していねぇよっというエラーなので、$cacheのメソッド一覧を確認してみましょう。
406行目の下に次の行を追加してみます

<?php
 407 print_r( get_class_methods($this->_cache) );
>?

以下が結果です。

<?php
Array
(
    [0] => getInstance
    [1] => __loadEngine
    [2] => config
    [3] => engine
    [4] => set
    [5] => gc
    [6] => write
    [7] => read
   -----
    [20] => cakeError
    [21] => _persist
    [22] => _savePersistent
    [23] => __openPersistent
)
>?

あ、あれ、このメソッドどこかで見たことあるぞ。。。っていうか配列の20個目,cakeとかいう文字が。。。PEARのライブラリなのに。。。
つまり、CakePHP内でもCacheという名前のクラスがあって、それがPEARのCacheクラスと競合していました。
Services/Amazon.phpPEAR Cacheを使用したいのに、CakePHPのCacheクラスのインスタンスが生成されてしまっています。

解決方法(暫定

ということで解決方法です。
今回とった方法は、あんまりよくないかもしれないけど、PEARのライブラリを書き換えてしまいます。
1.Cache.phpをコピーしてCache_PEAR.phpを作成
まず、クラス名が被っているのがまずいのでPEAR::Cacheのクラス名を変更します。
直でCache.phpを書き換えてもいいのだけど、一応コピーをとって、そっちを書き換える方針で。
2.Cache_PEAR.phpの次の2箇所(クラス名とコンストラクタ部分)を変更

  • 66行目
    • 変更前:class Cache extends PEAR
    • 変更後:class Cache_PEAR extends PEAR
  • 129行目
    • 変更前:function Cache_PEAR($container, $container_options = '')
    • 変更後:function Cache_PEAR($container, $container_options = '')

3.Services/Amazon.phpでCacheインスタンスの代入部分を変更

  • 変更前
<?php
 399         if(!class_exists('Cache')){
 400             @include_once 'Cache.php';
 401         }
 403         @$cache = new Cache($container, $container_options);
>?
  • 変更後
<?php
 399         if(!class_exists('Cache_PEAR')){
 400             @include_once 'Cache_PEAR.php';
 401         }
 403         @$cache = new Cache_PEAR($container, $container_options);
>?

結果

無事、Services_Amazonのキャッシュ部分が動きました。

cakePHPのfindAllで柔軟なJOINを行う

やりたいこと

あるコントローラでfindを実行する際に,一箇所でのみ特定のテーブルとJOINをしたり,状況によってJOIN設定を変更する方法について考える.


もっとも、そこまで特殊で数箇所でしか使用しないリレーションの場合,直接SQLを書いてしまったほうがラクだったりもする。
今回、findをJOINするのは,他のコンポーネントとの兼ね合い。特にPaginationコンポーネントを利用する場合に、直接SQLを書くとコンポーネントを利用したページング設定に手間がかかりそうだから。


$model->beforeFindを設定することで、そのmodelに関するfindのSQLを発行するときのJOINの設定などを行うことができるが、これはmodel自体の設定のため、findを使用している箇所全てに適用される。
(詳細は→CakePHP findAll で INNER JOIN する方法 | Sun Limited Mt. )
ということで今回、もう少し柔軟にJOINを発行する方法について考えてみる。

環境

php5 + cakephp 1.1.17.5612

とりあえずfindAllのコードを見てみる

cakephpの./cake/libs/model/model_php5.phpないのfindAllの部分を読んでみると..

  • $queryDataにSQLに関する情報が入っている。
  • $queryDataの'joins'にJOINの情報を入れてやればやればよさそう!
    • cakePHP自体のコード変更に抵抗がない場合はfindAllに引数$joinを追加してやるのが一番てっとり早そう
    • cakePHP自体のコード変更ができない場合は$model->beforeFindを使うしかない
    • 2つの場合、それぞれについてfind毎にJOINする方法を考える

cakePHP自体のコード変更する場合

  • ./cake/libs/model/model_php5.phpを以下のように変更
<?php
   //最終引数に$joinsを追加
   function findAll($conditions = null, $fields = null, $order = null, $limit = null, $page = 1, $recursive = null,$joins = null) {

		//--------略-----------------

        $queryData = array('conditions' => $conditions,
                            'fields'    => $fields,
                            'joins'     => $joins, //変更部分
                            'limit'     => $limit,
                            'offset'    => $offset,
                            'order'     => $order
        );


		//--------略-----------------

        return $return;
    }
?>
  • コントローラー内での使い方は以下のようになる
<?php
    $joins[] = array(
        "type" => "INNER",
        "alias" => "",
        "table" => INNER JOIN するテーブル,
        "conditions" => INNER JOIN の ONに指定する条件,
    );

    $findAll($conditions , $fields, $order, $limit, $page, $recursive,$joins);
?>

beforeFindを用いる場合(cakePHP本体のコード変更しない場合)

  • cakePHP本体のコードが変更できない場合は$model->beforeFindを定義することになる
  • しかしbeforeFindはモデル自体に適応されるため、コントローラーすべてのfindに適用される
  • find毎に設定を変更するためにプロパティを追加する方法を考えてみる
./app/model/モデル名.phpを編集
  • プロパティにjoinSettings、メソッドにbeforeFindを以下のように追加
<?php
    var $joinSettings = array();

    function beforeFind(&$queryData){
            $queryData["joins"] = $this->joinSettings;
            return true;
        }
コントローラー側での使い方
  • JOINが必要ないときは通常にfindを使用
  • JOINが必要なときはfindの前に以下のように記述
<?php
        $this->モデル名->joinSettings[] = array(
                'type' => 'LEFT',
                'alias' => '',
                'table' => 'テーブル名',
                'conditions' => 条件,
            );

?>
  • これで特定の箇所だけ特定のJOINを実行できるはず

英語

なんとなくこれから英語を趣味的に勉強してこーかなーと思ってます。
なにかをチマチマ、コツコツとなんかをやるのがわりと好きなんです。


ということでブログ左に「はてなグラフの」僕の英語学習時間のグラフを今日から表示しました。
累計270時間になってるのは、実は去年に春〜秋ぐらいまでは勉強してたからです。


よかった参考書なんかがあればここで紹介しますね。