JINS MEME + MQTT + Node.js で、目が疲れて遠くを見つめた時に勝手にMacの画面を消してくれる便利な奴を作る
こんにちは、きょろです。
この記事は JINS MEME Advent Calendar 2015 - Qiita の15日目です。
やったこと
タイトルの通りですが、まとめサイト閲覧中 コーディング中に目が疲れて遠くを見つめた時に、Macの画面を勝手に消して光量を落とし、リラックスを手助けしてくれるプログラムを作ってみました。
といっても、もちろんアプリを本気で作る訳ではなくて、「JINS MEMEのデータをクラウド経由で常に周囲のパソコンやIoTデバイスに送りつけると、どんな楽しいことができるかな?」というコンセプトで、JINS MEMEのセンサーデータをMQTTを使って外部に送り、受信したプログラム側で色々遊ぶという事をためしてみたいと思います。
なんで外部サーバ+MQTT?
直接パソコンにデータを送るのであれば、mito_logさんのアドベントカレンダー8日目「JINSMEME - JINS MEMEのデータをtcp socket通信で出力、Processingに流してグラフ化してみる - Qiita」が良くまとまって書かれているのでぜひご参考ください。
今回、外部サーバを経由してデータをやり取りするのは「日常でいつも自然に身に付ける事ができる」というJINS MEMEのメリットを活かして、できるだけ特殊な設定やネットワーク環境&設備の制約が一切なく、ネットに繋がってさえいれば、アプリを立ち上げるだけすぐに使えるJINS MEMEの応用例を考えてみようと思ったからです。 たとえば
-「スタバでMacBookを開いたら、既にJINS MEMEが繋がって画面制御ができる」 -「家に帰ったら家電がJINS MEMEと自動で連携して動き出す」
みたいなIoTな世界を体験することができます。
一般的にMQTTを使うメリットには、以下の様な事があります。これはJINS MEMEを使った、特にリアルタイムの通信を伴うアプリを作る際にも有用です。
- プロトコルとして軽量
- プッシュ用途で扱いやすい
- IoT用途で組み込み機器で実装する際も負荷が小さい
- ライブラリも整っている
今回のデモでは、JINS MEMEから頭の傾きデータを受けたiPhoneアプリが、MQTTでブローカー(サーバー)にセンサーデータをパブリッシュし、MQTTブローカーを経由して、Mac側でサブスクライブしているNode.jsのアプリにデータを受け渡します。そして受け取ったデータを元に、Node.jsのプログラム側で画面の点灯制御を行います。
MQTTブローカーを用意する
それでは実際に開発に入りましょう。まず、データを中継するMQTTブローカー(サーバ)を用意しましょう。 これはMQTTブローカーであればぶっちゃけ何でも良いです。流行りのAWS IoTを使ってもいいですし、SangoやMilkcocoaでも良いです。 私はMosquittoを使ってUbuntu上に自分でMQTTサーバを立てました(とっても簡単です) 興味がある人は私の過去の記事で詳しくまとめていますので、ぜひ参考にしてみてください。
iPhoneアプリを作る
さて、次はiPhoneアプリを作り、JINS MEMEのセンサーデータをMQTTブローカーに投げつけましょう。 と言っても、1から作るのは面倒くさいので、今回はJINS MEMEのデベロッパーサイトで配布されているリアルタイムデータ取得のサンプル(MEMELib_RealTime)を改造していきます。
MQTTKitの導入
MQTTブローカへの接続には、クライアントライブラリのMQTTKitを使います。 podでサクッと入るので、MEMELib_RealTimeのプロジェクトに追加しましょう。
platform :ios,'8.0' pod 'MQTTKit', :git => 'https://github.com/mobile-web-messaging/MQTTKit.git'
注意点として、pod導入後、Build SettingsのBuild Active Architecture OnlyをYESにしましょう。そうしないと、JINS MEMEのSDKの都合でビルドが通りません。
MMDataViewController.mの改造
あとは簡単な改造です。データ取得の大部分は既存のサンプルを使うので、MQTTブローカへの送信部分だけ追加で実装しましょう。 まずは、MQTTKitのヘッダーを読み込みます。
#import <MQTTKit.h>
次に必要なプロパティを追加
@property MQTTClient *client;
@property BOOL mqttConnected;
@property NSTimeInterval lastSentTimestamp;
viewDidLoadedには、MQTTブローカへの接続ロジックを記述します。
// connect to the MQTT server _mqttConnected = NO; _lastSentTimestamp = 0; NSString *clientID = @"MEME-Client"; _client = [[MQTTClient alloc] initWithClientId:clientID]; [self.client connectToHost:@"your-mqtt-broker.com" completionHandler:^(NSUInteger code) { if (code == ConnectionAccepted) { _mqttConnected = YES; NSLog(@"MQTT Connected"); } }];
あとは、データを送りつけるメソッドを用意します。JINS MEMEのデータ取得レートは結構速いので、さすがにMQTTと言えど、Unixtimeを見て毎秒1回の送信程度に制限します。
トピックは受信側のプログラムと揃えれば何でもいいです。今回は頭のピッチ方向(上下)の傾きしか使わないので、「pitch:
-(void)sendDataToMQTTServer: (MEMERealTimeData*) data { if(!_mqttConnected){ return; } NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970]; if((timeStamp - _lastSentTimestamp) > 1){ _lastSentTimestamp = timeStamp; [self.client publishString:[NSString stringWithFormat:@"pitch:%f",data.pitch] toTopic:@"meme" withQos:AtMostOnce retain:NO completionHandler:^(int mid) { NSLog(@"message has been delivered"); }]; NSLog(@"Send pitch:%f", data.pitch); } }
最後に、memeRealTimeModeDataReceivedの最後に先ほど記述したデータ送信メソッドを呼び出す処理を書き足せば完成です。
[self sendDataToMQTTServer:data];
Node.js側のプログラムを書く
それではMacで動かすNode.js側のプログラムを作りましょう。 の前に、Macの画面の輝度を制御する、その名も「brightness」という簡単なコマンドがあるので、先立ってそれをbrewで導入しましょう。
brew install brightness
また、MQTTライブラリが必要なので、npmでインストールしましょう。
npm install mqtt
コードを記述します。こちらはフルスクラッチで書いたので、JINS MEME SDKと違いライセンスが自由です。 ライセンスはとりあえずMITにしておきますのでご自由にお使いください。極めて短いです。
頭の確度は上を向くほど小さくなりますので、今回はいい感じになった-15°と決め打ちにしています。 状況にあわせて変えてください。
以上で開発は終わります。簡単ですね! 完成したらiPhoneアプリとNodeのプログラムを動かし、JINS MEMEを接続して、上を向いてみてください。画面が消えましたか?!(上を向いたのでモニタが見えないかもですが) こんな感じで、JINS MEMEを使って、iPhoneアプリ意外の物を簡単に連携させることができました。
気付かないうちにJINS MEMEが生活と連携する世界って面白い
まとめです。今回はJINS MEMEとNode.JSをMQTTで繋ぎ、リアルタイムに動作するプログラムを作成しました。 今回は簡単に頭の傾きを使いましたが、もっとJINS MEMEらしく「目を閉じた時」とか「集中力が下がった時」なんかをトリガーにすると、もっと面白いかもしれませんね。 JINS MEMEの何よりの良さは「使っていることを忘れる」レベルに自然に日常使いができるという所だと思います。実際、今回は単なるデモアプリでしたが、このブログ記事を書いたる間にふと遠くを見つめたときにMacの画面が消えて、おおっ!と思いました。「意識しなくていい」というのは、とても大切なUXだなと再認識しました。 JINS MEMEが、それこそ「接続」や「設定」などの意識的な動作が不要に、気づかないうちに空間や世界と連携しているような未来が来ると面白いな、と思いました。
Mosquittoに認証プラグインを導入する
今回はMosquittoに認証プラグインのmosquitto-auth-plugを導入して、MQTT接続時のユーザ認証を試してみます。
mosquitto-auth-plug
https://github.com/jpmens/mosquitto-auth-plug
ビルドに必要な環境を揃える
はじめに、ビルドにに必要なものを入れておく
sudo apt-get install make libcurl4-openssl-dev
適当なワークディレクトリを掘って、mosquitto-auth-pluginを取ってくる
git clone https://github.com/jpmens/mosquitto-auth-plug.git
Makeに必要なので、MosquittoとOpenSSLのソースも持ってくる
Mosquitto
http://mosquitto.org/download/
kyoro@iot:~/git$ wget http://mosquitto.org/files/source/mosquitto-1.4.4.tar.gz --2015-10-21 20:30:24-- http://mosquitto.org/files/source/mosquitto-1.4.4.tar.gz Resolving mosquitto.org (mosquitto.org)... 85.119.83.194, 2001:ba8:1f1:f271::2 Connecting to mosquitto.org (mosquitto.org)|85.119.83.194|:80... connected. HTTP request sent, awaiting response... 200 OK Length: 325077 (317K) [application/octet-stream] Saving to: ‘mosquitto-1.4.4.tar.gz’ 100%[==================================================================>] 325,077 493KB/s in 0.6s 2015-10-21 20:30:25 (493 KB/s) - ‘mosquitto-1.4.4.tar.gz’ saved [325077/325077] kyoro@iot:~/git$ tar zxvf mosquitto-1.4.4.tar.gz kyoro@iot:~/git$ mv -f mosquitto-1.4.4 mosquitto
Open SSL
https://www.openssl.org/source/
kyoro@iot:~/git$ git clone git://git.openssl.org/openssl.git
ビルドの設定
configファイルを作る mosquitto-auth-plugのワークディレクトリに移動して、config.mkをサンプルから作る。
kyoro@iot:~/git/mosquitto-auth-plug$ cp config.mk.in config.mk
configの中身を編集 今回はHTTP-Backendのみ使うので、以下のようにする。
# Select your backends from this list BACKEND_CDB ?= no BACKEND_MYSQL ?= no BACKEND_SQLITE ?= no BACKEND_REDIS ?= no BACKEND_POSTGRES ?= no BACKEND_LDAP ?= no BACKEND_HTTP ?= yes BACKEND_MONGO ?= no # Specify the path to the Mosquitto sources here MOSQUITTO_SRC = /home/kyoro/git/mosquitto # Specify the path the OpenSSL here OPENSSLDIR = /home/kyoro/git/openssl OSSLINC=-I$(OPENSSLDIR)/include OSSLIBS=-L$(OPENSSLDIR)/lib -lcrypto
プラグインのビルド
configの設定が終わったらmake
kyoro@iot:~/git/mosquitto-auth-plug$ make Selected backends: HTTP Using mosquitto source dir: /home/kyoro/git/mosquitto OpenSSL install dir: /home/kyoro/git/openssl If you changed the backend selection, you might need to 'make clean' first cc -I/home/kyoro/git/mosquitto/src/ -I/home/kyoro/git/mosquitto/lib/ -fPIC -Wall -Werror -DBE_HTTP -I/src -DDEBUG=1 -I/home/kyoro/git/openssl/include -c -o auth-plug.o auth-plug.c cc -I/home/kyoro/git/mosquitto/src/ -I/home/kyoro/git/mosquitto/lib/ -fPIC -Wall -Werror -DBE_HTTP -I/src -DDEBUG=1 -I/home/kyoro/git/openssl/include -c -o base64.o base64.c cc -I/home/kyoro/git/mosquitto/src/ -I/home/kyoro/git/mosquitto/lib/ -fPIC -Wall -Werror -DBE_HTTP -I/src -DDEBUG=1 -I/home/kyoro/git/openssl/include -c -o pbkdf2-check.o pbkdf2-check.c cc -I/home/kyoro/git/mosquitto/src/ -I/home/kyoro/git/mosquitto/lib/ -fPIC -Wall -Werror -DBE_HTTP -I/src -DDEBUG=1 -I/home/kyoro/git/openssl/include -c -o log.o log.c cc -I/home/kyoro/git/mosquitto/src/ -I/home/kyoro/git/mosquitto/lib/ -fPIC -Wall -Werror -DBE_HTTP -I/src -DDEBUG=1 -I/home/kyoro/git/openssl/include -c -o envs.o envs.c cc -I/home/kyoro/git/mosquitto/src/ -I/home/kyoro/git/mosquitto/lib/ -fPIC -Wall -Werror -DBE_HTTP -I/src -DDEBUG=1 -I/home/kyoro/git/openssl/include -c -o hash.o hash.c cc -I/home/kyoro/git/mosquitto/src/ -I/home/kyoro/git/mosquitto/lib/ -fPIC -Wall -Werror -DBE_HTTP -I/src -DDEBUG=1 -I/home/kyoro/git/openssl/include -c -o be-psk.o be-psk.c cc -I/home/kyoro/git/mosquitto/src/ -I/home/kyoro/git/mosquitto/lib/ -fPIC -Wall -Werror -DBE_HTTP -I/src -DDEBUG=1 -I/home/kyoro/git/openssl/include -c -o backends.o backends.c cc -I/home/kyoro/git/mosquitto/src/ -I/home/kyoro/git/mosquitto/lib/ -fPIC -Wall -Werror -DBE_HTTP -I/src -DDEBUG=1 -I/home/kyoro/git/openssl/include -c -o cache.o cache.c cc -I/home/kyoro/git/mosquitto/src/ -I/home/kyoro/git/mosquitto/lib/ -fPIC -Wall -Werror -DBE_HTTP -I/src -DDEBUG=1 -I/home/kyoro/git/openssl/include -c -o be-http.o be-http.c cc -I/home/kyoro/git/mosquitto/src/ -I/home/kyoro/git/mosquitto/lib/ -fPIC -Wall -Werror -DBE_HTTP -I/src -DDEBUG=1 -I/home/kyoro/git/openssl/include -L/home/kyoro/git/mosquitto/lib/ -lcares -fPIC -shared -o auth-plug.so auth-plug.o base64.o pbkdf2-check.o log.o envs.o hash.o be-psk.o backends.o cache.o be-http.o -lcurl -L/home/kyoro/git/openssl/lib -lcrypto -lmosquitto /usr/bin/ld: cannot find -lcares /usr/bin/ld: cannot find -lmosquitto collect2: error: ld returned 1 exit status make: *** [auth-plug.so] Error 1
ん?なんか最後でコケた
関係なさそうだったので、見つからなかった-lcaresと-lmosquittoを外してみる。
kyoro@iot:~/git/mosquitto-auth-plug$ cc -I/home/kyoro/git/mosquitto/src/ -I/home/kyoro/git/mosquitto/lib/ -fPIC -Wall -Werror -DBE_HTTP -I/src -DDEBUG=1 -I/home/kyoro/git/openssl/include -L/home/kyoro/git/mosquitto/lib/ -fPIC -shared -o auth-plug.so auth-plug.o base64.o pbkdf2-check.o log.o envs.o hash.o be-psk.o backends.o cache.o be-http.o -lcurl -L/home/kyoro/git/openssl/lib -lcrypto kyoro@iot:~/git/mosquitto-auth-plug$ ls *.so auth-plug.so
わーい、コンパイルできた!
プラグインを設定する
早速使ってみましょう。
できたauth-plug.soを適当な場所に移動して
kyoro@iot:~/git/mosquitto-auth-plug$ mkdir ~/bin kyoro@iot:~/git/mosquitto-auth-plug$ mv auth-plug.so ~/bin/
mosquitto.confを編集してauth-pluginを有効化します
sudo vi /etc/mosquitto/mosquitto.conf
# Place your local configuration in /etc/mosquitto/conf.d/ # # A full description of the configuration file is at # /usr/share/doc/mosquitto/examples/mosquitto.conf.example pid_file /var/run/mosquitto.pid persistence true persistence_location /var/lib/mosquitto/ log_dest file /var/log/mosquitto/mosquitto.log include_dir /etc/mosquitto/conf.d allow_anonymous false auth_plugin /home/kyoro/bin/auth-plug.so auth_opt_backends http auth_opt_http_ip 127.0.0.1 auth_opt_http_port 3000 auth_opt_http_getuser_uri /auth auth_opt_http_superuser_uri /superuser auth_opt_http_aclcheck_uri /acl
設定が終わったらmosquittoを再起動
kyoro@iot:~$ sudo service mosquitto restart mosquitto stop/waiting mosquitto start/running, process 20677
動作確認(と言っても弾かれる確認)
さて、この状態でmosquittoクライアントからトピックを購読できるか試してみましょう
kyoro@iot:~$ mosquitto_sub -d -t test Client mosqsub/23400-iot sending CONNECT Client mosqsub/23400-iot received CONNACK Connection Refused: not authorised.
Anonymousだとバッチリ怒られます!
kyoro@iot:~$ mosquitto_sub -d -t test -u bob -P bob-password Client mosqsub/23401-iot sending CONNECT Client mosqsub/23401-iot received CONNACK Connection Refused: not authorised.
ユーザ名&パスワードを指定してももちろん接続できません。(認証サーバが無いので)
これでMosquittoの認証プラグインが有効になりました。 次回は簡単な認証サーバを書いてMosquittoのアクセス制御を行ってみたいと思います。
UbuntuでMQTTブローカーを構築
AWS IoTが発表されてMQTTがまた盛り上がっていますね! 今日は自前でMQTTブローカーを構築して試してみたので備考録を残しておきます。 MQTTの説明についてはネット上により詳しく丁寧な情報が乗っていますので、ここでは割愛。
Ubuntu 14.04上でMosquittoを使ったMQTTブローカー(サーバ)を構築します。
Mosquitto
パッケージリストにリポジトリを追加
kyoro@iot:~$ sudo add-apt-repository ppa:mosquitto-dev/mosquitto-ppa [sudo] password for kyoro: More info: https://launchpad.net/~mosquitto-dev/+archive/ubuntu/mosquitto-ppa Press [ENTER] to continue or ctrl-c to cancel adding it gpg: keyring `/tmp/tmpmtjlhsxh/secring.gpg' created gpg: keyring `/tmp/tmpmtjlhsxh/pubring.gpg' created gpg: requesting key 262C4500 from hkp server keyserver.ubuntu.com gpg: /tmp/tmpmtjlhsxh/trustdb.gpg: trustdb created gpg: key 262C4500: public key "Launchpad mosquitto" imported gpg: Total number processed: 1 gpg: imported: 1 (RSA: 1) OK
パッケージリストの更新
kyoro@iot:~$ sudo apt-get update
クライアントの導入
sudo apt-get install mosquitto-clients
ブローカー(サーバ)の導入
sudo apt-get install mosquitto
Mosquitto導入完了!
接続テスト
クライアントでtestを購読
kyoro@iot:~$ mosquitto_sub -d -t test Client mosqsub/12779-iot sending CONNECT Client mosqsub/12779-iot received CONNACK Client mosqsub/12779-iot sending SUBSCRIBE (Mid: 1, Topic: test, QoS: 0) Client mosqsub/12779-iot received SUBACK Subscribed (mid: 1): 0
ターミナルを別に開いてメッセージをpublish
kyoro@iot:~$ mosquitto_pub -d -t test -m "Hello world!" Client mosqpub/12852-iot sending CONNECT Client mosqpub/12852-iot received CONNACK Client mosqpub/12852-iot sending PUBLISH (d0, q0, r0, m1, 'test', ... (12 bytes)) Client mosqpub/12852-iot sending DISCONNECT
クライアント側で受信されます
Client mosqsub/12779-iot received PUBLISH (d0, q0, r0, m0, 'test', ... (12 bytes)) Hello world!
めっちゃお手軽ですね!
次回は認証プラグインの導入をやってみようと思います。
※そんなことより前回の記事から1年以上ブログ放置しててビビる…!
株式会社ミクシィを寿退職しました
本記事は「寿退職」エントリーです。
少し前の事で恐縮ですが、本年3月5日(巫女の日!)にサンフランシスコ市庁舎にて、はとねさん(@hatone)と結婚し、晴れてco-founderとして新しい家庭をスタートアップすることになりました。また、それに伴いまして、新卒入社から6年2ヶ月勤めた株式会社ミクシィを卒業して、はとねさんの勤務地であるシリコンバレーに本格的に移住することにしました。
退職エントリーに関しては諸説あるので「別に書かなくても大丈夫??」なんて気軽に思っていたのですが、実際には皆様にご報告しておかないと、実生活で結構色々と大変…というか、せっかくミクシィやお仕事の関係のお話を頂いてもお答えすることができず、申し訳ないと思う機会が日々増えてきましたので、この機会にきちんとご報告させて頂きたいと思います。
在職中は社内外問わず、オンでもオフでも本当に沢山の皆様にお世話になりました。 本当にありがとうございました。
では、良い人生の転換点でもありますので、この機会にミクシィでの生活を振り返ってみようと思います。
2008年のウェブ業界
私は、2008年4月に株式会社ミクシィの新卒採用第1期生として入社しました。
当時は「Web2.0かっこわらい」なんて言葉が出始めた時代。今も昔も言葉そのものは「かっこわらい」以外の何物でもないのですが、全ての人がネットを介して繋がり、サービス同士もどんどん繋がり、お互いが様々な情報をリアルタイムで交換する「単なるデータベース」では無いインターネットが始まりつつある時代でした。
まだFacebookもTwitterもまだ日本語版はありません。(追記:デジガレと組んでTwitter日本語版が始まったのが4月23日なので、これは入社前後の話。誤解を招く表現でごめんなさい) YouTubeはGoogleに買収される前で、スマホと言えば感圧式タッチパネルにWindowsCE、多くの人が家ではTrident、外ではNetFrontでレンダリングされたWebを使っていました。そんな時代に何が出来るか、何をすべきか、まだ私を含め多くの人は答えを出せてはいませんでしたが、来るべき時代の夜明けを感じ、その未来にWebに関わる多くの人が希望を抱き、心を踊らせて色んな挑戦をしていたような気がします。
当時の僕が作ったパワポには「数年後の世界」としてこんな事が書いてありました。
他人との距離は、ネットでもっともっと近くなる。他人の体温をネットで感じる。
ソーシャルなんて形はどうあれ、空気のように当たり前になる
道行く人々全てにIPアドレスが振られ、リアルタイムでコミュニケーションするのが普通になる
スマホが当たり前の今ではどれも当然の事ですが、たった6年前の自分にとっては「作りたい明日」だったのです。 ふと振り返ると、何でもないようなこの業界の日々の変化というのは、実は凄く大きい物なんだなと再認識させられます。 また同時に、自分が夢見ていた時代がちゃんと来た事をすごく嬉しく思っています。
ミクシィでやったこと
ミクシィでは本当に多くの仕事を担当しました。最初の仕事はヘルプシステムの改修、その後にコミュニティやレビューなど、コミュニケーション関係のサービスの開発やリニューアルを多く担当しました。フォトサービスのリニューアルではバックエンドからフロントエンドまで大部分のコードを書き直しました。「ミクシィ(笑)」といつも馬鹿にされるサービスですが、それでも当時のトラフィックは大きく、時に悩み、時に苦しみつつも、本当に楽しくてやり甲斐のある仕事でした。優秀なエンジニアに囲まれて、ビビりながらも多くを学べたのも良い体験でした。
会社員の身分で結構自由に何でもやらせてもらえたの本当に良い経験で、公式ブログで萌えオンラインコーヒーポット「萌香」たんのハードウェアハックを実名で公開したり、「Web2.0中の人ナイト」というライブイベントを色んなベンチャー企業の中の人を集めてやったり、個人で作ったNFCを使った趣味サービス「Taglet」を絡めて会社でサービスを作ってTechCruchに載せてもらったり、記事の執筆でも勉強会でも外部講演でも、いつも社名を背負って好き勝手に色々やらせてもらいました。
4年目には「ちょっとアメリカ行ってくる」みたいな無茶なノリで、ミクシィに籍を置きながら1年間シリコンバレーのスタートアップ「fluxflex」に旅に出させてもらいました。ビジネスの立ち上げやファイナンスなど、スタートアップを日常を経験する中で「0を1にする仕事、新規事業の立ち上げは本当に面白い!」と、心から思うようになりました。また「面白さ」という価値観も、単に目立つ一発ネタではなく、ちゃんとした社会への影響と持続性を持って初めて成し得るもの、というふうに価値観や人生観が変わりました。技術だけでなく、ファイナンスを含めて興味を持って実践し始める良い機会になりました。
帰国してミクシィに復帰してからは、イノベーションセンターの立ち上げから、一貫して新規事業に取り組みました。どうせやるなら、海外のマーケットに対してもアプローチできる事業が面白いと考え、言語の壁を超えやすい「開発者支援」「バックエンド」という事業領域に対して、ミクシィの開発基盤を元にした「DeployGate」というスマホ開発者向けのテストプラットフォームを作って挑戦を挑みました。 DeployGateはおかげさまで93カ国以上で使ってもらえるサービスに成長し、75%以上が海外ユーザという自分の作ったサービスの中では初めてのグローバルなプロダクトになりました。 イノベーションセンターでは他にもPoPollyを初めとした様々なプロトタイプの開発を行いました。世に出たもの、出なかったもの共に沢山ありますが、ビジネスを含めて仮説を立て、次から次へとモノを作って世の中にぶつけて検証して行く作業は、本当に楽しい仕事でした。
在職中、経営の方では色々と変化も多かったミクシィでの6年間でしたが、 今思い返せば、何だかんだで有意義で、心から楽しかった素敵な経験だったと思います。 社内外問わず、良い人達に出会えて、一緒に良い仕事が出来て本当に幸せでした。
これから何するの?
現在は退職手続きも終わり、ベイエリアのSan Brunoに家を借りて妻と一緒に暮らしています。
新婚早々、無職にジョブチェンジという大変問題な状況ではあるのですが、このような状況に理解を示し、次の挑戦に向けて応援してくれている妻に心から感謝を伝えたいと思います。(本当にありがとう!)
これから取り組みたいと考えているのは、Webとスマホでやってきた技術を使って、フィジカルな世界の社会的問題を解決するという事です。 あくまで個人的な想いなのですが、自分としては「バーチャルの世界は結構十分やり切った」という感覚を持っています。 今まで、私達はWebの世界、スマホの世界と技術を持って色々な課題を解決して来ました。 まだまだWebもアプリも使いやすいものへと進化を続けていくと思いますが、それ以上に、この世界には画面から出てこないと解決できない問題があまりにも多すぎます。
また、2008年のWeb業界と同じように、Maker’s MovementやIoT(Internet of Things)/IoE(Internet Everythig)と呼ばれる世界で、確実に変化が起こりつつあると言うのをベイエリアにいると肌で感じるという事があります。実際のところ、現段階では「作ってみた」以上のものではないかもしれませんが、明らかにノウハウがたまって「小慣れて」きており、もはやビジネスや問題解決を実現する1つの手段として十分に戦えるレベルに成熟してきていると感じます。それはまるで、単なるデータベースであったインターネットが、コミュニケーションプラットフォームに姿を変えていく過程を追体験しているようです。 今年1月にラスベガスのIoTハッカソンで優勝して自信を得たというのも大きいのですが、2009年の萌香たん以降、5年近く趣味でやってきたハードウェア分野での興味を、そろそろ仕事として実戦に移してもいい頃なんじゃないかと考えています。
食、物流、商流、決済、安全、などなど、今までWeb&スマホエンジニアが培ったネット技術やUXのノウハウを用いれば、ちょっとしたハックでもっと便利に、もっと素敵になる事は山のようにあります。次はそういった事業領域に、身を投じてみたいなと考えています。 今、少しずつではありますが準備を進めていますので、お知らせできる段階になりましたら改めてご報告させて頂きたいと思います。
最後に
サンフランシスコで入籍しましたが、結婚式&披露宴は日本でやる予定なので、皆様よろしくお願いします!
それでは、何か面白いお話がありましたら、いつでもお気軽にご連絡下さい。 今後とも、どうぞ宜しくお願いいたします。
WebPayLiteを使ってiOSから簡単に決済用トークンを作成する
この投稿はWebPay Advent Calendar 2013の18日目の記事です。
- 17日目: ActiveMerchantをWebPayで使ってみる。
- 19日目: TBD
WebPayLiteを改良してみた
こんにちは!WebPayアドベントカレンダー3日目で「WebPayLiteを使ってiOSから簡単にクレジットカード決済する」という記事を書いたところ、シークレットキーを使ってクライアントサイドで決済させる実装が良くなかったのか、なんか記事が干されてしまった感に地味に落ち込んでいる、きょろです。( ;⌓;)
とはいえ、落ち込んでいても仕方ないので、今回は私の作った「WebPayLite」というObjective-C向けの軽量WebPayクライアント・ライブラリを改良し、クライアントサイドでのトークン化に特化させる実装に変更しましたので紹介しようと思います。
WebPayLiteの使い方
WebPayやWebPayLiteの紹介は前回と変更がありませんので、WebPayLiteを使ってiOSから簡単にクレジットカード決済するをご参考下さい。 それでは、WebPayLiteを使ってカード情報をクライアントアプリサイドでトークン化する方法を紹介します。 WebPayLiteにはサンプルのiPhoneアプリのプロジェクトが付随していますので、そちらを参考にしながら読み進めて下さい。
1. ファイルをプロジェクトに追加します。
WebPayLiteをGithubから入手して、「WebPayLite.h」と「WebPayLite.m」をあなたのプロジェクトに追加します。ツリーのルートに突っ込むだけでOK、ビルドターゲットへの設定を忘れずに!
2. ViewController.hに、インポート宣言とデリゲート宣言、クラス変数の定義を追加します。
トークン化機能を実装するViewControllerのヘッダファイルに、WebPayLiteのインポート宣言、コールバックのデリゲートを受け取るためのデリゲート宣言、WebPayLiteのインスタンスを格納するクラス変数の定義を追加してください。
#import "WebPayLite.h" @interface ViewController : UIViewController <WebPayLiteDelegate>{ WebPayLite *wpl; }
3.初期化処理
ViewController.mの実装ファイル側に、初期化処理を追加します。 大抵の場合、viewDidLoadedとかに追加すれば問題ないと思います。 内容はインスタンスの作成、デリゲートの登録、WebPayの公開可能鍵の登録です。 公開可能鍵のハードコーディングが気になる場合は、キーチェーンなどを利用して適切に保護してください。 鍵を設定するプロパティは、secretKeyからapiKeyに変更になっているのでご注意ください。
//WebPayLite Initialize wpl = [[WebPayLite alloc] init]; wpl.delegate = self; wpl.apiKey = @"YOUR_PUBLIC_KEY";
4.トークン化
ここまですれば後はトークン作成メソッドを呼ぶだけです。createTokenメソッドでトークンの作成ができます。基本的にはWebPayのAPI呼び出し時のパラメータをそのままNSDictionaryで与えればいいだけの薄いラッパなので、WebPayのAPIドキュメントを見ながら自由に呼び出してください。
NSDictionary *params = @{ @"card[number]" : @"4242424242424242", @"card[exp_month]" : @"12", @"card[exp_year]" : @"18" , @"card[cvc]" : @"123", @"card[name]" : @"KYOSUKE INOUE", @"description" : @"Test Tokenize" }; [wpl createToken:params];
5. コールバックの実装
必要に応じてWebPayLiteDelegateの各種コールバックをViewControllerに実装してください。引数として渡されるjsonはNSStringですので、NSJSONSerializationなどを使って必要なキーを読み取って下さい。戻り値なども基本的にはWebPayのAPIドキュメントの通りです。 ここでは、WebPayLiteDelegateCompletedを受け取って、作成したトークンを取得してみます。
- (void)WebPayLiteDelegateCompleted:(NSString *)jsonBody { NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData: [jsonBody dataUsingEncoding:NSUTF8StringEncoding] options: NSJSONReadingAllowFragments error:nil]; NSString *token = [jsonObject objectForKey:@"id"]; UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Succeed!" message:[NSString stringWithFormat:@"Token: %@",token] delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; }
6. トークンをサーバに送信し、サーバーから秘密鍵で課金する。
ここまでくれば後は簡単です。WebPayLiteで作成したトークンをあなたのサーバに送信し、秘密鍵を使って顧客の作成や課金などを行って下さい。WebPayにはRubyやPHP、Pythonなどの各種言語バインディングがありますので、サーバーサイドの課金の実装はとても簡単です。詳しくは他のWebPayアドベントカレンダーをご参考ください。
みんなで楽しくトークン決済!
こんな感じで、皆さん、できるだけトークン決済を行うように心がけましょう。トークン決済はお客さんの決済情報を守るだけでなく、アプリを作るあなた自身の不安を取り除き、楽しく決済機能を実装できるようになります。 WebPayLiteはGithubで、MITライセンスにて公開中です。ご自由にお使い下さい&プルリクエスト歓迎です。
それでは!
Squareが承認されたので爆速で決済してみた
Squareついに日本上陸!のニュースを聞いた直後に申し込んでいた日本のアカウントが今朝方、無事承認されたので、爆速で決済を試してみました!
もちろん、まだリーダは届いていないのですが、アメリカで買ったリーダ(旧版も新版も両方)が問題なく使えたので、それを使いました。
起動画面です。ツアーとか終わるとこういう画面になります。
電卓みたいですが、ここで商品名とか値段を打ち込みます。画像も付けれます。(別に空欄でもいい)
もちろん、+を押して複数の商品を一緒に決済するのもOK。最低決済金額は100円からで、現金で合わせて決済することもできます(クレカ決済分から除外される)
せっかくなので写真も撮ってみましょう!
スクエア形状に商品写真を撮影後にクリップします。
商品の打ち込み終了。写真撮影を除けば、ここまでわずか数秒です!素晴らしい。
ここでリーダーを接続して(接続すると認識される。ちなみに、なぜか楽天スマートペイでも認識される(笑))
カードをスワイプ!!!!!
ただちにオーソリ(承認)作業が開始され
承認完了!サインを記入する画面が出ます。
サインを書いて
決済完了です!早い!!!
レシートはSMSかメールで受け取ることもできますし、省略することもできます。
メールで送られてくるレシートはこんな感じ!
とにかく簡単!これは本当に素晴らしい。
こんな素敵なエクスペリエンスが日本でもいよいよ誰でも手元に持てる時代がくると思うと、ワクワクしますね!!!
ちなみに先週末のMakerFair@シリコンバレー・サンマテオでは、手芸のバザーのほとんどのお店でSquareが使えてクレカで買い物ができました。正直ビビった。
露天の机の右下にSquareのマークが輝いていますね!
日本で使えないアメリカンエクスプレスなども使えます。日本で使えないのは、おそらくJCBが使えないからというのが理由だと思われます。日本のアメックスはJCBのネットワークで決済するのですが、JCBは日本の会社ということもあって、リスクが読めず、まだまだこの領域は様子見なのでしょう。早く使えるようになるといいな!!
日本でもフリマや即売会(ガチのコミケだと人&回線的にトランザクションを捌けなそうだけどw)から、田舎の露天の八百屋で野菜買うときまで、すべてクレカ決済が使える時代が、すぐそこに来ています!
ちなみにクリス・アンダーソンのMAKERSには、Squareがどうして生まれたか、みたいな事が書いてあったりするので、興味がある人は一度読んでみると面白いです。おすすめですよ!
Android APKというapk情報を取得するgemを作った
Android APKというgemを作ってrubygemsで公開したよ!
Androidのマニュフェストファイルを読んで、アプリ名やパッケージ情報を取得できます。
gem作るのは初めてだったのだけど、cpanより断然簡単でビビった。
rake build -> rake releaseとコマンドを打つだけで一発公開。これはやばい。
ということで、興味のある方はよろしければどうぞ。
RubyGems - kyoro/android_apk
https://rubygems.org/gems/android_apk
使い方はシンプルで
require 'android_apk' apk = AndroidApk.analyze("/path/to/apkfile.apk") apk.nil? #This file is invalid apk file. apk.label #Application Name apk.package_name #Package Name apk.icon #Included Icon file in apk file apk.version_code #Version Code apk.version_name "Version Name
こんなかんじ!
【激安】送料込み440円!MacBook用Thunderbolt(Mini Display Port)-HDMI変換ケーブルがおすすめ
「え〜キモ〜イ☆Macの寿命ってOSアップデート1回分までだよね〜。キャハハ」
こんにちは。Mountain Lionが発表され、さすがにそろそろMac Book Air 11インチ(2010年初代モデル)を買い換えたくなってきた、きょろです。
私はあやしいデバイスやサプライ品を買う(そして失敗して後悔)するのが趣味なのですが、今回は珍しく「これは凄いコスパが良くて気に入った!」と思った商品があったので紹介します。
MacBook持ちなら全種類ひと通り持っておきたいディスプレイ変換ケーブル(通称、しっぽ)
Macとモニタを接続するときに使う、ディスプレイ変換ケーブル、しっぽ(尻尾)はみなさんお持ちですか?
あれって地味に種類がVGAとDVIの2種類があるし、勉強会でのプレゼンや職場など、使おうと思った時に持ってくるのを忘れたり、どこかに置き忘れてなくしちゃったり、いつのまにか友人に奪われてたり、持ち運ぶの色々面倒くさいですよね。
大画面だと解像度的にDVI接続が良いし、でもプレゼンのプロジェクタだと大抵VGAだし、そのたびに誰かに借りるのも申し訳ないし。
最近は僕は完全にMacBookだけで生活してるので、
「できれば尻尾を持ち運ぶんじゃなく、 ディスプレイ側に全部に尻尾を装備して、尻尾を持ち歩かない生活がしたいなー」と思っていたわけです。
でも尻尾って意外と高いんですよね。アップル純正品だと3400円もします。
家のデスク、リビングの42インチ液晶テレビ、寝室のプロジェクタ、職場のモニタ。
4つ買ったら13600円。さすがにそれは無い><。
お買い得な互換品を使おう!
以上のような状況から、安い互換品が無いかな〜と探したら、普通にありました。
なんとお値段、送料込み440円!しかも安心のAmazonさんです。
Mini Display Port用と書いてありますが、もちろんThunderboltでも使えますよ!
で、こいつが更にいいのは、HDMI接続という所なんです。
HDMI接続をすると、なんと音声もモニタ側から出力できるんです。つまり、液晶TVなどに繋げば、MacBookから大画面で音声も一緒にアニメ再生できたりします。最近のフルハイビジョンの大型液晶TVは1920x1080の表示にも対応してるので、HDMI接続にすればフルハイビジョンモニタとして普通に使えます。目がチカチカすることもありません。大画面にターミナルを表示して、プログラムを晒しながら、リビングでペアプロとかできちゃいます。すごく便利。これで440円はかなりお得です。
ノーブランドじゃなくて、せめて名前を聞いた事のある日本のメーカーの製品が良い!という人もご安心ください。HDMI変換アダプタの互換品って結構いろんな種類があって、たとえばサプライ品で有名なエレコムの商品だと1800円から、プラネックスだと1320円くらいからあったりします。
変換アダプターだとHDMIケーブルが別に必要で煩わしい…と思う人には、HDMIケーブルと変換アダプターが一緒になったタイプがおすすめです。ケーブルの一方がMiniDisplayPort(Thunderboltでも使えるよ!) もう一方がHDMIになってるので、このケーブル一本でTVとMacをつなぐ事ができちゃいます!便利ですね〜。
ほかにも互換品の尻尾っていろんな種類があって、DVIなら950円、VGAなら1518円で送料込みです。共にプラネックス製です。
おすすめはDVIの950円の方で、こちらはなんとDVI−Iが出力できます。つまり、変換アダプタでDVIにもVGAのアナログディスプレイにも両方出力できるんです。一石二鳥!
(Appleの純正DVI変換ケーブルはDVI-DなのでVGAに変換できません)
安すぎる製品には注意
ここに上げた商品は全て実際に自分で購入して「これはいける!」と感じたものをピックアップしています。プラネックスの製品は、昔の安い方は断線しやすいので、ここで紹介したPL-MDPVG02の方がおすすめです。
他にも探せば安い変換ケーブルは沢山出てくるのですが、特にVGAで安すぎるノーブランドの物は 、突然映らなくなったり、断線したりと大変で、実際に私も何種類か購入したのですが、結局は安物買いの銭失いでした。
生活圏をすべてMini Display Port化!
というわけで、Apple純正尻尾を1個買う値段で、互換品ならHDMI、DVI、VGAと3種類全部買い占めれます!(笑)
自分の生活圏のモニタをすべてMiniDisplayPort化したら、私は日々の生活が幸せになりました。ランチ代より安いMacの尻尾を気軽に買い占めて、あなたも素敵なHackLifeを実現してみませんか?
全世界の友人から届いた最高の誕生日メッセージ
前からず〜〜っと欲しかったオリンパスのPEN mini(E-PM1)を
私の27歳の誕生日に贈って頂きました!!
憧れのコンパクトデジタル一眼!もうそれだけでも幸せ一杯で嬉しさ爆発なのですが、
なんと予想もしないサプライズが!
早速撮った写真をプレビューしていたら
「きょろさん、もうちょっと戻してみてください!」
「???」
ってなりながら、プレビューを戻していくと…
「!!!!!!!!!」
なんと、日本中、いや、アメリカの友人も含めて、
世界中の大好きな友人からのお誕生日のお祝いメッセージが!!
シェアハウスのみんなだけかと思えは、
同僚のらいちゃんや、ジョンや、うかやれまで。
さらに北海道の友人、実家の母さん&ばあちゃん&わんこ、
未踏のくまぎさんや、高専のあやちゃん、恩師の岡田先生から後輩、
Googleの小西さんと、サイバーの、えと、はじめましての方!!!(笑)
アメリカのfluxflexメンバーやルームメイトの友人やしゃんさんまで!!!!
もう感動して泣いてしまいました。
なんてソーシャル、なんて温かい!
こんなに誕生日を祝ってもらったのって本当に初めて。
こんなにも大切にしてもらえて、俺は本当に幸せ者です。
去年の26歳の誕生日は、本当にいろいろあったので、アメリカで一人ぼっちで、
誰にもローカルじゃ祝ってもらえないさみしいものでした。
それが、1年後にこんなにも幸せに包まれるなんて想像もしなかった…!
みなさん本当にありがとう。
そして、お誕生日のお返しまっててくださいね!!!w
今年も1年、全力で頑張っていこうとおもいます!
どうぞよろしくね!!!
全ての個人開発者に読んでほしいスタートアップの教科書「リーン・スタートアップ」の日本語版が出るらしい!
アメリカでfluxflexの開発に参加していたとき、スタートアップをやる上で最も多くの学びを与えてくれた、いわば”スタートアップの教科書”「The Lean Startup」の日本語版が4/16に出るらしい!
今までソフトウェアの開発手法に関しては「アジャイル」だの「プロトタイプモデリング」だの、色々とノウハウやナレッジが生まれてきては話題になっていた。しかし、個人的には「で、結局それらを使ってどうやってビジネスをしたり、サービスを運営すればいいの?」という、事業主として(またはプロジェクトを運営する個人開発者として)どうオペレーションを回していけばいいのか、という点に関しては、結局のところ良い答えや、見習うべきモデルや、テンプレートが全く分からないままだった。そんな自分に、ある1つの解を与えてくれたのが、この「リーン・スタートアップ」だ。
リーン・スタートアップのリーン(lean)とは、英語で「〔人や動物が〕ぜい肉の取れた、体が締まった、痩せた」という意味だ。この本では、極限まで必要最小限にそぎ落としたスタートアップを生み出すには、どういうオペレーションを回していけばいいかというノウハウについてまとめられている。
この本で大切にされているポイントはいくつかあるのだが、個人的に重きを置いているなと思うのは「MVPを生み出す事」「検証可能性があるものはどこまでも検証すること」「イテレーションをとにかく短くし、検証から学び改善することを怠らない事」という事だと思う。
その中から、MVPとは何かということについて今回は話そうと思う。
MVPというのは「Minimum Viable product」の略で、市場ニーズを迅速に検証できる最低要件の小さなプロダクトという意味だ。何か新しいプロダクトを生み出そうとしたとき、そのプロダクトが結局顧客のニーズに合っているかとか、新しい価値を創造できているかというのは、はっきり言って結果論でしかなく、やってみないとわからない事が多い。たとえば数億円の社運をかけたビックプロジェクトをドカンと投下したものの、全く当たらず響かず、大コケするなんてことは、実際にすごく多い事例だ。これが体力のある会社だったら、まぁ高い勉強料ってことになるのだろうけど、小さなスタートアップや個人開発者にとっては1回のチャレンジで人生が詰んでしまう大変な問題だ。
そこで、今自分たちが創造したいと思っている価値の最小単位まで機能を削ぎ落としたプロダクトを作り、それが市場に受け入れられるか実際にリリースしながら検証(Validation)し、鉱脈を掘るまで何度も何度もイテレーションを回して、ピボットを繰り返すことで、成功の確度を高めようと言うものだ。
このとき、MVPというものとプロトタイプやベータ版なんかがごちゃごちゃに日本では訳されて取り扱われている感じがするけど、実際にはMVPはそれらとは別のものだ。
MVPのM(ミニマム)は機能要件を絞ったプログラムという意味のMではなく、ビジネスやサービスとしてのMという意味である。つまり、必要最低限の機能やビジネスモデルが通っている必要があって、たとえば課金モデルが前提のサービスであれば「その最低限の機能に対してユーザがお金を支払うか」という仮説まで含めてMなわけなので、課金の実装もしっかり行い、お金もユーザから取らなければ、ちゃんとしたMVPではないし、仮説の検証は出来ない。
fluxflexで良く話していたのは、MVPとは、大きな製品の1部分から段階的につくる事(プロトタイプ)ではなく、小さな完成された結晶を生み出す作業だということ。
どんなに小さくてもいいので、まずはビジネスとして完結した小さな結晶を生み出す事に全力になる必要がある。そして、そのプロダクトの世界観をしっかり生み出す事ができれば、あとは物量を投下して結晶を徐々に大きくしていけばいいのだと俺は考えている。
長々と語ってしまったが、これだけでは伝えられない様々なスタートアップのノウハウがこの1冊に集約されている。
全ての個人開発者とプロダクト生産者に読んでほしい教科書が、このリーンスタートアップだ。英語版しかなかったときは僕も苦しみながら、ちょっとずつ摘み読みをして、ネイティブレベルの英語力のメンバーに補足してもらいながら理解を深めていた。そんな読み方でも、圧倒的に得られるものが多い1冊だった。ただ、本当に良書なのだが、残念ながら洋書なので、全ての友人に読んで!とお願いするのはさすがに気が引けた。こんなとき、このタイミングで日本語版が出るのはとてもうれしい。旨を張って、すべての人にお勧めしたい。
僕ももう一度、日本語で読み直してみようと思う。
細けぇことはいいんだよ!シリコンバレーのNFC交通事情と開発スタイル
日本であたりまえのSuicaやPasmo,日本がNFC先進国と呼ばれていたのは今は昔。米国でもじわじわとNFCを使うシチュエーションが増えてきた。というか、この1年ですっかり日常に溶け混み始めている。その普及速度はとても速い。
(まだ利用者はそこまで多くないので、コモディティ化とまではまだ言えないが)
代表的なのはClipperCard。これは、日本のPasmoみたいなもので、バス、カルトレイン(電車)、路面電車で共通して使える電子乗車券だ。面白いなぁと思うのが、実装が超簡素で、運用がすっごくレイジーなところ。
たとえば、自動改札なんてものはカルトレインにはない。
カルトレインというのは、カリフォルニアの西海岸をサンフランシスコからサンノゼまで走ってる鉄道で、車を持たない人の移動でもっとも一般的な移動手段の1つだ。写真ではわかりにくいかもしれないけど、これぞアメリカ!って感じで、かなりデカイ。2階建てで、自転車なんかも持って乗ることができ、轟音を響かせながら走る。
で、このカルトレイン、基本的に全部無人駅だ。もちろん、改札なんてものはない。どうやってClipperCardを使うかというと、乗車駅に置かれている端末にタッチ(TAG ONという)して、乗車後に、降車駅に置かれている端末でタッチ(TAG OFF)する。
面白いのは課金方式だ。実はTAG ON時に、想定されうる限り最高額の料金(20ドルくらい)をカードから引き落としてチャージしておき、TAG OFF時に実区間分の料金を残した上で、不要分を返金(Refound)する。デポジットのような感じだ。つまり、TAG OFFを忘れると返金されない。TAG OFFを忘れた方が悪いというスタイル。
そんな適当な仕組みと運用だと、クレームがついたり無賃乗車する人が続出するんじゃない?!なんてエンジニアは思っちゃうわけだけど、そこは運用でカバーしている。ルールがとても厳しいのだ。
たまに車掌さんが検札をして車内を回っているだけど、そのときにTAG ONを忘れて乗っていると、ものすっごく怒られる。何人か怒られている人を見たことがあるんだけど、日本の怒るとかのレベルではない。列車は最寄り駅に停車し、罰金200ドル払うか、警察を呼んだから、降車して逮捕されるかどちらか選べと怒鳴られる(本当に警察来てる)。ものすごく怖い。必ずTAG ONを確認する習慣が着いた。
アメリカの仕組み全般的に言えることなんだけど、基本的に自己責任という運用方式が多いような気がする。鉄道事業者は取るだけ取るし、消費者は返してもらえる分だけ返してもらうという感じ。
一見不親切のように見えるこの仕組みだけど、実はとても、消費者と提供者の責任分界点が明確で合理的だ。アメリカの運用は、ルールを大まかにざっくり、そして、しっかりと決める。で、細かい運用や問題は、問題が発生したときに、レイジーに裁量をもって対応する。
だから、たとえClipperCardの認識速度がタッチして3秒くらいかかる酷い実装でも(Suicaの100倍くらい反応速度が悪い)、自動改札やインフラの整備にお金や時間をかけなくても、ルールの大枠の中でうまく回るし、導入が始まれば、合理的な実装かつ低コストなので、一気に広まる。
良い意味で適当。これがとてもアメリカ的で、リーンなスタイルだなと感じる。 シリコンバレーのアプリやWebサービスの開発にも共通しているなぁって思う。
面白いこと、便利なものを、細かいことを考えずに、とりあえず実用化しちゃうのがアメリカ流。日本のWebやアプリ開発の現場は、こういう文化の人たちと戦っていかなければいけないのだ。
問題や責任は、よっぽど大きなものが想定されない限り、起こった時に考えればいい。こういう考え方が、日本の現場にはもっと必要なのかもしれないね。
最後に、技術的なことを書いておくと、ClipperCardはNFCの種類ではType2(MIFARE)を使ってる。日本のSuicaに使われてるFeliCaよりは価格競争力はありそうね。
あと面白いのが、ClipperCardのWebサイトで、カードのシリアル番号を入れて、そこに自分のクレジットカードをひもづければ、クレカ引き落としのオート・リロード・カードになったりする。とても便利。
乗車履歴や残高確認もオンラインでできちゃいます。
アメリカはオンラインとオフラインをうまく組み合わせてサービスを作るのも進んでるなと思う。「WebでできることはWebでやればいいじゃん!便利だし安いしスケールするし!」というメッセージを随所から感じる。
少なくとも、全PC用にPasoriを買わせるって仕組みよりは、すごく合理的だよね。
バス停英会話
今日スタバに行こうと歩いてたら、ホームレスっぽい人にバス停で6セント頂戴って言われたので、6セントだけ?ならいいよ!って返して、多めに25セント硬化をあげた。
ところが、彼が「one more!」とか言ってきたので、「お前さっき6セント言うたやんww 理由説明してみ?」っていう会話のやり取りをしたんだけど、このときのやり取りが思ったより楽しかった。
で、思ったんだけど、1ドルくらいあげるか、コーヒをおごってあげて、30分くらい話しようよ!って言ったら、意外としてくれたりするのかな。英会話の相手と考えたら、意外とコスパいいかもとか思ってしまった。
他には、コールセンターとか銀行も無料英会話教室として秀逸だよね。
アメリカのバス停で待ってると、よく声をかけられることが多い (大抵は「Are you chainese?」だけどw) 最初のころは怖くて話なんてできなかったんだけど、最近は度胸もついてきて楽しくなってきたなって思う。もうすぐアメリカに来て1年だけど、それなりに成長してるんだな。
大切なのはユースケースを絞り込むこと、忠実にアプローチすること。 Eventbrite
シリコンバレーのスタートアップが作る最近のサービスは、ユースケースがすごくSpecificで良い。
ユースケースの絞り込みって、サービスを新しく作る上で凄く重要で、そのユースケースに必要ならば、決済だろうが、オフラインだろうが、Webで解決できないこともどんどん盛り込んでいくくらいの方がよいし、実は収益化や差別化のポイントはそこにあるんじゃないかなって思う。
Eventbriteは、イベントの企画運営や、チケットの販売なんかが行えるサービスだ。日本で言うATNDのような感じなのだけど、チケットをオンラインで販売して参加費を回収したり、残席数の管理を自動で行ったり、チケットにバーコードをつけて入場管理なんかができちゃう便利なサービスだ。デザインも綺麗でいいよね。PeatixなんかがEventbrite for japanを狙っているのだと思う。
俺が面白いなって思うのは、Eventbriteの導線設計がすごくシンプルで洗練されていることだ。基本的にEventbriteでイベントを探すことはなくて、Facebookやブログ、オフィシャルサイトなんかにEventbriteプラグインが埋め込まれてて、そこからチケットを購入したり予約を入れに行くかたち。あくまでプラットフォームとして設計されてて、ごちゃごちゃしてない。
また、単なるイベント企画と募集ならWebで十分なんだけど、その一歩先の、イベントの運営管理といったペインにしっかりアプローチして、ローカルのソリューションまでも拾い上げてサービスにしている。Meetupとかでは捨ててた部分をしっかり拾い上げているのがいいね。
ATNDはトレンドを拾い上げるのが凄く良かったし、タイミングもばっちりだった。ただ、そこで終わってしまったのが、今から思えば少し勿体無かったなという感じかもしれない。ユースケースを絞り込んで、忠実に手段を選ばずアプローチを続けることが大切だ。
[Eventbrite] http://www.eventbrite.com/
[ATND] http://atnd.org/
[Meetup] http://www.meetup.com/
[PeaTix] http://peatix.com/
俺が勝手に考える正しいMVCの実装。モデルはデータAPI!
最近、一緒にコードを書く人(特にRailsから始めた学生さん)に、
MVC(Model - View - Controller)において、「model = DB」だと考えている人が多いなぁと感じたので、このあたりに関する自分の考えをまとめて書いておきます。
あくまで俺の考えなので、違ってたらごめんね。
MVCをちゃんと理解している人には当たり前すぎる話かもなのでスルーでよろしく!
初学者はViewをモリモリ生やす
これはプログラミングを始めた人なら誰でも経験ありますよね。
むしろ、MVCとか始める前の、誰でも経験あるであろう
<?php print '<a href="${hoge}">link</a>';
なんてのは完全にViewだけで実装されたプログラムですね。
最近のMVCのテンプレートはとても高機能です。
変数の宣言も、条件処理も、ループも、プログラム言語としてひと通りの「逐次、反復、制御」ができちゃいます。
だからプログラムのロジックをそこに生やしてしまいたくなる気持ちはわかりますが、やはりMVCという考え方において、その実装は正しくありません。
Viewにロジックが入り込んでしまうと、Viewの生産性とポータビリティ、保守性を下げてしまうからです。
自分が本格的にMVCを触れた時に使ったテンプレートは、PerlのHTML::Templateでした。
とても高速な代わりに、まともなロジックなんて一切記述できない堅物で、最初はとっても使いにくかったのですが、
気づけば半強制的にViewとControllerの切り分けを意識させられていました。
既に時代遅れ感はあり、いろいろ問題もありますが、私としては好きなモジュールです。
MVCが理解できた!と思った頃に陥るController厨
Viewにロジックを書くことをやめ、処理と表示を切り分けて考えれるようになった頃に、多くのひとはController厨になり、Controllerに処理をモリモリ生やし始めます。
というか、Controller厨で止っている人は意外と多いんじゃないでしょうか。
さくっと書いて捨てるコードなら、コントローラだけで十分な場合も多いです。
また、Railsに影響を受けた多くのフレームワークがそうですが、一見すると「モデル=データベース」と扱っているように見えてしまいますので尚更です。
「railsでモデルって勝手に作られるけど、自分でコードを足したことないなぁ」
って人もいるんじゃないかな。
しかし、コントローラにすべてのロジックを埋めこんでおくと、これもはやり保守性が下がります。
たとえば、モデルから呼び出したデータをキャッシュしたり、MySQL以外にKVSに格納されているデータと連携させたり、TwitterのAPIにアクセスしたりするような処理を想像してください。
ある日、TwitterのAPIのスキーマが一部変更になりました。
それに応じてKVSへのデータの保持方法が変わったり、DBのカラムが追加しなければいけなくなりました。
このとき、すべての処理をコントローラに書いていると、モデルの書き直しに加え、コントローラの大幅な書き直しが発生します。
しかも、コントローラには入力値のヴァリデーションから表示用データの加工まで様々なロジックが入っています。
処理が長いこと、変更箇所が多いことは、それだけでバグを埋め込む確率が上がります。
保守性も下がります。
正しいMVCではModelが太る
コントローラ厨時代を乗り越え、「こりゃダメだ」と感じ始めると、エンジニアは表示、処理に加えてデータという概念を持ち始め、モデルを太らせ始めます。
様々な考えがあると思いますが、私はモデルが太っているコードのほうが健全だと思います。
もし上記のケースであれば、モデルにメソッドを生やして、モデル内部でTwitterAPIやKVSの処理を隠蔽して実装していれば、ビューや、コントローラ側の実装に一切手を加えること無く、プログラムを改修することができます。
MVCにおけるModelとは、広い意味でのデータ全般であり、DBだけを差すものではありません。
TwitterAPIや外部ストレージ、Memcached、KVS、こういった生データやデータストア系は
すべてModelで取り扱い、その処理を隠蔽すべきだと私は思います。
ModelはMVCにおいて、データAPI的に設計すべきです。
それは、たとえばモデルへのアクセスがメソッド呼び出しじゃなく、RPCやWebAPIになったとしても困らないくらい綺麗なモデルのインタフェースを設計した方がいいという意味でもあります。
たとえば、Railsのモデルをコントローラでnewしたり、createすることって、ほとんどありません。
その理由は、フロントエンドのデータをそのまま無加工で流し込んで終わり、ってシーンがほとんどないからです。
ユーザ登録を行うことを考えてみましょう。ユーザ登録時にパスワードをSHA1などでハッシュ化して保存したいと考えた時、
コントローラ側で
User.create(:name => params[:name] , :password => Digest::SHA1.hexdigest(SALT + params[:password]) )
とするのは良くない実装です。
SALTや、ハッシュ化のロジックは、コントローラではなくモデル依存の都合のものなので、
クラスメソッドとしてモデル内に隠蔽すべきです。
#Controller User.register(:name => params[:name] , :password => params[:password] ) #Model def self.register(args) user = User.new user.name = args[:name] user.password = Digest::SHA1.hexdigest(SALT + params[:password]) user.save end
こうしておけば、SALTの変更や認証ロジックにTwitetr連携や他の操作を追加しようと思った時に、モデルの変更だけで改修が可能になります。
コントローラ側からすれば、モデルの内部実装を考えなくてすむ綺麗な呼び出し(インタフェース)なので、使いやすいですね。
オブジェクト思考は共同開発におけるプライベートスペース
ぶっちゃけ、複数人で共同開発しているときには、他人のコード1行1行について
趣味や価値観を押し付けあうのは、お互いにとって精神衛生良くないことですし、指摘する方も指摘される方も、なんだかギスギスしてしまいます。
それに、書き方なんて人それぞれの好みがありますし、友達と一緒に作る趣味プログラムに、厳格で厳しいコーディング規約を策定したりするほうが辛いし切ないですよね。
なので、インタフェースを綺麗に設計し、内部の実装にはある程度寛容に不干渉、というのが、みんなで楽しく開発するために良いスタイルじゃないかなと考えています。
そのぶん、インタフェースの設計に関してはどこまでもこだわって意見をぶつけ合って良いと思います。
人間関係もそうですが、共同開発にも「プライベートスペース」って大切ですよね。
それでは、みんなで楽しい共同開発を!
C#でmixi Graph APIにアクセスするサンプルを書いたよ
Macでmono使って書いた。意外と使えるね!
kyoro/mixi_graph_api_examples · GitHub
using System; using System.Text; using System.Net; using System.Web.Script.Serialization; using System.Collections.Generic; using System.Collections.Specialized; namespace MixiGraphAPIExample { class MainClass { private const string CONSUMER_KEY = "[YOUR CONSUMER KEY]"; private const string CONSUMER_SECRET = "[YOUR CONSUMER SECRET]"; private const string REDIRECT_URL = "[YOUR REDIRECT URL]"; public static void Main (string[] args) { MixiAPIClient client = new MixiAPIClient(CONSUMER_KEY,CONSUMER_SECRET,REDIRECT_URL); Console.WriteLine("1. Open this url and allow your application access."); Console.WriteLine(client.GetAuthorizeUrl()); Console.WriteLine("2. Input 'code' parameter value of redirected url."); string code = Console.ReadLine(); client.GetToken(code); Console.WriteLine("----"); string resText = client.Call("/people/@me/@self"); Dictionary<string,object> result = MixiAPIClient.JsonToDictionary(resText); Dictionary<string,object> entry = (Dictionary<string, object>)result["entry"]; foreach(string key in entry.Keys){ Console.WriteLine(key + " : " + entry[key]); } } } class MixiAPIClient { private const string MIXI_TOKEN_ENDPOINT = "https://secure.mixi-platform.com/2/token"; private const string MIXI_API_ENDPOINT = "http://api.mixi-platform.com/2"; private const string MIXI_AUTHORIZE_URL = "https://mixi.jp/connect_authorize.pl"; private string consumer_key; private string consumer_secret; private string redirect_url; public string token = ""; public string refreshToken = ""; public static Dictionary<string,object> JsonToDictionary(string jsonText) { JavaScriptSerializer serializer = new JavaScriptSerializer(); return serializer.Deserialize<Dictionary<string,object>>(jsonText); } public MixiAPIClient(string consumer_key, string consumer_secret, string redirect_url) { this.consumer_key = consumer_key; this.consumer_secret = consumer_secret; this.redirect_url = redirect_url; } public string GetAuthorizeUrl() { return MIXI_AUTHORIZE_URL + "?scope=r_profile&client_id=" + consumer_key; } public void GetToken(string authorizationCode) { NameValueCollection data = new NameValueCollection(); data.Add ("grant_type","authorization_code"); data.Add ("client_id",consumer_key); data.Add ("client_secret",consumer_secret); data.Add ("redirect_uri",redirect_url); data.Add ("code",authorizationCode); WebClient wc = new WebClient(); Byte[] resData = wc.UploadValues(MIXI_TOKEN_ENDPOINT, data); string resText = Encoding.UTF8.GetString(resData); Dictionary<string,object> result = MixiAPIClient.JsonToDictionary(resText); token = result["access_token"].ToString(); refreshToken = result["refresh_token"].ToString(); } public string Call(string endpoint) { string url = MIXI_API_ENDPOINT + endpoint + "?oauth_token=" + token; WebClient wc = new WebClient(); byte[] resData = wc.DownloadData(url); return Encoding.UTF8.GetString(resData); } } }