打倒COOKPAD!!Rails4とNokogiriでレシピまとめサイトを作ってみた

こんにちは。ichikoichです。
初めてWebサービスを作り、嬉しくなったのでブログを書いてみました。

スペック

  • Webサービス開発経験なし。DBなんていじったことない。
  • ネイティブアプリ開発は多少あり。C#が一番好き。

作ったもの

COOKPAD, 楽天レシピのレシピをつくレポ数順に並べたまとめサイトを作りました。
「レシピの新聞」というコンセプトで、「Recipe + Paper = ReciPaper」です。

http://recipaper.com

f:id:ichikoich:20140501224719p:plain

個人的に見てておしゃれだなと思ったPinterestのレイアウトを採用しました。

なぜレシピまとめなのか

奥さんが、毎日3〜4時間レシピサイトを手動で横断検索していたからです。
特にCOOKPADの人気ランキングが有料会員のみ見れるということもあって、無料でさらに見やすくみせてやりたい!と思いました。

振り返って思うこと

実際に開発にかけた時間は1週間くらいなのですが、ほんとに色々なことを学べました。
自分が今回作ったものはOSSの寄せ集め」です。OSSすげーです。

UI

レスポンシブに対応するために、Twitter Bootstrapを使いました。自分は本開発で初めてレスポンシブという言葉を知りました。実際にアクセス解析すると、ほとんどがスマホからのアクセスで、レスポンシブは必須だなと思いました。

「Pinterestのレイアウトにしたい」という想いがあり、Wookmark jQuery PluginというCSSを使わせていただきました。
最初は、CSS-Only Pinterest Style Columns Layout | CSSDeckというJavaScript無しでスタイルシートで実現するテクニックを採用したのですが、並び方が左➡右にしかならず制御できなかったので、WookMarkに乗り換えました。


サーバサイド

さくらのVPSを借りて、Ruby on Railsで作りました。これは、2014年のDeveloper's SummitでWantedlyの仲さんの講演を聞いたのがきっかけです。当初一人でRailsをベースにWantedlyをローンチしたと聞いて、自分も一度やってみようと思った次第です。幸い、Railsに関する文献は多く、特に自分でカスタマイズすることもなく組み合わせるだけで、思ったサービスを作ることができました。ほんとに基本中基本しか触っていないという前提です。(先駆者の方恐るべしです)

COOKPADはWeb APIを公開していないため、スクレイピングOSSのNokogiriでレシピ情報をとってきました。

⬇参考サイト
http://morizyun.github.io/blog/ruby-nokogiri-scraping-tutorial/

取ってきたレシピ情報はDBに保存しています。初DB保存でした!

反省点

そもそも、全然人が来ないです(汗)

一日、新規ユーザ5〜10人のペース。。

肝心の奥さんは、このサービスに満足しているので最低限の目標は達成??かと。

やっぱりお金を稼ぎたいですね。

今回は勉強のためということもあり、どうやって儲けるかをあまり考えていなかったのですが、やはり最初にちょっとでも考えておくべきでした。次のサービスでは、「どう稼ぐか、どう集客するか、」を一番はじめに考えたいと思います。

COOKPADは売り上げの約50%が有料会員からもらっているというのは凄っす。

さくらのレンタルサーバを借りてみた

さくらのvpsの一番安いプランを契約してみた。

フレームワークはnode.jsとrailsのどちらかで迷ったけど、
ライブラリも充実しているrailsの方が早く作れると思い、railsをインストール。
スクリプト言語の経験があまりないもんで、言語的ハードルはどちらも同じです。

$rails server

にてCould not find a JavaScript runtime. と怒られたけど、Gemfileに下記を追加すれば無事起動できました。

gem 'therubyracer'
gem 'execjs'

参考:
Rails - Could not find a JavaScript runtime? - Stack Overflow

NS_ENUMとNS_OPTIONS

iOS 6 / Mac OS X 10.8からNS_ENUMとNS_OPTIONSが使えるようになった。
http://nshipster.com/ns_enum-ns_options/

使い分け方をまとめると、

  • ビットマスクを使うとき : NS_OPTIONS
  • それ以外 : NS_ENUM
■OKパターン
typedef NS_OPTIONS(NSUInteger, ESampleStateAnimal) {
    ESampleStateCat  = 1 << 0,
    ESampleStateDog  = 1 << 1,
    ESampleStateBird = 1 << 2,
};

ESampleStateAnimal state = ESampleStateCat | ESampleStateDog;
■NGパターン
typedef NS_ENUM(NSUInteger, ESampleStateAnimal) {
    ESampleStateCat  = 1 << 0,
    ESampleStateDog  = 1 << 1,
    ESampleStateBird = 1 << 2,
};

ESampleStateAnimal state = ESampleStateCat | ESampleStateDog; <= コンパイルエラー

