AngularJSで作業用BGM再生アプリを作りました

AngularJSの練習をしていたら作業用BGM再生アプリができました。
ナガレテッター

どういうものか

J-WAVEで流れた曲がプレイリストになっています。
プレイリストから曲をクリックすると、YouTubeにあるその曲の動画が流れます。

なぜ作ったか

AngularJSの勉強のため。
新しい曲・名曲を知るきっかけになるのがラジオであることが多いです(個人的に)。なのでそのきっかけが広がるものを作りたいと思ったため。

仕組みは

HerokuのClock ProcessでJ-WAVEで流れた曲を定期的に取ってきてMongoDB(MongoLab)に保存しています。
さらにHerokuにRailsでAPIサーバーを立てて、MongoDBのデータを返します。
クライアントはYeomanのgenerator-angularを元にして作りました。これをGitHub Pagesにデプロイしてます。
YouTube再生の内部的な仕組みはyusukebeさん作のFMTubeの実装を参考にさせてもらいました。勉強になります。

AngularJSで無限スクロール

こんにちは。今日は暖かい日和です。もう3月ですね。

さて、放置気味だったAngularJSの勉強を再開しました。 とりあえずこの本を読んでいます。
良い本と思います。

さて、Twitterのタイムラインのような無限スクロールをAngularJSで作るとどういう感じになるのかな、ということを調べてみました。

ググってみると、ズバリなものが見つかります。
ngInfiniteScroll
これでええやん、という話ですが、今回はもっと軽い実装を探してみます。

そうすると、こういうものがJSFiddleにアップされています。
AngularJS: Infinite Scrolling
なるほど、AngularJSではコレクションのレンダリングは自動でやってくれるので、一番下までスクロールしたタイミングでコレクションにデータを追加してやれば簡単にできるようです。

これを参考にして実装してみました。

Template

無限スクロールを表示する部分です。

<div class='content' when-scrolled='loadMore()'>
<ul ng-repeat='d in data'>
  <li>{{d}}</li>
</ul>
</div>

dataという名前のコレクションをリスト表示しています。 リストを囲っているdivにwhen-scrolledというディレクティブを指定しています。

Directive

whenScrolledディレクティブです。
一番下までスクロールしたらattributeの値を実行するようにします。
つまりコントローラーのloadMoreを実行します。

angular.module('myApp.directives', []).
  directive('appVersion', ['version', function(version) {
    return function(scope, elm, attrs) {
      elm.text(version);
    };
  }])
  .directive('whenScrolled', function($window) {
    return function(scope,  elem,  attr) {
      var raw = elem[0];
      angular.element($window).bind('scroll',  function() {
        if (raw.offsetTop + raw.offsetHeight < document.documentElement.scrollTop + window.innerHeight) {
          scope.$apply(attr.whenScrolled);
        }
      });
    };
  });

Controller

コントローラーはInfiniteScrollCtrlです。
初期時には25個の要素をdataに入れます。
loadMoreメソッドがコールされると、dataに25個追加します。
これにより無限スクロールするように見えます。

angular.module('myApp.controllers', []).
  controller('MyCtrl1', [function() {

  }])
  .controller('MyCtrl2', [function() {

  }])
  .controller('InfiniteScrollCtrl', ['$scope', function($scope) {
    var i = 0,
        data = [];
    for(i = 0; i < 25; i++) {
      data.push(i);
    }
    $scope.data = data;
    $scope.next = data.length;

    $scope.loadMore = function() {
      if ($scope.next === i + 25) {
        return;
      }
      $scope.next = i + 25;

      for(; i < $scope.next; i++) {
        data.push(i);
      }
    };
  }]);

まとめ

データを増やせば自動でレンダリングされるので、jQueryなどで実装するよりコード量が減りますね。
ここに実装おいてます。

Flotでグラフを書いてみる

データビジュアライゼーションが気になる今日この頃です。

D3.jsでグラフを描いてみる を読んだのですが、割とシンプルなグラフなのにけっこう面倒くさいと感じました。
まあD3.jsがグラフに特化してるわけでないからと思います。

人気のKibanaを見たところ、グラフはjQueryベースのFlotで書いているようです。
elasticsearch / kibana
というわけで、Flotを試してみようと思いました。

Flot

Examplesにあるこれを参考にしました。
Categories

それっぽいものはすぐにできたのですが、
s5ot / gist:8970149
どうもFlotにはd3 transition APIのようなアニメーション向けのAPIがないようにお見受けしました。
ちょっとそれをゴリゴリ書くのはキツいですね。。

RubyMotionアプリでStoryboardを使う(ibを使う)

以前にRubyMotionでStoryboardを使う方法を調べたことがありました。

RubyMotionアプリでStoryboardを使うには

まあ使えることは使えたんですが、Storyboardとヘッダファイルの結びつけとかできなくて、あまり実用的でないなあ。。という感じでした。
最近になって、そういうモヤっとしたところを解決してくれるgemがあることをこちらで知りました。

