読者です 読者をやめる 読者になる 読者になる

のらねこの気まま暮らし

Perlについてとか、創作についてとか、発展途上の自分と向き合う記録。

指定期間でDOMを出し分けてくれるangular-periodを公開しました

angular-periodとは

AngularJS のDirectiveで、期間を指定するとその 期間前期間中期間後でDOMの表示を切り替えてるようにするものです。

例えば、なんかの応募とかで、以下の様なHTMLをテンプレートエンジンやJavaScriptなどを使って出し分けることがある時に便利です。

angularJSを使った一番お手軽なケースとしてはこんなかんじでしょうか。

angular.module("campaign", [])
  .controller('CampaignCtrl", function () {
    this.now   = new Date().getTime();
    this.start = new Date('2015-08-01T00:00:00').getTime();
    this.end   = new Date('2015-08-31T23:59:59').getTime();
  });
<div class="campaign" ng-controller="Campaign as campaign">
  <!-- 期間前の表示 -->
  <div class="form is-previous" ng-if="campaign.now < campaign.start">
    <p>○月☓日から応募できます</p>
  <!-- 期間中の表示 -->
  <form class="form" ng-if="start <= campaign.now && campaign.now <= campaign.end">
    <button type="submit">応募する!</button>
  </form>
  <!-- 期間後の表示 -->
  <div class="form is-after" ng-if"campaign.end < campaign.now">
    <p>応募は締め切りました</p>
  </div>
</div>

angular-periodを使えば、以下のように書けます。

angular.module('campaign', ['angularPeriod']);
<div class="campaign" ng-period
  ng-period-start="'2015-08-01T00:00:00'"
  ng-period-end  ="'2015-08-31T23:59:59'">
  <!-- 期間前の表示 -->
  <div class="form is-previous" ng-period-when="previous">
    <p>○月☓日から応募できます</p>
  <!-- 期間中の表示 -->
  <form class="form" ng-period-when="during>
    <button type="submit">応募する!</button>
  </form>
  <!-- 期間後の表示 -->
  <div class="form is-after" ng-period-when="after">
    <p>応募は締め切りました</p>
  </div>
</div>

開始と終了を渡すと、その期間の前、中、後のHTMLを選択してDOM Treeに組み込みます。 最も単純なケースではコントローラーすら不要なので、ロジックやテストを大幅に削減できます。

また、angular-periodはリアルタイムな反映及び、setTimeoutの上限値である32bit intを超える値もサポートできるようにしているので、長期にわたる期間であってもある程度は許容されます。

インストール

npm, bowerともに公開しているので必要な方を利用してください。

npm install angular-period
bower install angular-period

日付の指定とパースについて

利用可能な値

上記には文字フォーマットを用いた例を記載しました。 ただ、実際にプログラムする上で文字列だけでは様々な不便なことがあるでしょう。

ngPeriodは Date オブジェクトに変換可能な値をサポートしています。

具体的には、 - Dateパース可能な文字列 - Date オブジェクト - momentjsなどのDateオブジェクトに渡せるオブジェクト

先ほどの例で、例えばmomentjsを使う場合は以下の様に書けます。

angular.module('campaign', ['angularPeriod'])
  .controller('CampaignCtrl", function () {
    this.start = moment('2015-08-01 00:00:00');
    this.end   = moment('2015-08-31 23:59:59');
  });
<body  ng-controller="Campaign as campaign">
<div class="campaign" ng-period
  ng-period-start="campaign.start'"
  ng-period-end  ="campaign.end">
  <!-- 期間前の表示 -->
  <div class="form is-previous" ng-period-when="previous">
    <p>○月☓日から応募できます</p>
  <!-- 期間中の表示 -->
  <form class="form" ng-period-when="during>
    <button type="submit">応募する!</button>
  </form>
  <!-- 期間後の表示 -->
  <div class="form is-after" ng-period-when="after">
    <p>応募は締め切りました</p>
  </div>