NS_ENUMは、コンパイラの種類によって挙動が変わる。
C++コンパイラだとORでマスクした時、暗黙的にenumにキャストされず、コンパイルエラーになる。

【Objective-C デザインパターン】Prototypeパターン

Javaではよくデザインパターンの例文があるけど、そういえばObjective-Cではあんまりないなと。これを機会に本買って、Objective-Cデザインパターンを勉強してみる。

 

Pro Objective-C Design Patterns for iOS

Pro Objective-C Design Patterns for iOS

 

早速、Prototypeパターンについて振り返り。

[Prototypeパターンとは]

すでに存在するオブジェクトを複製することにより、新しいオブジェクトを生成する。

 

[利用するケース]

クラスからのインスタンス化が難しい場合。

newによる初期化にかなりの負荷がある場合。

 

[具体的には]

NSCopyingプロトコルのcopyWithZoneを実装することにより実現できる。

NSObjectクラスはcopyメソッドを持っているが、NSObjectプロトコルは持っていない。ただし、デフォルトではcopyメソッドは空なので、NSCopyingプロトコルをサポートしてcopyWithZoneを実装しなければいけない。

 

シャロウコピー、ディープコピーどちらにするかはケースバイケース。


ここでは、Productオブジェクトをプロトタイプとして作成。あとは、Productオブジェクトを量産したいときにcopyWithZoneを叩くのみ。

//
// Product.h
//
#import <Foundation/Foundation.h>

@interface Product : NSObject <NSCopying>
{
NSString *productName;
float price;
id delegate;
}
@end

//
// Product.m
//
#import "Product.h"

@implementation Product
- (id)copyWithZone:(NSZone *)zone

{

Product *copy = [[[self class] allocWithZone: zone]

initWithProductName:[self productName]

price:[self price]];

[copy setDelegate:[self delegate]];



return copy;

}

@end


特に、動的に生成が難しいCompositeパターンと併用するケースが有効みたい。またの機会に試したいですね。

BlueStackを使ってみた

Androidエミュレータは重い。。そこで代替手段を探していると、BlueStackというエミュレータがあるらしい。

http://www.bluestacks.com/

これをインストールするとWindows PC上でもAndroidアプリを動かせるとのこと。

早速インストールしてEclipseから自家製Androidアプリのデバッグビルド〜!

 

動かない〜 device not found.

 

どうやらEclipseを起動する前にBlueStackを起動しないと検出できないらしい。

 

calabash-androidの導入

Androidの受け入れテストのフレームワークを調べてみるとどうやらBDDでできるものがあるとのこと。

https://github.com/calabash/calabash-android

メリットとしては、

  • 受け入れ基準を流行りのCucumber形式で書ける。
  • 実機でもテスト可能。Jenkinsとの連携も可能。
  • Javaで簡単にデフォルトにないステップを作成できる。
  • 頻繁にpull requestが出されている。(2013/3/28現在)
  • iOS版もあるので、マルチプラットフォーム開発にも使えそう。

デメリットは、

  • リリースビルドされたapkファイルはテストできない。

インストール方法は簡単。以下のコマンドを実行すればインストールされる。

$sudo gem install calabash-android

ただし、以下の準備が必要なので要注意。

  • ANDROID_HOMEを設定していること
  • JAVA_HOMEを設定していること
  • rubyがインストール済み。今回は1.9.3で試した。
$calabash-android version

でcalabashのバージョンが表示されるとインストール完了。

テスト実行も簡単!

Androidプロジェクトのホームディレクトリ(AndroidManifest.xmlがあるディレクトリ)で

$calabash-android gen

と実行するとfeaturesフォルダが自動で作成される。featuresフォルダは以下の構成。

features
|_support
| |_app_installation_hooks.rb
| |_app_life_cycle_hooks.rb
| |_env.rb
|_step_definitions
| |_calabash_steps.rb
|_my_first.feature

 あとは自分のテストしたいシナリオをmy_first.featureに書いた後、

$calabash-android run <apk>

すればテストが実行される。この時にAndroidエミュレータが起動していること、もしくは実機が接続されていなければならない。また、デバッグビルドされたapkファイルしかテストできない。

# 本当なら受け入れテストはリリースビルドされたものに対して実施したいんだが、calabash-androidはどうやらAndroidユニットテストフレームワークを裏で使っているようなのでそこは我慢。。

 

<apk>はテスト対象のapkファイルの相対パスとなる。例えばHoge.apkがbinフォルダにある場合、bin/Hoge.apkとなる。

ちなみに、htmlファイルにテスト結果を出力したい場合はオプションを指定する。

$calabash-android run <apk> --format html --out reports.html

また、Cucumberのオプションもそのまま使用できるみたい。
例えば、タグ指定して特定のシナリオだけ実行したい場合は、

$calabash-android run <apk> --tags @hoge