第4回開発コンテスト24に参加

ブログを書くまでが開発コンテストらしいので(ぇ?)、とりあえず書いておこうと思う。

クックパッドさんが主催する開発コンテスト24に参加した。

今回は自分は結婚式の準備とかで忙しいので参加するのは辞めようかなと思っていたのだけれど、チームで参加するのであれば手伝える範囲で頑張ろうと思い、前日になって@ore_public@mako_wis(通称:ハーレム)のチームに入れてもらった。

全員、岡山Ruby,Ruby on Rails勉強会のメンバーなので、サーバ側はRailsで、クライアント側はハーレムがiPhoneアプリを作るという構成だったので、自分はサーバ側の手伝えるところをやるか、Androidですぐに作れそうだったらやろうかな〜という程度に考えてた。

当日を迎えると、テーマが「自分が年をとったときに使うサービス」だったので、チャットワークでブレストを行った。ちなみに今回はこの3人、開発期間中はリアルでは会っていません。完全にチャットワークだけでやり取りしてました。

とりあえずテーマ発表が9時だったので、アイデアだしは10時半までとした。3人で色々意見をチャットワーク上に書き、リアルタイムで自分がMindNode(マインドマップを書くためのMac用ソフト)に反映していき、10時半を迎えた時点でpdf化してチャットワークで共有。そこから、絞っていった。
当初は定年後のことを考えたりしていたのだけれど、「そもそも年をとったときってどれくらいよ?サービスを使える年齢って50代から60代くらいじゃないのか?それ以上になるとなにがあるかわからんしなー」という話になった。
最終的に、文字を打つ必要がない、基本的に定型文だけのキーワードチャットと、キーワード思い出記録の2案が残った。

ぱっと考えたときに、キーワード思い出記録ってフォトストレージサービスとの差別化が全然思いつかなかったので、自分的にはキーワードチャットのほうがいいかと考えていたのだが、他の2人の意見がキーワード思い出記録のほうがいいということだったので、そちらをブラッシュアップする方向で考えるようにした。
思い出記録なんだけれど、自分が年をとった時でも使う気になるもの。写真をポイポイ放り込んでおいたら、push型で思い出が通知されるような仕組みがいいなぁということになった。ただ、話がでかくなりそうで24時間では厳しそうだったのでプロトタイプを作ることで合意した。

このあたりでだいたい昼過ぎくらい。それぞれ用事があったので、俺はスマホでチャットワーク見ながら意見を言ってた。

この手の実装は人数が多すぎても誰が何をやるかがはっきりしていないと、短期勝負では難しいので、俺は他の雑務もあったので、アプリのサイトとか作るからよろしくと言って一旦個人的なタスクを潰していた。その間に2人がAPIの仕様とかを決めて、コーディング開始した。この時点で残り12時間くらい。昼間があんまり何もできなかったからしゃーない。

とりあえずチャットワークを眺めながら、自分のToDoが一段落したので、23時くらいからアプリのプロモーション用のサイト作りを開始。
思い出記録という名目の写真を扱うアプリなので、子供の写真をフリー素材のサイトの写真素材 足成から探した。あとは、Twitter Bootstrapでカルーセルを使ったいい感じのテンプレートがあったので、それを採用し、まずはイメージを固める。その後、文言を考える。エモーショナルな表現なら任せろー!とばかりにいろいろとアプリのイメージに沿う文章を考えて、チャットワークに載せて2人に確認してもらった。

いいんじゃね!ということだったのでそのまま作り込み。

途中で@ore_publicが寝落ちしてしまって返事がなかったので、リリースできるのか心配になるところはあったが(herokuへのデプロイは@ore_publicが握ってたから)、途中で起きてくれて助かったw

あとは自分たちのプロフィールを載せるページを作り込んで、アプリの使い方のチュートリアルを載せて、自分が関わる部分は完成とした。
それぞれが得意な分野をやるようにしたので、わりといい配分で作業できたんじゃないかなーと思った。朝の7時くらいに提出して、それぞれ就寝した。

で、プロモーション用のサイトですが、以下になります。

Toruyo : 〜 思い出を貯めよう 〜

