Rails Assets を使って Rails プロジェクトに AngularJS をインストール

Rails で AngularJS を使おうと思っていたけど、

  • ファイルをダウンロードして vendor/assets に直接配置するのは管理が面倒
  • AngularJS をラップした gem は AngularJS 本体がバージョンアップしたとき対応するまでタイムラグがある
    • そもそも angular-gem や angular-rails や angularjs-rails-resource など乱立してるし
  • Bower で AngularJS を管理するのがいいかも
    • Bower を動かすために Node が必須なので、Heroku デプロイ時に AngularJS をインストールできない
    • カスタム Buildpack を作れば可能だけど、アプリの本質ではないので労力をかけたくない。

という紆余曲折を経て、『Rails Assets』を使ってみることにした。

Rails Assets は、Bower のパッケージを gem にコンバートし、ホストしているサービス。 Gemfile に

source "https://rails-assets.org"

を追加し、

gem "rails-assets-<Bower パッケージ名>"

という形式で指定することで、Bundler で JavaScript ライブラリをインストールできる。

Rails で AngularJS を使いたい場合、

source "https://rails-assets.org"
gem "rails-assets-angular"
gem "rails-assets-angular-route"

と書いて bundle install でインストール。 このとき依存するライブラリもインストールしてくれる。

あとは app/assets/javascript/application.js に

//= require angular
//= require angular-route

と書けば、Asset Pipeline に AngularJS が組み込まれる。

Rails Assets は gem のインストールなので Bower 不要。Node も不要。 カスタム Buildpack を使わなくても Heroku で動かせる。

Subversion でリベースしたい

仕事では大人の事情で Subversion を使わざるをえないんだけど、 Subversion でも Github Flow みたいに、ブランチ切って作業するように努めてはいる。

今回、他の作業が入って、ブランチをだ〜いぶ放置していたら、その間に trunk に結構修正が入ってしまった。 trunk の修正内容をブランチにマージすると、最終的にブランチを trunk にマージするとき衝突しまくった過去があるので、リベースしたい。

Git なら rebase で一発だけど、Subversion には rebase コマンドはない。 仕方ないので、他の方法をとることにした。

具体的には、リベースしたい feature ブランチがあるとき下記の手順をおこなった。

  1. trunk から最新リビジョンを起点に新しいブランチ rabase_feature を作成
  2. feature ブランチを rebase_feature ブランチにマージする
  3. feature ブランチを削除
  4. rebase_feature ブランチを feature にリネーム

これでリベースっぽくなった。 ただ、旧 feature ブランチの履歴を引き継げないから完璧ではない…。 もっと上手いやり方はあるのかも。

Subversion から Git に移行するのが個人的には一番いい。 Visual Studio に Git バンドルされないかな。 そうなれば大人の事情もクリアできそうなんだけど。

パーフェクト Ruby on Rails

技術評論社のパーフェクトシリーズに、 ついにRuby on Railsが出たので購入。

お目当ては、9章『より実践的なモデルの使い方』。

自分の場合、Railsは趣味プログラミングで使うことがほとんどなので、 つい動かすことを優先にコードを書きがち。 コントローラーが太らないよう心がけてはいるので、 ビジネスロジックをモデルに実装していったら、 気が付けばファットモデルの出来上がり。 悲しいけど、よく遭遇するパターンだったりする。

そこでモデルのダイエットに取り組むのだけど、

  • 複雑なバリデーションとコールバックをクラスに切り出す
  • データベースに依存しないモデルを切り出す
  • 値オブジェクトを切り出す
  • 共通の関心ごとを cencerns に切り出す
  • 複数のモデルにまたがった処理をサービスクラス化

といった、9章で紹介されている手法は、概ね自分がやっている手法と同じでホッとした。 バリデーションやコールバックをクラスにするまではやってないけど。

文章が読みやすかったので、週末一気に読み終えた。 扱う範囲が、Railsアプリ開発とテストだけに留まらず、インフラ運用とRailsの拡張までと幅広く、 盛りだくさんという言葉がふさわしい本だった。 Rails 初心者には難しい。中級者向けなのは間違いない。

