grunt-wiredep使用の環境でmomentの日本語ロケールファイルを含めたい

より正確にいうとgrunt-wiredep使用の環境でangular-momentロケール指定したい、ということ。

angular-momentをインストールする

% bower install angular-moment --save

これでmomentも依存関係でインストールされて、wiredepで差し込まれてめでたしめでたし…

index.html

  <!-- bower:js -->
  <!-- 略 -->
  <script src="bower_components/moment/moment.js"></script>
  <script src="bower_components/angular-moment/angular-moment.js"></script>
  <!-- endbower -->

いやいや、これだとロケールファイルが含まれていない。

bower_components/下のmomentを見ると、minディレクトリにmoment-with-locales.jsがある。

なので、プロジェクトのbower.jsonでmainをoverrideする。

bower.json

{
// 略
  "overrides": {
    "moment": {
      "main": "min/moment-with-locales.js"
    },
    "angular-i18n": {
      "main": "angular-locale_ja-jp.js"
    }
  }

ロケール入りになった。

index.html

  <!-- bower:js -->
  <!-- 略 -->
  <script src="bower_components/moment/min/moment-with-locales.js"></script>
  <script src="bower_components/angular-moment/angular-moment.js"></script>
  <!-- endbower -->

これで設定できる

app.coffee

myapp.run((amMoment) ->
    amMoment.changeLanguage('ja')
)

ProtractorでngMockE2Eを読み込んでモックを使ってテストしている場合、build時にはngMockE2Eを除外したい

yeomanのgenerator-angularで作ったプロジェクトで、grunt-wiredepを使っているので、素直にE2Eテストでangular-mocksを使おうとすると、bower.jsonのdevDependenciesからdependenciesに移す必要がある。

// bower.json
{
  "name": "MyApp",
  "version": "0.0.1",
  "dependencies": {
    "angular": "1.2.23",
    "angular-resource": "1.2.23",
    "angular-cookies": "1.2.23",
    "angular-sanitize": "1.2.23",
    "angular-animate": "1.2.23",
    "angular-touch": "1.2.23",
    "angular-ui-router": "~0.2.11",
    "angular-i18n": "~1.2.23"
  },
  "devDependencies": {
    "angular-mocks": "1.2.23"    // dependenciesに移すと…
  },
  "overrides": {
    "angular-i18n": {
      "main": "angular-locale_ja-jp.js"
    }
  },
  "appPath": "app"
}

しかし、これだとテスト時はいいが、grunt buildして製品用に吐き出した時にangular-mocks.jsも一緒に結合、圧縮されてしまう。

<!-- index.html -->
  <!-- build:js(.) scripts/vendor.js -->
  <!-- bower:js -->
  <script src="bower_components/jquery/dist/jquery.js"></script>
  <script src="bower_components/ng-file-upload-shim/angular-file-upload-shim.js"></script>
  <script src="bower_components/angular/angular.js"></script>
  <script src="bower_components/angular-resource/angular-resource.js"></script>
  <script src="bower_components/angular-cookies/angular-cookies.js"></script>
  <script src="bower_components/angular-sanitize/angular-sanitize.js"></script>
  <script src="bower_components/angular-animate/angular-animate.js"></script>
  <script src="bower_components/angular-touch/angular-touch.js"></script>
  <script src="bower_components/angular-ui-router/release/angular-ui-router.js"></script>
  <script src="bower_components/angular-i18n/angular-locale_ja-jp.js"></script>
  <script src="bower_components/angular-mocks/angular-mocks.js"></script>
  <!-- wiredepにより、ここに挿入されるので、一緒にまとめられてしまう!! --> 
  <!-- endbower -->
  <!-- endbuild -->

何かいい方法はないかと調べたら、そのものずばりであった。

…And $httpBackend Mock For All (Unit & E2E) Testings

ので、下のようにを、ファイル名指定なしで書いてangular-mocks.jsを囲う。

  <!-- index.html -->
  <!-- build:js(.) scripts/vendor.js -->
  <!-- bower:js -->
  <script src="bower_components/jquery/dist/jquery.js"></script>
  <script src="bower_components/ng-file-upload-shim/angular-file-upload-shim.js"></script>
  <script src="bower_components/angular/angular.js"></script>
  <script src="bower_components/angular-resource/angular-resource.js"></script>
  <script src="bower_components/angular-cookies/angular-cookies.js"></script>
  <script src="bower_components/angular-sanitize/angular-sanitize.js"></script>
  <script src="bower_components/angular-animate/angular-animate.js"></script>
  <script src="bower_components/angular-touch/angular-touch.js"></script>
  <script src="bower_components/angular-ui-router/release/angular-ui-router.js"></script>
  <script src="bower_components/angular-i18n/angular-locale_ja-jp.js"></script>
  <!-- endbower -->
  <!-- endbuild -->
  <!-- build:js -->
  <script src="bower_components/angular-mocks/angular-mocks.js"></script>
  <!-- endbuild -->
 ```

これでテスト時は普通にモックが使えて、grunt buildすると、angular-mocks.jsのセクションは消えている。

grunt-wiredep使用の環境でangular-i18nをbowerでインストールしたら注入できないとメッセージ

AngularJSリファレンス購入して読了。これは良い本。

で、フィルタのi18nの項をみて、早速いれてみたら、grunt-wiredepを使っているGruntのタスクで注入出来ないとメッセージが出て、htmlファイルのコメントの間にi18nへのscriptタグが挿入されない。

ちなみにgenerator-angularで作ったプロジェクトで、bowerでi18nを入れての話。

% bower install angular-i18n --save

% grunt serve

# .......略...............

Running "wiredep:app" (wiredep) task
app/index.html modified.

angular-i18n was not injected in your file.
Please go take a look in "/home/vagrant/myApp/client/bower_components/angular-i18n" for the file you need, then manually include it in your file.

Running "wiredep:sass" (wiredep) task

angular-i18n was not injected in your file.
Please go take a look in "/home/vagrant/myApp/client/bower_components/angular-i18n" for the file you need, then manually include it in your file.

index.html

  <!-- bower:js -->
  <!-- 略 -->
  <script src="bower_components/angular-i18n/angular-locale_ja-jp.js"></script>
  <!-- ↑これが入って欲しいが入らない -->
  <!-- endbower -->

angular-i18nのbower.jsonを確認するとmainが指定されていない。 まあ色々な言語のパッケージが入ってるし、勝手にロケールを解決してくれるとも思えないので、そりゃそうか。 なので、bower.jsonにこうした。

//bower.json
  "overrides": {
    "angular-i18n": {
      "main": "angular-locale_ja-jp.js"
    }
  }

これでgrunt serveなりwiredepのタスクが含まれたタスクを実行すると…OK.注入された。

index.html

  <!-- bower:js -->
  <!-- 略 -->  
  <script src="bower_components/angular-i18n/angular-locale_ja-jp.js"></script>
  <!-- endbower -->

Vagrant環境でload-grunt-taskを使ったGruntのtaskが遅いのでjit-gruntに変えた

インストール

% npm install jit-grunt -D
% npm uninstall load-grunt-tasks -D

jit-gruntを使うようにGruntfile.coffeeを書き換え。
後、watchにspawn: falseも追加しといた。

#Gruntfile.coffee

 module.exports = (grunt) ->
 
   # Load grunt tasks automatically
-  require("load-grunt-tasks")(grunt)
+  require('jit-grunt')(grunt)
+  grunt.loadNpmTasks('grunt-connect-proxy')
+  grunt.loadNpmTasks('grunt-protractor-runner')
+  grunt.loadNpmTasks('grunt-google-cdn')
+  grunt.loadNpmTasks('grunt-usemin')
 
   # Time how long tasks take. Can help when optimizing build times
   require("time-grunt")(grunt)

# ----------------省略------------------
   # Watches files for changes and runs tasks based on the changed files
     watch:
+      options:
+        spawn: false
# package.json
     "grunt-wiredep": "^1.7.0",
+    "jit-grunt": "^0.8.0",
     "jshint-stylish": "^0.2.0",
# ----------------省略------------------
     "karma-spec-reporter": "0.0.13",
-    "load-grunt-tasks": "^0.4.0",
     "protractor": "^1.0.0",

jit-gruntは load-grunt-tasksだと読み込んでくれてたものを読み込んでくれないパッケージがある*1ので、読み込んでくれないパッケージは明示的にloadNpmTasksする。 どれが読み込まれないのかよくわからなかったら、差し替えてtaskを実行してみれば見つからないと表示してくれるので、それを読みこめばOK.

jit-grunt: Plugin for the "useminPrepare" task not found.
If you have installed the plugin already, please setting the static mapping.
See https://github.com/shootaroo/jit-grunt#static-mappings

早くなりました。

参考

*1:読み込み規則がちがう

deprecatedが出ていたngminからng-annotateへ切り替え

AngularJSのminify対策をしてくれるアレ。
例によって、generator-angularで作ったプロジェクトでの話。
grunt-ng-annotateをインストールしてgrunt-ngminを外して、記述をngminからngAnnotateにしただけでいけました。

% npm install grunt-ng-annotate -D
diff --git a/package.json b/package.json
index 77190a9..e1f2b1a 100644
--- a/ngapp/package.json
+++ b/ngapp/package.json
@@ -25,7 +25,7 @@
     "grunt-google-cdn": "^0.4.0",
     "grunt-karma": "^0.8.3",
     "grunt-newer": "^0.7.0",
-    "grunt-ngmin": "^0.0.3",
+    "grunt-ng-annotate": "^0.3.2",
     "grunt-protractor-runner": "^1.1.0",
     "grunt-svgmin": "^0.4.0",
     "grunt-usemin": "^2.1.1",
-- a/Gruntfile.coffee
+++ b/Gruntfile.coffee
@@ -300,10 +300,7 @@ module.exports = (grunt) ->
           dest: "<%= yeoman.dist %>"
         ]

-  # ngmin tries to make the code safe for minification automatically by
-  # using the Angular long form for dependency injection. It doesn't work on
-  # things like resolve or inject so those have to be done manually.
-    ngmin:
+    ngAnnotate:
       dist:
         files: [
           expand: true
@@ -443,7 +440,7 @@ module.exports = (grunt) ->
     "concurrent:dist"
     "autoprefixer"
     "concat"
-    "ngmin"
+    "ngAnnotate"
     "copy:dist"
     "cdnify"
     "cssmin"

Karma-Jasmineの出力結果表示形式をRspec風にしたい

f:id:yuichi_katahira:20140827112945p:plain

そっけないです.....

karma-spec-reporterをインストールして設定する。

% npm install karma-spec-reporter -D
# karma.conf.coffee
  plugins: [
       'karma-phantomjs-launcher'
       'karma-jasmine'
       'karma-coffee-preprocessor'
+     'karma-spec-reporter'
       'karma-coverage'
     ]

-     reporters: ['progress','coverage']
+    reporters: ['spec','coverage']

f:id:yuichi_katahira:20140827113013p:plain

OK.

Railsのwrap_parametersは何をしてくれるのか?

Action Controller Overview — Ruby on Rails Guides
↑ここに書いてある。

config/initializers/wrap_parameters.rbはRails4.1だとデフォルトで多分こうなっている。↓

 Be sure to restart your server when you modify this file.

# This file contains settings for ActionController::ParamsWrapper which
# is enabled by default.

# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
ActiveSupport.on_load(:action_controller) do
  wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
end

# To enable root element in JSON for ActiveRecord objects.
# ActiveSupport.on_load(:active_record) do
#  self.include_root_in_json = true
# end

この設定のままの場合は要するに'application/json'でJSONRailsへ送った時にルート要素を省いて送れる。

//↓これが
{ "user": { "name": "katahirado", "address": "Kakyouin" } }
//↓こう送れる
{ "name": "katahirado", "address": "Kakyouin" }

↓Controllerへ送るとパラメータはこうなる

{ name: "katahirado", address: "Kakyouin", user: { name: "katahirado", address: "Kakyouin" }}

何が嬉しいのかというと、例えばAngularJSとかからJSONを送るときに$resourceで処理しやすい、でもって、Rails側では通常と変わらずUser.new(params[:user])と出来る*1、とかそういう感じで。

ちなみにController名にしたがってラップされるので、そこは注意が必要。
例えばDeviseのRegistrationsControllerの場合は、registration: {...}とラップされてる。

DeviseはJSON考慮に入れてないので、APIとかの用途の場合はSorceryとか、自前とか、oAuthの仕組みに乗るとかしたほうが良さ気。

*1:説明の為にStrongParametersを省いてます