写真系のアプリはもう結構あるので、アプリ名を考えるのが一番難しかったかもしれないw
思いつくアプリ名をぐぐると大概既にあって、時間だけがどんどん過ぎていったので、日本語からサービス名を決める発想にして、Toruyoにした。Toruyoという名前はSonyのPS3のテレビチューナーのTorneにすごく似ているのが気になったのだけれど、「撮るよ〜!(パシャ」というだけで思い出記録ができるというお手軽さをイメージするにもいいし、サービス名としてもゴチャゴチャしてなくていいなということで決まった。

今のところはクライアントはiPhoneのみで、正式にリリースする予定はまだないのですが、面白かったので、3人でちゃんと形になるところまでやっていきたいなと思っています。

残念ながら賞をとることはできなかったけれど、とりあえず、楽しかった!
こういう機会を与えてくれたクックパッドさんとTeam Toruyoの仲間に感謝したい。


Twitter Bootstrapのファイルアップロードをカッコよくする!

bootstrapで、ファイルのアップロードのタグを設定したら、デフォルトのボタンとかが出てきて非常にダサいです。いい方法がないかなー?と思って探していたところ、bootstrapをフォークしたと思われるかっこいいライブラリがありました。

それが、Jasny Bootstrapです。

これの、Javascriptのところから、File Uploadを選びましょう。
リンクはこちら

ファイルを選択するためのボタンがbootstrap風になってますね。
Javascript · Jasny Bootstrap

これが、ファイルを選択したら、カッコいい感じになります。変更や削除にも対応していてイカス!
Javascript · Jasny Bootstrap-1

画像のプレビューに対応しているタイプも揃っています。
以下はプレーンなやつ。
Javascript · Jasny Bootstrap-2

これはNo Imageがついているやつ。いいですねぇ!
Javascript · Jasny Bootstrap-3

これはユーザーアイコン用?
Javascript · Jasny Bootstrap-4

導入はこのページのダウンロードボタンから、zipをダウンロードして入れて、決まった形式で記述するだけです。
bootstrapだけでは手の届いていない部分をやってくれていて非常にありがたいですね!!


PHPのImagickでPDFを読み込んでPNGに変えるときの注意点

ようやくそこそこ綺麗に出力できるようになったのでメモ。

Imagickでpdfを読み込む前に、解像度を設定しておく必要があります。
その解像度を設定するメソッドは、setresolution($x, $y)です。

$imagick = new Imagick();
$imagick->setresolution(144, 144); // ここで解像度設定!!
$imagick->readimage($pdf_path);
$page_count = $imagick->getimagescene(); // ページ数を取得
for($i = 0; $i <= $page_count; $i++) {
  $imagick->setimageindex($i);
  $imagick->setimageformat('png');
  $imagick->writeimage(png_image_path($i)); // 連番を作る関数を適当に作る
}
$imagick->destroy();

これでphpのImageMagick(Imagick)でpdfを画像に変換しても綺麗に出るようになりました。
解像度を上げすぎると、ファイルサイズがでかくなるのでそこだけ注意が必要です。


MAMPでImageMagickを使ってpdfからpng生成

MAMPでImageMagickを使えるようにするのは結構大変でした。
今回、自分がやりたかったことは、MAMP上で動いているウェブアプリで、pdfファイルからpng画像を作り出すというやつでした。
しかし、phpコマンドやpeclコマンドが、MAMPのものではなく、HomeBrewでインストールしたphpになっていたため、難儀しました。

参考にさせてもらったサイトは以下の通り。

とりあえず、まずHomeBrewで入れているphpを削除します。

brew uninstall php54

次に、MAMPで使うphpにパスを通します。~/.bash_profileを修正します。

cd
vi .bash_profile

以下を追加します。brewで入れたphpへのパスはコメントアウトします(あれば)。

#PATH="$(brew --prefix josegonzalez/php/php54)/bin:$PATH"
PATH=/Applications/MAMP/bin/php/php5.4.10/bin:/Applications/MAMP/Library/bin:$PATH
export PATH

.bash_profileを再読み込みします。

source .bash_profile

そして、MAMPのダウンロードサイトから、MAMP_components_2.1.2.zipをダウンロード・解凍します。
中にあるphp-5.4.10.tar.gzを解凍し、MAMPのphpのバージョンの規定位置のincludeフォルダの中にphpフォルダとして配置し、configureします。

tar zxvf ~/Download/MAMP_components.2.1.2/php-5.4.10.tar.gz
mkdir /Applications/MAMP/bin/php/php5.4.10/include
mv php-5.4.10 /Applications/MAMP/bin/php/php5.4.10/include/php
cd /Applications/MAMP/bin/php/php5.4.10/include/php
./configure

そして、peclのimagickをインストールします。まだimagemagickが入ってない場合はbrewで入れてください。

brew install imagemagick
pecl install imagick

imagickでエラーになるようだったら、imagick-betaにしてみましょう。

PDFを画像に変換しようと思ったら、GhostScriptが必要でした。入ってない人はbrewで入れましょう。

brew install ghostscript

これで動くかと思いきや、GhostScriptの位置をImageMagickがわかってなくてエラーがでました。
教えてやりましょう。

cd /usr/local/Cellar/imagemagick/6.7.7-6/etc/ImageMagick
cp delegates.xml delegates.xml.org # バックアップ
vi delegates.xml

ImageMagickは処理を他のライブラリに委譲(delegate)していることが結構あります。PDF->画像もその一つです。
gsの位置を教えてやりましょう。command=&quot;gs&quot;という文字列のgsを、/usr/local/bin/gsにすればいいはず…。

<delegate decode="eps" encode="pdf" mode="bi" command="&quot;/usr/local/bin/gs&quot; -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 &quot;-sDEVICE=pdfwrite&quot; &quot;-sOutputFile=%o&quot; &quot;-f%i&quot;"/>
  <delegate decode="eps" encode="ps" mode="bi" command="&quot;/usr/local/bin/gs&quot; -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &quot;-sDEVICE=pswrite&quot; &quot;-sOutputFile=%o&quot; &quot;-f%i&quot;"/>

これでいけるかと思ったけどまだダメだったので、ぐぐったところ、やはりgsの位置がわかってないということだったので、以下をやってみました。
ここから下はコピペしないでください!

[root@din ~]# convert -list configure | grep -i delegates
DELEGATES     bzlib fontconfig freetype jpeg jng png tiff x11 xml zlib
[root@din ~]# ls -l /usr/bin/gs
-rwxr-xr-x 1 root root 6024 Sep 30  2010 /usr/bin/gs
[root@din ~]# ls -l /usr/local/bin/gs
-rwxrwxr-x 1 root root 19758147 May  3 16:12 /usr/local/bin/gs
[root@din ~]# mv /usr/bin/gs /usr/bin/gs.orig
[root@din ~]# ln -s /usr/local/bin/gs /usr/bin
[root@din ~]# 

私の場合は、/usr/bin/gsが存在しなかったので、オリジナルのバックアップはせずに、/usr/local/bin/gsのシンボリックリンクを/usr/bin以下に置きました。

その後、phpでImagickを使ってpdfからpngファイルを作ることに成功。長かった…。
ちなみにそのコードはこちら。

// CodeIgniter使ってます。
class Hoges extends CI_Controller {
  // ... 略
  private function _pdf_to_png($data) {
    $imagick = new Imagick();
    $delete_files_if_exception = array($data['full_path']); // アップロードされたpdfのパス
    try {
      $imagick->readimage($data['full_path']); // pdfを読み込み
      for($i = 0; $i <= $imagick->getimagescene(); $i++) {
        $imagick->setimageindex($i); // ページを指定
        // 出力予定の画像のパスを定義
        $image_file_path = "{$data['file_path']}/{$data['raw_name']}_{$i}.png";
        // ページを画像として出力
        $imagick->writeimage($image_file_path);
        // 出力予定の画像のパスを削除用配列に入れとく
        array_push($delete_files_if_exception, $image_file_path);
      }
      // 処理が終わったので後処理
      $imagick->destroy();
      return true;
    } catch (Exception $e) {
      // 例外が発生したので後処理
      $imagick->destroy();
      // 例外が起きたので出力した画像を削除する
      foreach($delete_files_if_exception as $file) {
        unlink($file);
      }
      echo $e->getMessage();
      return false;
    }
  }
}

まぁこんな感じです。


CodeIgniterでエラー時にフォームに値を戻す方法

Form Validationを設定して、View側ではset_value(‘name’, ‘default_value’);でいいのかと思っていたらそうではありませんでした。なんなんだー!CodeIgniter!とても面倒だぞ!!

set_value(‘name’, ‘default_value’)メソッドは、Form Validationで検証された要素のみ、反映されます。例えば、5つの要素があったとして、

  1. $this->form_validation->set_rules(‘firstname’, ‘FirstName’, ‘required’)
  2. firstname
  3. lastname
  4. address
  5. tel
  6. fax

の5つがあるとして、firstname, lastnameが必須で、後は別にいいやーと思って、以下のようにしたとします。

function create() {
  if ($this->input->post()) {
    $this->load->library('form_validation');
    $this->form_validation('firstname', 'FirstName', 'required');
    $this->form_validation('lastname', 'LastName', 'required');
    if ($this->form_validation->run() == false) {
      // エラー
    } else {
      // 正常
    }
  }
}

この場合、エラーになって再度フォームを表示しても、set_valueメソッドが有効なのはfirstnameとlastnameだけで、他のフィールドは空欄になってしまいます!
じゃあどう回避するのか?こうでしょ!

function create() {
  if ($this->input->post()) {
    $this->load->library('form_validation');
    $this->form_validation('firstname', 'FirstName', 'required');
    $this->form_validation('lastname', 'LastName', 'required');
    $this->form_validation('address', '', '');
    $this->form_validation('tel', '', '');
    $this->form_validation('fax', '', '');
    if ($this->form_validation->run() == false) {
      // エラー
    } else {
      // 正常
    }
  }
}

だ、ださい…。他にいい方法があるのだろうか?