パーフェクト Ruby on Rails

パーフェクト Ruby on Rails

鼎泰豊

博多阪急にオープン時から出店しているの『鼎泰豊』。 本店は世界の人気レストラン10店に選ばれたらしい。 その情報を知って、行きたかったんだけど、ランチといえど敷居が高くて…。 予算オーバーではあるけど、今回意を決して行ってみた。

いろんな種類の小龍包を食べるか、それともランチセットにするか注文ギリギリまで迷って、 土壇場で海老入りチャーハンセット(1620円)を注文。空腹が決め手だった。

f:id:griefworker:20140607124118j:plain

オーソドックスな肉餡の小龍包が 4 個。 刻み生姜と酢醤油でいただく。 薄皮を箸でつつくと、閉じ込めらていたスープがあふれ出し、 それが酢醤油と混ざって、旨みにほのかな酸味が合わさって美味だった。

f:id:griefworker:20140607124548j:plain

これまたオーソドックスな炒飯。 海老を生かす、あっさりとした味付けだった。 大ぶりの海老の、プリっとした食感が印象的。

今回は空腹だったのでランチセットにしたけど、もっといろんな種類の小龍包を食べてみたかった。 ホタテ、かに味噌、ウニなど、絶対旨いに違いない。

関連ランキング:飲茶・点心 | 博多駅祇園駅

AngularJS で Underscore.js を使ってみた

AngularJS にユーティリティ関数あるにはあるけど、配列やオブジェクトを操作する関数が足りないので、 Underscore.js を使うことにした。

AngularJS らしく依存性注入したいから、Underscope.js を AngularJS モジュール化している。

<!DOCTYPE html>
<html ng-app="UnderscoreSample">
  <head>
    <meta charset="utf-8">
    <title>UnderscoreSample</title>
  </head>
  <body ng-controller="MainCtrl">
    <ul>
      <li ng-repeat="value in values" ng-bind="value"></li>
    </ul>

    <script src="underscore.js"></script>
    <script src="angular.js"></script>
    <script>
      // underscore をモジュール化
      angular.module("underscore", []).factory("_", function() {
          return _;
      });

      var app = angular.module("UnderscoreSample", ["underscore"]);

      // モジュール化した Underscore を使っていることがわかるように、$_ にしておく。
      // 実際は _ で使っている。
      app.controller("MainCtrl", ["$scope", "_", function($scope, $_) {
          var src = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

          // 偶数だけを表示する
          $scope.values = $_.filter(src, function(n) {
            return n % 2 === 0;
          });
      }]);
    </script>
  </body>
</html>

余談だけど、angular-underscore というライブラリがある。

こいつはフィルタも提供してくれるんだけど、フィルタは今のところ必要としていないのと、 $rootScope に Underscore の関数を生やすのが個人的に気に入らないので、導入は見送った。

AngularJS のテンプレートを入れ子にしたかったから angular-ui-router を使ってみた

AngularJS でテンプレートを入れ子にしたかったけど、ng-view は入れ子にできない。 つまり AngularJS の機能だけでは困難。 おまけに ng-view は複数置いて、別々のテンプレートを表示することもできないときた。

そんな ng-view や ngRoute に置き換わるのを目指したモジュールで、angular-ui-router がある。

こいつを使えばできそう。論よりコード。