第八回 RubyMotion で Interface Builder を使うには?

ということで、こちらで紹介されているibを使ってみました。

ibをインストール後、rake ib:project タスクを実行すると、Storyboard用のXcodeプロジェクトが作成されます。
そして rake ib:open タスクを実行すると、Xcodeが起動してStoryboardが表示されます。
このときに、ibがRubyMotionで実装したOutletやメソッドを自動でStubs.hファイルに展開してくれます。
Outletはoutletクラスメソッドで宣言的に書きます。(下記参照)
メソッドはIBActionになります。

具体的には、このように書いておいて rake ib:open を実行すると、

class NewPostViewController < UIViewController
  extend IB
  outlet :addressLabel, UILabel
  outlet :detailTextView, UITextView
  outlet :tagTextField, UITextField
  outlet :scrollView, UIScrollView
  outlet :uploadButton, UIBarButtonItem
  outlet :manholeImageView, UIImageView

  def onSingleTap
    // Do something
  end

  // snip...

end

Stubs.hにはこんな感じで展開されるわけです。

@interface NewPostViewController: UIViewController

@property IBOutlet UILabel * addressLabel;
@property IBOutlet UITextView * detailTextView;
@property IBOutlet UITextField * tagTextField;
@property IBOutlet UIScrollView * scrollView;
@property IBOutlet UIBarButtonItem * uploadButton;
@property IBOutlet UIImageView * manholeImageView;

-(IBAction) onSingleTap;

@end

あとはStoryboardからStubs.hにビーッと線を伸ばして紐付けをしてあければよいわけです。
やっぱりStoryboardを使うとビューのコードがググっと減るので便利ですね。全体構成もつかみやすくなるし。
これからはibを使っていこうと思ってます。

Herokuで定期処理(Clock Process)

ちょっとした定期処理をやりたい事案が発生しました。できればお金をかけずにやりたい。
ということでここはHerokuでなんとかできないか調べてみたところ、Clock Processというのが期待どおりな感じでした。 Scheduled Jobs and Custom Clock Processes in Ruby with Clockwork

clockworkというgemを使って定期処理を書いて、Herokuで常駐プロセスが定期処理を実行してくれるというものです。 tomykaira / clockwork
また、clockworkを使うHerokuアプリのひな形を作ってくれるシェルスクリプトがあったりしてありがたい感じです。
tomykaira / clockwork-init.sh
clockworkはシンプルなAPIでやりたいことをすぐに書ける感じになってますが、かなり高機能でもあるようです。こちらが詳しいです。
clockwork について

ということで、ちょっとした定期処理を書くならHerokuのClock Processが便利という話でした。

アプリックスのBM1でiBeaconを試す

アプリックスのBeaconモジュール「BM1」を入手したので(お借りしたので)、試しています。

BM1にはBeaconのデータを書き換えることができるアプリが提供されていて、このアプリからproximityUUID, major, minor, measured powerを書き換えることができます。

BM1からの電波を受信するアプリを作ってみました。
BM1Receiver

参考にさせてもらったのはこちらです。
BeaconSample

Beaconの領域に入ったり出たりしたときにLocal Notificationを出したりします。

思ったこと

複数のBM1を運用するケースで、ProximityUUIDをアプリ経由で書き換えるのは手間がかかる。一括で管理するアプリがあると便利かも。。出荷時にアプリックスに一括設定を頼むことはできるみたい。あ、別途提供予定らしいですね。

BM1で使えるProximityUUIDはどこから発行されているのだろう?
国際規格に基づいてアプリックス社で生成されているらしいのですが。。
もし同じUUIDが他で使われていた場合は損害賠償を求めることができるらしいです。

BM1設定アプリでmeasured powerを設定できるけども、これはCoreLocationのAPIでは取得できないもよう。CoreBluetoothのAPIからは取れると思うのだけれども。

BM1の電波を受信して動作するアプリがProximityUUIDをどうやって得るか。
サーバーに問い合わせる形になりますよね。
やっぱりサーバーサイドの管理アプリが必要ですよね。

RubyMotionアプリでTestFlightを使ってみる

前々からTestFlightを使ってみたいと思ってたのですがなかなかできてなかったのでとりあえずやってみました。

RubyMotionアプリでTestFlightを使うにあたっては、motion-testflightという便利なgemがあります。 これを素直に使わせてもらいます。
motion-testflight

こちらを使うとRakeタスク「testflight」でビルドしたアプリをTestFlightへアップロードして配布することができます。

$ bundle exec rake testflight notes="zomg!"

このRakeタスクを実行するにはRakefileに3つのtokenを設定する必要があるのですが、それらをどこから得るのか分かりにくかったのでメモしておきます。

api_token

https://testflightapp.com/account/#api

team_token

https://testflightapp.com/dashboard/team/edit/?next=/api/doc/

app_token

Appsタブからアプリを登録すると発行される

iOSのAPNsを試してみた


あけましておめでとうございます。
Apple Push Notificationサービス(APNs)を自分で動かしたことがなかったので試してみました。