</div>
</body>

至ってシンプル。

パースできない値

Dateオブジェクトでパース可能な値はブラウザによって異なります。 パースできない値を渡すと、Dateは Invalid Date というオブジェクトを返すので、 それをそのままDateとして扱おうとすると、Nanだったり不適切な値になりえます。

困るケースでは、ChromeSafariで利用可能なDateのフォーマットが異なるという点です。

ChromeYYYY-MM-DD HH:mm:ssという値をサポートしていますが、SafariではInvalid Dateが返ってきます。 これは失念すると非常に気づきにくくリスキーで、常にChromeSafariでの動作確認を要求されるため大変不便です。

angular-periodでは、Invalid Date になるような値を渡された場合その時点でErrorオブジェクトをthrowするようにはしています。 JavaScriptのDateオブジェクトは非常に複雑かつブラウザ間で挙動が違うので、それを吸収するよりは、すでに実装され安定的に運用されている他のモジュール(momentjsのような)を利用することを推奨します。

まとめ

というわけで結構便利なモジュールを書いたつもりで居るのでどうぞお試しあれ。

node-hariko にクリパラメータによってモックデータを割り振る機能を入れた

欲しかった機能をいれたかったので、 hariko を v1.1.0 に更新しました。

更新内容としては、複数のリクエストをExamplesに設定している時に、リクエストパラメータがマッチするやつを優先的にレスポンスさせるような機能を追加しました。

multipe example

API-Blueprintでは一つのアクションに複数のレスポンスを記載できます。

# GET /messages{?page}

+ Request /messages (application/json)

+ Response 200 (application/json)

    パラメータが無いときは `page=1` としてデータを返す

    + Body

             [
               /* message arrays */
             ]

+ Request /messages?page=2 (application/json)

+ Response 200 (application/json)

    + Body

             [
               /* message arrays */
             ]

+ Request /messages?page=9999 (application/json)

+ Response 200 (application/json)

    存在しないページをしたら空の配列(`[]`)を返す

    + Body

             []

モックサーバのレスポンスとの兼ね合い

複数のExampleを定義をした時、リクエストとレスポンスは一つのActionの中に配列で格納されるんだけど、drakov ではキーのマッチングで優先順位を決めてマッチングしてたけど、そんなことよりキーと値が一致したらそいつを優先的に返して欲しい。

上記の例だと

|GET /message|ひとつめ| |GET /message?page=1|ふたつめ| |GET /message?page=2|ふたつめ| |GET /message?page=9999|ふたつめ|

みたいに帰ってきて、9999にリクエストしたら空のレスポンスが帰ってこない!!みたいになる。 いくら、json-outputの機能があるとはいえ、毎回毎回書き換えるのはだるくて仕方ないし、ドキュメント上で明示しておきたい。

クエリパラメータのでマッチングして完全一致したやつを優先的に出力する

node-hariko/resource_spec.js at master · rymizuki/node-hariko · GitHub

こんな感じで。 クエリパラメータの値がMarkdown上で定義されて居て、かつ一致するものがあればそれをレスポンス、 一致するものが無かったら先頭で定義しているものをレスポンスという形にした。

|GET /message|ひとつめ| |GET /message?page=1|ひとつめ| |GET /message?page=2|ふたつめ| |GET /message?page=9999|みっつめ|

を返してくれるようにした。

まとめ

  • リクエストとレスポンスを密に連携してよりデバッグしやすい環境を作りました!

todo

  • warningsをいまちゃんと出力してないので教えてあげてほしい。
    • できれば、コードを示して「どこでwarningsでているのか」をもっとわかりやすくしたい

Introduction to Hariko API Server with API-Blueprint

はい、harikoの紹介です。

harikoとは

hariko

harikoとはNodeJS製のAPI MockServerであり、APIドキュメンテーションツールであるAPI Blueprintのエコシステムです。