<!DOCTYPE html>
<html ng-app="UIRouterSample">
  <head>
    <meta charset="utf-8">
    <title>UIRouterSample</title>
  </head>
  <body ng-controller="MainCtrl">
    <ul>
      <li ng-repeat="label in labels">
        <a ui-sref="label({ labelName: label.name })" ng-bind="label.name"></a>
      </li>
    </ul>
    <div ui-view></div>

    <script id="mails_template" type="text/ng-template">
      <table class="table">
        <thead>
          <tr>
            <th>Title</th>
          </tr>
        </thead>
        <tbody>
          <tr ng-repeat="mail in mails">
            <td><a ui-sref=".thread({mailId:mail.id})" ng-bind="mail.title"></a></td>
          </tr>
        </tbody>
      </table>
      <div ui-view></div>
    </script>

    <script id="thread_template" type="text/ng-template">
      <table class="table">
        <thead>
          <tr>
            <th>Title</th>
            <th>Message</th>
          </tr>
        </thead>
        <tbody>
          <tr ng-repeat="mail in mails">
            <td ng-bind="mail.title"></td>
            <td ng-bind="mail.message"></td>
          </tr>
        </tbody>
      </table>
    </script>

    <script src="angular.js"></script>
    <script src="angular-ui-router.js"></script>
    <script>
      // ui.router を使う
      window.App = angular.module("UIRouterSample", ["ui.router"]);

      App.controller("MainCtrl", ["$scope", function($scope) {
        $scope.labels = [
          { id: 1, name: "today" },
          { id: 2, name: "someday" },
          { id: 3, name: "scheduled" }
        ];
      }]);

      // $stateParams からパラメータを取り出せる
      App.controller("MailsCtrl", ["$scope", "$stateParams", function($scope, $stateParams) {
        var prefix = $stateParams.labelName + "_";
        $scope.mails = [
          { id: 1, title: prefix + "aaaaaa" },
          { id: 2, title: prefix + "bbbbbb" },
          { id: 3, title: prefix + "cccccc" }
        ];
      }]);
      App.controller("ThreadCtrl", ["$scope", "$stateParams", function($scope, $stateParams) {
          var prefix = "mail" + $stateParams.mailId + "_";
          $scope.mails = [
            { id: 1, title: "aaaaaa", message: prefix + "hogehoge" },
            { id: 2, title: "bbbbbb", message: prefix + "fugafuga" },
            { id: 3, title: "cccccc", message: prefix + "foobar" }
          ];
      }]);

      // ステートを登録
      // URL・コントローラー・テンプレートのセットに名前を付ける
      App.config(["$stateProvider", "$urlRouterProvider", function($stateProvider, $urlRouterProvider) {
        $stateProvider.
          state("label", {
            url: "/labels/{labelName:[a-zA-Z]+}",
            controller: "MailsCtrl",
            templateUrl: "mails_template"
          }).
          state("label.thread", {
            url: "/thread/{mailId:[0-9]+}",
            controller: "ThreadCtrl",
            templateUrl: "thread_template"
          });
      }]);
    </script>
  </body>
</html>

URL とコントローラーとテンプレートをセットにして「ステート」として扱っている。 ステートというより、Django のエンドポイントに近い気がする。 名前から URL を逆引きできるし。

angular-ui-router を AngularJS 本体に組み込む、なんて動きもあるみたいだけど、 早く進めて欲しいところだ。

天ぷら だるま

福岡市で天ぷらといったら「ひらお」が人気なんだけど、 大名にある「だるま」もグルメ本にちょくちょく載っていて気になっていたので、 週末行ってみた。

天ぷら定食 (800 円) を注文。

f:id:griefworker:20140531112541j:plain

まずはご飯とつゆ。大根おろしは少なめ。

f:id:griefworker:20140531112929j:plain

味噌汁。味噌汁は魚介のアラを使っているのか、いい出汁が出ていて旨い。

f:id:griefworker:20140531112719j:plain

最初の魚。

f:id:griefworker:20140531112844j:plain

海老。身がプリップリだった。

f:id:griefworker:20140531113128j:plain

魚その二。

f:id:griefworker:20140531113009j:plain

茄子。デカイ。ジューシーすぎて、汁で口の中やけどした。

f:id:griefworker:20140531113256j:plain

最後はイカ。実が柔らかくて、簡単に噛み切れた。

天ぷらは以上の5品。味は良かったけど、食べ足りない。 値段も 800 円と品数に対して高め。 いや、天ぷら定食 800 円って傍目に見れば安いけど、どうしてもひらおと比べてしまう。 正直なところ、同じ値段払うなら、ひらおに行くだろうな。

関連ランキング:天ぷら | 赤坂駅薬院大通駅西鉄福岡駅(天神)