ggってみると、こちらがいろんなところからお手本として参照されているようです。
確かにとても親切な解説になっているので、これでAPNsをバッチリ試すことができました。
iOS6向けと書いてますが、iOS7でも特に違いはありません。
Apple Push Notification Services in iOS 6 Tutorial: Part 1/2

apache-loggen + Fluentd + Elasticsearch + Kibana3をVagrant with Dockerで動かしてみる

この前、手元のMac OS X上にFluentd + Elasticsearch + Kibana3の環境を作ってみました。 けど、こういうのは仮想環境上に作っておくのがなにかと便利です。

Vagrantの最近のアップデートでprovisioningにDockerが使えるようになりました。 これはDocker使って環境作ったらかっこええんちゃう?と思ったら、すでにやっている方がいました。
簡単!Vagrant+Docker+td-agent+Elasticsearch+Kibanaで流行に乗ろう

こちらを参考にDockerを触ってみて、Dockerの雰囲気と面白さがなんとなく感じられました。
ただ、Kibanaでいい感じの表示をするためにはある程度のデータがあったほうがいいので、apache-loggenでデータを自動生成するのも混ぜたいなと思いました。
そこでできあがったのがこちらになります。
s5ot / vagrant-docker-td-es-kibana-sample

vagrant upすればapache-loggenがデータを作り続け、Fluentd のtailで Elasticsearch へ流し続けてます。
http://localhost:5555 でご覧ください。 Kibanaの使い方の練習ができます。

Dockerに関して湧いた疑問

ImageをBuildして最後にミドルウェアなりを起動するときにENTRYPOINTで起動するケースと、CMDでrun.shを叩いて起動するタイプがある。 これはどういう使い分けなんかな?と思ったけど、マニュアルに書いてあった。
Build Images (Dockerfile Reference)

コンテナの起動時にconfig的なデフォルト値を与えて起動したいなら、CMDでrun.shのようなものを使う。CMDはImageのBuild時には実行されない。固定のバイナリを叩いてコンテナを起動するならENTRYPOINTを使う。
というように理解しました。

あとやりたいこと

Kibanaのアップデート

Kibana3のイメージはarcus/kibanaを使ってるのですが、これはKibana 3 milestone 2になってます。本家はもっと新しいのが出ていてかなり機能が変わってるようなのでアップデートしたいところ。

apache-loggenコンテナ

今のところ、apache-loggenはtd-agentコンテナに同居しているのですが、apache-loggenだけのコンテナを作り、td-agentとDockerのmount Volumes機能で連携するのがスマートだと考えています。

Fluentd + Elasticsearch + Kibana3を動かしてみる

Elasticsearchすげーよ、なんでいままで使ってなかったんだろう!的なツイートを見かけて、おおこれは触っておかなければ、と思い立ちました。

ElasticsearchにRESTでデータを投げ込んで、それをKibana3で見てみましたが、なんだかピンとこなかったので、みんなどうしているのかなとggってみると、ApacheログをFluentdで読んでElasticsearchに投げてKibana3で見る、っていうのをみんなチュートリアル的にやってる感じなのでそれをやってみました。
参考にしたのはこちらです。
Apache-loggen + Fluentd + Elasticsearch + Kibana = ログ検索デモ

Elasticsearchのインストール

本家サイトからzipをダウンロードして解凍したディレクトリで

$ bin/elasticsearch -f

で起動。 簡単です。

Kibana3のインストール

これまた本家サイトからzipをダウンロードして解凍したディレクトリをApacheのドキュメントルートに放り込めば動きます。
簡単です。

Fluentdのインストール

若干ハマりました。。
Fluentdはいろいろとインストールの方法があるのですが、なんとなくbrewでインストールしてみました。
で、FluentdからElasticsearchへ連携するためにfluent-plugin-elasticsearchというFluentdのプラグインを使いますのでインストールします。
fluent-plugin-elasticsearch
そしてFluentdの設定を追加します。

td-agent.conf
<source>
  type tail
  format apache
  path /Applications/XAMPP/logs/access_log
  tag xampp.apache.access
</source>

<match *.apache.*>
  index_name adminpack
  type_name apache
  type elasticsearch
  include_tag_key true
  tag_key @log_name
  host localhost
  port 9200
  logstash_format true
  flush_interval 10s
</match>
$ td-agent

で、td-agentを起動します。

これで準備はできたのですが、トラブル発生。 td-agentでエラーが発生しました。

2013-12-18 17:05:29 +0900 [error]: config error file="/usr/local/etc/td-agent/td-agent.conf" error="Unknown output plugin 'elasticsearch'. Run 'gem search -rd fluent-plugin' to find plugins"

elasticsearchのpluginが見つからないと。。インストールしたはずなのですが。

調べてみると、Fluentdは独自のGEM_HOMEを持っていて、そこにインストールしないとダメらしいです。

$ /usr/local/bin/fluent-gem install fluent-plugin-elasticsearch

これでtd-agentがpluginを見つけてくれました。