使い方

npmからインストールして下さい。

npm install -g hariko

harikoはAPI-Blueprintのドキュメントをベースにレスポンスを返すAPI Serverを立ち上げるので、 まずはAPI-Blueprintの仕様に沿ったmarkdownが必要です。

# GET /api/message
+ Response 200 (text/plain)

        hello world

適当に、 docs/api/message.md とかに保存します。

harikoのサーバを立ち上げます。 CLIから以下のように打ち込んで下さい。

hariko -f 'docs/api/*.md'
[INFO] Running Hariko Server ... http://localhost:3000

とshellがレスポンスしたらサーバの実行完了です。 ブラウザとかから http://localhost/api/message を叩いてみてください。 hello world と表示されるはずです。

harikoの機能たち

詳細はREADME.md とか見て下さいねって感じなんですけど、便利!!!と思って作った機能を幾つか紹介しませう。

markdownファイルの一部除外

drakovになくて地味に不便だったので入れた。 APIサーバに載せたくない一部ファイルを任意で除外するやつ。

glob形式で読み込む奴はブラックリストで弾ける用にしてほしい気持ち。

hariko -f 'docs/**/*.md'\
       --exclude 'docs/metadata.md'\
       --exclude 'docs/overview.md'

watch

ファイルを監視して、更新したらサーバを再読み込みします。 これは gaze をそのまま使ってます。gruntやgulpやらで使ってる奴。

gruntやらgulpやらのplugin書くときにいちいちreload書くの面倒でしょ?って思ったので。

hariko -f 'docs/**/*.md' -w

proxy

API-Blueprintに定義されていないパスにリクエストが飛んできたら、 指定したサーバにプロキシするオプション。

stubcellの全部のAPIリソースを一斉に移管するのが辛かったので、逐次移行できるようにしたいが為に用意したの。

application → hariko server → stubcell

hariko -f 'docs/**/*.md' --proxy 'http://localhost:8100'

output

これ!!!まじべんりだから!!!べんりだから!!!! 指定したディレクトリにレスポンスデータを吐き出すオプション。

stubcellやeasymockみたいにJSONファイルを弄れば次のレスポンスから結果が変わるという、手動アプリケーションのための仕組み。 リロードしたら全部リセットされるので注意ね♪

hariko -f 'docs/**/*.md' -o 'api/'

verbose

ログレベルをverboseに設定します。 リクエストのデータとか、その辺のデバッグに使うといいかも?

ログ周りは自前だけどそれなりに気を配って書いた気持ち。

hariko -f 'docs/**/*.md' -v

harikoを作った背景

