Rails4をJSON APIとして構築していてCreateのAPIに関連のID一覧をparameterとしてPOSTしてはまった

前提

1.Railsのwrap_parametersはJSONRailsへ送った時にルート要素を省いてくれる。というか、省いて送ってもよしなにwrapしてくれる。

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

2.has_manyを定義するとrelation_ids,relation_ids=というメソッドが使えるようになる

#group.rb
Class Group
  has_many :groups_users
  has_many :users, through: :groups_users
end
#user.rb
class User
  has_many :groups_users
  has_many :groups, through: :groups_users
end
#irb
group = Group.first
group.user_ids
# =>[1,2,3]
group.user_ids=[2,3,4]

3.Railsのstrong_parametersは許可されたパラメータ以外を取り除く

#groups_controller.rb
class GroupsController < ApplicationController
  def create
    @group = Group.new(group_params)
    # 省略
  end

  private
  def group_params
    params.require(:group).permit(:name, user_ids: [])
  end
end

問題

このgroups#createに対して以下のようなJSONを送付したところ、groupモデルは作成されたが、userとの関連は作成されなかった。

// json
{
  name: "新規グループ",
  user_ids: [1,2,3]
}

user_idsがstrong_parametesに書いたにもかかわらず弾かれる。

実際のparamsを見るとこんな感じ

 Parameters: {"name"=>"新規グループ", "user_ids"=>[1,2,3], "group"=>{"name"=>"新規グループ"}}

調べるとどうもnested_attributes_forも引っかかるようだ。

解決

strong_paramtersのREADMEに書いてあった。

以下のようにrequire(:group)を取り除く

# groups_controller.rb
class GroupsController < ApplicationController
  # 省略

  private
  def group_params
    params.permit(:name, user_ids: [])
  end
end

これで、wrap_parametersもきき、groupとuserの関連も作成される。

grunt-wiredep使用の環境でjquery-uiのdatepickerのロケールとCSSを含みたい

grunt-wiredep使用の環境でAngularJSのui-dateロケール指定したい&jquery-uiのCSSを当てたいと。

bower.jsonに追加してbower install

bower.json

{
  "dependencies": {
    "angular-ui-date": "latest"
  }
}

ui-dateがjquery-uiに依存しているので、jquery-uiも入る。

jquery-uiの構成を確認すると、ロケールはui/i18n/datepicker-ja.jsに、CSSのテーマはthemes/にある。

なので、プロジェクトのbower.jsonでmainをoverrideして、ロケールファイルとCSSを含める。

下記はsmoothnessにしてみた例。

bower.json

 "jquery-ui": {
      "main": [
        "jquery-ui.js",
        "ui/i18n/datepicker-ja.js",
        "themes/smoothness/jquery-ui.css"
      ]
    }

grunt serveとか実行するとこうなる↓

<!-- index.html -->

  <!-- 省略 -->

  <!-- build:css(.) styles/vendor.css -->
  <!-- bower:css -->
  <link rel="stylesheet" href="bower_components/jquery-ui/themes/smoothness/jquery-ui.css" />
  <!-- endbower -->
  <!-- endbuild -->

  <!-- 省略 -->

  <!-- build:js(.) scripts/vendor.js -->
  <!-- bower:js -->

  <!-- 省略 -->
  <script src="bower_components/jquery-ui/jquery-ui.js"></script>
  <script src="bower_components/jquery-ui/ui/i18n/datepicker-ja.js"></script>
  <script src="bower_components/angular-ui-date/src/date.js"></script>
  <!-- endbower -->
  <!-- endbuild -->

あとはrunあたりで設定でOK

#app.coffee
myapp.run(->
  $.datepicker.setDefaults($.datepicker.regional[ "ja" ])
)

buildして製品用に吐き出す場合に、cssの読むimageも処理しなければならないが、grunt-contrib-copyなどでよしなにすればOK。

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環境下でのgrunt watchでファイル監視の反映に時間がかかるので、webstormの自動保存を無効にした

↓ こちらのとおりです。
WebStorm/PhpStorm メモ - 自動保存を停止したい - {u:b}

うーん、あまり変わらない気がする。別のところに原因がありそう。