もともと、いまいる会社でSPA (Single Page Application) の開発で、UIファーストを実践していたのですが、どうにもAPIドキュメンテーションが不足し、サーバとフロントエンドでやりとりの齟齬や過去のリソースについての確認があったり、まあ、ドキュメント欲しいよねって感じでした。(主に僕が

過去に easymock を使っていたのですが、ドキュメントの記法が独特で、メンテナンスもしばらくされていなかったので次のプロジェクトからは、 stubcell を使うようになりました。

どちらのツールも、JSONファイルをレスポンスとして返却してくれるツールだったんですが、 stubcellはJSON5に対応しているし、レスポンス内容を細かく設定できるしで好んで使っていました。

でもさすがに、JSON5だからといってjsonファイルにコメントアウトドキュメンテーションするのは読みやすいとはいえない状況...

その当時(大体2015年の2月あたり?)からAPI-Blueprintの存在は知っていて、でもドキュメントMarkdownとは言いつつも学習コスト高そうだなぁ、エコシステムもどうなんだろうなぁと思っていたのでした。

時は経ち、「やっぱりドキュメント欲しいね」ってなって、改めてAPI-Blueprintを検証したのでした。

改めて調べてみるとエコシステムはなかなか充実しており、aglio なんかは美しいドキュメントを生成してくれて素晴らしいなぁと思っていたので、ついでに drakov というAPI Mock Serverがあるそうなのでそれも一緒にインストしました。

やーべんりべんり、と思っていたのはつかの間。 drakovを使っていた上で幾つか不便な点が出てきました。

  • mdファイルを監視してサーバをリロードしてほしい
  • mdファイルを一部除外したい
  • 検証用にうん万行のJSONデータを返すようにしたい
  • 存在しないAPIはstubcellで立てたAPIにproxyしてほしい

stubcellやらeasymockやらでやっていたファイルを書き換えたら次のリクエストで反映されたJSONが帰ってくるっていう状況は実に効率的だった...

という要望が積み重なってわっと作ったのが hariko というわけです。

AngularJS x Webpack を使った構成の紹介

AngularJSとWebpackを上手いこと連携させてメンテナンスしやそうな構成を試行錯誤したので、 現状の記録と整理を兼ねて記事にしてみる。

結構いいかんじなアプローチなのではないかと思いつつ、まだ詰め切れていないところも多いので今後ブラッシュアップを重ねていきたい。

関連技術

要点

  • 役割でディレクトリを分ける
  • 汎用化出来そうな仕組みはアプリケーションの外に置く
  • DDDを意識してビジネスロジックを構築する
  • Webpackのローダーを活用する
  • Webpackのrequireをそこそこに活用する
  • $injectorを活用する
  • Angular UI-Routerの設定をもとにViewのディレクトリを作る

役割でディレクトリを分ける

AngularJSだと controller, service, config, directive のように機能でディレクトリを分けるのがスタンダードのようだけど、メンテナンスをしているとあっちいったりこっちいったりで結構面倒臭かったり、考えをごっちゃにしがちであまり使い勝手がよくなかった。

そんなわけでJSの役割を幾つかに別けて、それごとにディレクトリを切るようにしてみた。

  • app/config
    • アプリケーションの設定
  • app/domains
    • ビジネスロジックかつ見た目にかかわらない情報
    • DDDに影響を受けていたのでドメインと呼称している
    • 名前はともかく、controllerの肥大化を避けられる
  • app/views
    • 見た目に関する部分、 controllertemplate を内包する
    • view-modelとdomainを controller で連携させる
  • 汎用モジュール

汎用化出来そうな仕組みはアプリケーションの外に置く

モーダルやローディングなど汎用的に使え、アプリケーションに依存するものを設定に切り出せるようなモジュールは基本的に app (アプリケーションの本体)とは別のディレクトリにおく。

外出ししやすくなるし、管理を別けれるので一度FIXすれば後は気にしなくていい。

ディレクティブやView周りのproviderはだいたい汎用化出来そうなので、適当なプリフィクスを振って、 xx-modal みたいな感じでディレクトリを切ってそこに置くようにしている。

DDDを意識してビジネスロジックを組む

DDDというか、意識していることは以下。

  • 単一責務の原則
    • 一つのクラスが持つ役割はひとつ
    • 複数持つようなら分ける
  • Aggregateパターン
    • 複数のユーザストーリーを一つのクラスに集約することでViewから参照するクラスを制限する
    • しかし責務は分かれてるようにしているので相互影響は最小。最悪クラスの差し替えも想定してる
  • Repositoryパターン
    • APIやStorageへのアクセスを抽象化するようにしている
    • $resourceを使うことが多いけど、時には$localStorageなども使いたい
    • ビジネスロジック的にはどっちも同じように扱いたいので、localStorageを一段wrapして$resourceと同じような振る舞いをするようにしていたりする
    • 中途半端感あるのでこれはブラッシュアップしていきたいところ

いままであまりJavaScriptOOPを意識してこなかったのだけど、単一責務の原則をしっかりまもって、I/Fをある程度制約かければ結構取り回しが効いて良さそうな感じはする。 ただ、どのくらいの規模までハンドルできるかはちょっと見えてないので、今後の発展を見たい。

Webpackのローダーを活用する

CoffeeScriptコンパイルはもちろんだが、何より嬉しいのはHTMLをJavaScriptの文字列として展開できること。 これによって、 grunt-ng-template のようなライブラリは不要になったし、遷移のたびにXHRを投げる必要もなくなった。

UI-Routerと連携しつつ、templateファイルを require することで文字列をそのままわたせる。 ついでに、viewsのディレクトリの中で controllertemplateを並べせることができて、「controllertemplateはセットで編集したいのにディレクトリの移動面倒!」「ディレクトリ構造違くてわけわかんない」みたいな問題から開放された。

HTMLだけでなく、JSONYAMLのローダーを使うことで設定などの読み込みも容易になるのでなにかと便利になる。

Webpackのrequireをそこそこに活用する

webpackの拡張requireはあんまり使っていない。 index化したり拡張子の省略が主だけど、これが思いの外使い勝手が良かった。

Webpackのextensionsオプションに拡張子を登録しておけば、以下の様に拡張子を省略してかいた場合、 app.(js|coffee) あるいは app/index.(js|coffee) を探しにいってくれる。

var app = require("app");

設定的には、extensions: ["", "coffee"] とか登録しとくとCoffeeScriptも読んでくれるので便利。

indexファイルはファイルの機能によって何を書くかをわけている。

View系

exportするのはController。ユーザストーリーの入り口的な意味合いを持たせている。 DDDのAggregate的な扱いをしている雰囲気。雰囲気なのであまり厳密にやってない。

例としては、以下の様な感じ。

  • ユーザのサマリを出すページは user/index.js
  • ユーザの所持品を出すページは user/friend.js
  • ヘルプのページは help/index.js

モジュール系

angularの拡張としてproviderやfactoryを提供する場合。 ドメイン系はだいたいproviderとして提供しているし、汎用的なクラスだったりするとfactoryを提供する。

indexファイルでproviderやfactoryを定義し、別のファイルにクラスの定義をしている。

例として、ユーザクラスと、ユーザが持ってる友達のクラスを書くとしたら以下の様な構成にする。

+- domains/
  +- user/
    +- index.js
    +- user.js
    +- friend.js

$injectorを活用する

domainsなんかはクラスをたくさん作って、providerでインスタンス化している。

愚直にかくと以下のような書き方になる。

angular.module(module.exports = "user-domain", []).provider ()->
  User = require "./user"
  return {
    '$get': -> new User()
  }
  

requireで呼び出すことはできるけど、例えば $resource$q を使いたいときが結構ある。 呼び出すにしても $injector 呼ばなければならない。

$getでDIして、コンストラクタに渡す?でも無駄なプロパティは抱えたくない。

そこで、$injector.invoke を採用した。

こいつを使うと、以下のようにかける。

index.js:

angular.module(module.exports = "user-domain", []).provider () ->
  return {
    '$get': ($injector) ->
      new ($injector.invoke(require "./user"))
  }

user.js:

module.exports = ($resource) ->
  class User
    # Userクラスの定義

DIもクラス側に書けてメンテナンスがしやすくなった。

さらに、$injector.instantiateを使って、パラメータのバリデーションをしつつインスタンスを生成するようにする。

index.js:

angular.module(module.exports = "user-domain", []).provider () ->
  return {
    '$get': ($injector) ->
      $injector.instantiate($injector.invoke(require "./user"), {})
  }

$injector.instantiate を使うとコンストラクタに渡る値が存在しないときにErrorを投げてくれるので、「うっかり引数変え忘れた!」とかに気付ける。

若干鬱陶しいという気はしつつ、たくさんコードを書いているとどうしても漏れがでてくるのでエラーを投げてくれるのはありがたい。

Angular UI-Routerの設定をもとにViewのディレクトリを作る

state の構造と views のディレクトリ構造が合致するのが望ましい。

こうするとファイルをソースを見なくても探しにいけるし、なによりファイル名を決めるときに考えることが少ない。

templateとcontrollerを同じディレクトリにおいているので考える手間が一つになる。

$stateProvider
  .state "app",
    abstruct: true
    controller: require "app/views/app/index.coffee"
    template:   require "app/views/app/index.html"
  .state "app.user",
    url: "^/user/"
    controller: require "app/views/app/user/index.coffee"
    template:   require "app/views/app/user/index.html"
  .state "app.user.friend",
    url: "^/user/friend"
    controller: require "app/views/app/user/friend.coffee"
    template:   require "app/views/app/user/friend.html"

ディレクトリ構成

というわけで、大体以下の様な感じにディレクトリが出来上がる。

+- scripts
  +- xx-modal/
  +- xx-loading/
  +- xx-preload/
  +- app/
    +- config
    | +- router.coffee
    | +- resource.coffee
    | +- xx-modal.coffee
    +- domains/
    | +- user/
    | | +- user.coffee
    | | +- index.coffee
    | +- friend/
    +- views/
    +- app/        
    |  +- contents/
    |  |  +- header/
    |  |  +- index.coffee
    |  |  +- index.html
    |  |+- index.coffee
    |  +- index.html
    +- index.coffee

まとめ

AngularJS と Webpackを連携させる事例を紹介しました。 組み合わせて色々便利で管理も楽になっていいよ!

NodeJSでSystemJSを使ってES6を試す

NodeJS ES6

SystemJSとは、ES6やAMD等の汎用的なモジュールシステム。 SystemJSを使うとどんなふうにロードされるのかを試した。

インストール

npm install --save systemjs

基本的な使い方

良い感じローディングするためには初期設定が必要。 require で呼び出すんだけど、うーん・・・ coffee-script.register()だと思えばまぁ・・・

importの返り値はpromiseなので、exportしている場合は、 .then で受け取れる。

index.js:

var System = require("systemjs");

System.import("./lib/example"); // ./lib/example.js を読んでくれる

ベースのパスを指定して ./lib の指定をなくす

baseURLにURLを指定することでそこを基点にファイルを探査してくれるらしい。 windowsではパスの先頭に file: の指定が必要とのこと。

index.js:

var path   = require("path");
var System = require("systemjs");
System.config({
  baseURL: path.resolve("./lib");
});
System.import("example");

node_modulesの読み込み

これまじかよって思った。

index.js

var path   = require("path");
var System = require("systemjs");
System.config({
  baseURL: path.resolve("./lib");
});
System.map['lodash'] = "../node_modules/lodash/index"; // `./lib` からの相対パス
System.import("example");

lib/example.js

import _ from "lodash"; // 相対パスを指定するか、mapに登録する必要がある様子
// any script

まとめ

SystemJSを使ったES6の簡単なサンプルの紹介でした。 npmライブラリはおとなしく require 使いつつ自作ライブラリに関してはimport使うとかするのがいいのかな。

issuesとか眺めながら様子みたい。

EC6のimport及びexportを使いたい

ES6 NodeJS

jspm関連でEC6を使おうとしたけど、moduleの管理の仕方いろいろあってどうすればいいのかわからなかったので調べた。 SystemJSを使ってブラウザでもEC6のコードを利用できます。

SystemJSのI/Fで利用

ファイルをimportする

<script src="system.js"></script>
<script>
System.import("runtime-hoge");
</script>

ファイルをimportして処理を行う

<scirpt src="system.js"></script>
<script>
System.import("promise-hoge").then((hoge) => {
  // exportした`hoge`が引数に渡される
  console.log(hoge);
});
</script>

pollyfillを使う

<scirpt src="system.js"></script>
<script type="module">
import {hoge} from 'hoge';
console.log(hoge);
</script>

Polyfillを使ったmoduleシステムの利用

名前付きでエクスポート

fuga.js:

export var fuga = 'hoge';

index.js:

import {fuga} from 'fuga';
console.log(fuga); // hoge

名前付きでエクスポート

fuga.js:

var fuga = 'hoge';
export {fuga};

index.js:

import {fuga} from 'fuga';
console.log(fuga); // hoge

名前を指定しないでエクスポート

fuga.js:

export default var fuga = 'hoge';

index.js:

import fuga from 'fuga';
console.log(fuga); // hoge

エクスポートしないでcodeだけ実行する

fuga.js:

console.log('fuga');

index.js:

import 'fuga'; // fuga

grunt-contrib-connectのmiddlewareの順序による挙動について

grunt NodeJS

grunt-contrib-connectgrunt-connect-proxystubcellを設定したが POSTDELETE のリクエストが method not allowed で弾かれてしまって嵌ったのでメモ。

middlewareの設定順序が不適切

最初に正しい方法。middlewaresの先頭にproxyを挟む必要がある。

connect: {
  server: {
    options: {
      base: ["htdocs"],
      middleware: (connect, options, middlewares) ->
        middlewares.unshift(require("grunt-connect-proxy/lib/utils").proxyRequest);
        return middlewares;
      }
    },
    proxies: [
       // ... proxyの設定 ...
    ]
  }
}

middlewares.unshiftする必要性

引数で middlewares を受け取った時、connect.staticconnect.directoryIndex のmiddlewareがデフォルトで設定され、それらは GET , HEAD, OPTIONS のMethodしか許可していないため。

今回の僕のような POSTDELETE のようなRESTな感じのリクエストを投げたい場合にこれらよりあとにmiddlewareを設定してしまうとプロキシに到達する前に method not allowed となる。

middlewares.unshift のように先頭に middlewareを追加するか、 引数で middlewares を取らず、空の配列から新しくmiddlewaresを生成してしまうかするのが良いと思う。

背景

grunt-contrib-connect で静的ファイル(htmlとかjsとか)を配信し、RESTFulなAPIサーバーのモックとして stubcell を立てて、 grunt-connect-proxy でプロキシしてアプリ開発を行っていた。

が、proxyしているRESTFulなサーバに DELETEPOST を投げても 405 のレスポンスが返ってくる。

その時のGruntfileはこんな感じで、middlewaresにproxyRequestをpushしていた。 この時、middlewaresの配列が空の配列であるという錯覚が僕の3時間を無駄にした。

connect: {
  server: {
    options: {
      base: ["htdocs"],
      middleware: (connect, options, middlewares) ->
        middlewares.push(require("grunt-connect-proxy/lib/utils").proxyRequest);
        return middlewares;
      }
    },
    proxies: [
       // ... proxyの設定 ...
    ]
  }
}

調べた過程

ChromeのdevToolsを見ると、allowのヘッダが某かで付与されていた。 とりあえずmiddlewaresにallowを突っ込むヘッダを付与してみる。

moddleware.push(function (req, res, next) {
  res.setHeader('allow', 'GET, DELETE');
  next();
});

しかし通らない。

stubcellやproxiesのソースを読み、console.logを直で仕込みどこまでリクエストが伝播しているのかを調べているうちに、そもそも grunt-connect-proxy にリクエストが届いていないことが判明。

grunt-contrib-connect の某かで405を投げてるっぽい。 grunt-contrib-connect の中の node-connect のコードを読むが、 405 を投げている箇所が見当たらない。

検討がまったくつかないけれど、とりあえず障碍の範囲は絞れたので、grunt-contrib-connect の nextの実装に console.log をかまして様子を見る。

その過程で、どうやら、middlewareがproxyの前に幾つか処理されているらしいことがわかる。

gruntfile上で console.log(middleware) するとすでに配列にstaticとdirectoryIndexのmiddlewareが入っているではないか!

push辞めてunshiftして解決。