なっく日報

技術やら生活やらのメモ

ESLintでファイルの中身によって設定ファイルを切り替える

yukidarake.hateblo.jp

の続きで、実際やってみたという話です。

雑なメモですがご容赦ください。

ESLint plugin作成

ここではeslint-plugin-fooとしておきましょうか。

package.jsonとindex.jsを作ります。

eslint-plugin-fooという文字列があれば、.eslintrc2.ymlを使うようにします。

ソース

'use strict';
var eslint = require('eslint');
var alternativeCLIEngine = new eslint.CLIEngine({
  configFile: './.eslintrc2.yml',
});

function preprocess(text, filename) {
  if (!/eslint-plugin-foo/.test(text)) {
    return [text];
  }

  var config = alternativeCLIEngine.getConfigForFile(filename);

  var eslintRules = '/*eslint ' + Object.keys(config.rules).map(function(rule) {
    return rule + ':' + JSON.stringify(config.rules[rule]);
  }).join(',') + '*/';

  return [eslintRules + text];
}

function postprocess(messages) {
  return messages[0];
}

module.exports = {
  processors: {
    '.js': {
      preprocess: preprocess,
      postprocess: postprocess,
    },
  },
};

こんな感じ。

https://github.com/yukidarake/eslint-plugin-foo

ソースは↑にあげました。

仕組み

ファイルのどこかにeslint-plugin-fooがあれば、行頭に/*eslint no-var:0*/みたいに付けるという超力技。

動かしてみる

必要なモジュールのインストール

使いたいファイルのあるディレクトリで

npm i eslint
npm i yukidarake/eslint-plugin-foo

してください。

.eslintrcを2つ用意

.eslintrc.yml

---
root: true
plugins:
  - foo
rules:
  no-var: 2

.eslintrc2.yml

---
root: true
rules:
  no-var: 0

実行

これで準備は整いました!

a.js

// eslint-plugin-foo
var a = 1;

というファイルを使って、コメント行の文字列を消したり、戻したりしながら実行してみてください。

./node_modules/.bin/eslint a.js

結果

/private/tmp/a.js
  2:1  error  Unexpected var, use let or const instead  no-var

✖ 1 problem (1 error, 0 warnings)

と出たり出なかったり・・・

ソース中のテキストの有無によって.eslintrc.ymlと.eslintrc2.ymlを切り替えできているようです。

まとめ

この方法をうまいこと使えば同じディレクトリ内でES5とES2015のJSファイルが混在していてもうまいことチェックできたりするのでは?

というアイデアでした。

もっと良い方法や指摘事項等あればぜひ教えてください・・・

SVNの.svnディレクトリを一発で消す

2016年にSVN!?

しかも、Subversion 1.7より前の話!?
(1.7以降はルートにのみ.svnディレクトリがあるので今回みたいなことは起きない)

というのはさておき、困っていたデザイナーさんがいたので救いました
(あと、特に目新しい技術ネタもなかったので。。)

現象

他のディレクトリをまるっとコピーしたことで余計な.svnディレクトリが付いてきて、新しく作ったはずのディレクトリがコミットができない。

A/.svn
A/dir1
A/dir1/.svn
A/dir1/file1
A/dir1/file2

↑みたいなAディレクトリをBディレクトリとしてコピーしたら、.svnまでコピーしちゃったという感じ。

対応

一緒にコピーしてしまった.svnディレクトリだけを消してやると。

ターミナルでサクッと。

find . -type d -name '.svn' | xargs rm -r

本当は

find . -type d -name '.svn' -print0 | xargs -0 rm -r

とやったほうが安全だけど今回は端折りました。

上記のfindとxargsのコマンドの各オプションの意味は↓の記事が解説がしっかりしているのでオススメです。

findとxargsの基本的な使い方 -- ぺけみさお

Ansibleのshellモジュールでヒアドキュメントが使いたいときー

が、あると思います。

shellモジュールでヒアドキュメントを使うと・・・

github.com

コチラのissueにコメントがありましたが、

- shell: |
    cat <<EOT
    a
    b
    EOT

の結果は

 a
 b

(半角スペースがaとbの前に入る)

という。。

大半のケースでは大丈夫でしょうが、困る場合もあります。

対策

Ansibleのコアディベロッパーが推奨しているらしい方法

scriptモジュールを使う。

http://docs.ansible.com/ansible/script_module.html

ローカルにあるファイルをリモートに転送して実行してくれます。

例えば、

roles/common/file/script.sh
roles/common/task/main.yml

な構成をとっていた場合、roles/common/task/main.ymlでは

- script: script.sh

みたいに使えます。

これならAnsible特有の問題で嵌ることもないでしょう。

ワークアラウンド

発想の転換に感心してしまったのですが、sedを使って空白を取り除きますw

- shell: |
    sed 's/^ //' <<EOT | cat
    a
    b
    EOT

これで普通にヒアドキュメントをcatしたのと同じになると。

まとめ

ま、ほんとうは冪等性確保のために、shellモジュールもscriptモジュールも使うなということなのでしょうが・・・

どなたかの参考になれば幸いです。

Google Cloud Vision APIを試してみた

画像を送ったら、画像の中身を解析して、顔とか物とかを判別してJSONで返してくれるというヤツです。

昼休みにざっくりと触ってみたのでメモ。

下準備

こちらのブログが詳しく、大変参考にさせていただいたのでみてください(雑)

syncer.jp

ざっくりいうと

  1. GCPのプロジェクトを作成
  2. API ManagerからCloud Vision APIを有効化
  3. API キーを作成

というくらい。

料金表によると、月1000リクエストまでは無料っぽいですが、クラウド破産が怖い場合、上記の記事を参考にアラートを飛ぶようにしたほうがよいでしょう(私はアラートが飛ぶようにしましたw)

プログラム

APIキーを付けたCloud Vision APIのURLにbase64エンコードした画像ファイルをJSON形式でPOSTするだけです。

ワンライナーでもいけますが、見づらいのでシェルスクリプトにしておきました。

今回使用した画像はこちら(私のネット上でのアイコンです。スクリプト中のyukidarake.jpeg

f:id:nakimura:20160511192234j:plain

なお、風景写真から物を認識させるとか、人物写真から顔を認識させるのはfeaturesに指定した項目で変わるよう。

今回はLABEL_DETECTIONにしてみました(顔っぽいので、FACE_DETECTIONにしたら結果は空だった・・・)

#!/bin/bash
set -ue

BODY=$(cat <<JSON
{
  "requests": {
    "image":{
      "content": "$(base64 yukidarake.jpeg)"
    },
    "features": [
      {
        "type": "LABEL_DETECTION",
        "maxResults": 20
      }
    ]
  }
}
JSON
)

echo $BODY | curl \
  -H "Accept: application/json" \
  -H "Content-type: application/json" \
  -X POST \
  -d @- \
  https://vision.googleapis.com/v1/images:annotate?key=API_KEY

実行すると・・・

./vision.sh

こんな結果が。

{
  "responses": [
    {
      "labelAnnotations": [
        {
          "mid": "/m/0j272k5",
          "description": "eyewear",
          "score": 0.95789468
        },
        {
          "mid": "/m/0h8jxfl",
          "description": "vision care",
          "score": 0.93333292
        },
        {
          "mid": "/m/0jyfg",
          "description": "glasses",
          "score": 0.89535505
        },
        {
          "mid": "/m/0c_jw",
          "description": "furniture",
          "score": 0.84084433
        },
        {
          "mid": "/m/0b_rs",
          "description": "swimming pool",
          "score": 0.82729644
        },
        {
          "mid": "/m/04f6rz",
          "description": "turquoise",
          "score": 0.82495272
        },
        {
          "mid": "/m/0h8lg0g",
          "description": "eye glass accessory",
          "score": 0.799998
        },
        {
          "mid": "/m/02_n6y",
          "description": "goggles",
          "score": 0.77193254
        },
        {
          "mid": "/m/02crq1",
          "description": "couch",
          "score": 0.62357479
        },
        {
          "mid": "/m/01mzpv",
          "description": "chair",
          "score": 0.59412205
        }
      ]
    }
  ]
}

メガネは認識できているよう。

swimming pool って・・・

青っぽい画像のシマシマがスイムレーンだと誤って検出されたのでしょう。

まとめ

あんま例がよくなかった気がしますが、いろんな可能性が感じられて楽しいです。

実は、他の画像ではもうちょいうまく検出できました(著作権が気になったので自分のアイコンにしてしまいました。。)

興味持った方は、ぜひ、一度お試しください。

ウチのチームでKPTが根付いた話

本日はあまりにもネタがないため、今所属しているチームでKPTが根付いた話をしたいと思います。

気づいたら1年くらい続いておりすっかりチームの文化になりました(これは本当に素晴らしいことだなと思ってます🌞)

KPTとは何か?

振り返りのフレームワークのことですが、詳しくはググッてくださいw

いろんな企業での事例もあるし、本もいくつか出ていますね。

ソニックガーデンの↓の記事や

kuranuki.sonicgarden.jp

クックパッドの↓の記事

techlife.cookpad.com

なんかは素晴らしいですね!

KPT導入が成功したポイント

ウチのチームで)導入成功の要因だと思われるモノをいくつかご紹介したいと思います。

KPTの本を買って、みんなで読んだ

チームに入った人には↓の本を読むようにしてもらっています。

わかりやすく、内容もコンパクトなのでオススメです。

毎週決まった日時に繰り返し開催

毎週火曜日、決まった時間に開催してます。

繰り返し決まった時間に行うと習慣化しやすいです。

ちなみに、月曜日は祝日でよく潰れてリズムが崩れるのでオススメしませんw

アナログにやる

当初、デジタルでやっていたのですが、上手くいかず、ホワイトボード+付箋+サインペンのアナログ運用に切り替えてから上手く回り始めた気がします。

司会はみんなで持ち回り

社内Wikiに進行手順をドキュメント化して、誰でも司会役ができるようにしました。

KPTの理解が深まる、KPTのミーティングの時間を自分事として見れるようになるという点で、上手く機能している気がします。

KPTの進行手順自体をKPTで改良

ちなみにKPTの進行手順は「これだけ!KPT」の本に書かれている内容をベースとして、KPTを繰り返す中でチームに馴染むように改良していきました。

だいぶ安定はしてますが、今でもたまに手順を変更することもあります。

たとえば、当初はスタンダードなKEEP,PROBLEM, TRYだけで進行していましたが、途中でTODOという項目を追加したりしています(TRYからやることを選択して、この項目に移動させる。「これだけ!KPT」によれば、KPT2というやり方だそう)

KPTを導入してどんなメリットがあった・・・?

感覚的にはかなりプラスになっていると思います。

  • チームで仕事を進めている感が出た
  • 非効率・無駄な仕事を見直すいい機会
  • チームの問題を指摘して正したりできることで精神的な支えになる(イライラしない)

というあたり。

最後に

KPTオススメです。

興味はあるけど、いまいち導入に踏み切れない人はぜひ↓の本を読んでみてくださいw

オライリー本を全冊購入するといくらなのか?(ワンライナーバージョン)

コチラを見て、頭の体操程度で。

qiita.com

こんなコマンドで

curl -s https://www.oreilly.co.jp/catalog/ | perl -lnE 's/.+"price">([\d,]+).+/$1/ && (tr/,//d, $c++, $s+=$_); END{ say "合計 $c冊 $s円"}'

結果

合計 445冊 1533060円

結構増えてますね。

Reflect.applyとFunction.prototype.applyの速度を計測してみた

ESLintのprefer-reflectというルールをONにした

最近、Node.js v6が出たのでバージョンアップがてら、ESLintのprefer-reflectというルールをONにしました。

そして、既存のソースに早速ESLintをかけたところ・・・

警告ががが

既存のソースに早速ESLintをかけたところ func.call(obj, a)みたいに書いている箇所で警告を受けました。

http://eslint.org/docs/rules/prefer-reflectによれば、下記の古いメソッドはReflect.xxxに置き換えられると。

Reflect.apply effectively deprecates Function.prototype.apply and Function.prototype.call
Reflect.deleteProperty effectively deprecates the delete keyword
Reflect.getOwnPropertyDescriptor effectively deprecates Object.getOwnPropertyDescriptor
Reflect.getPrototypeOf effectively deprecates Object.getPrototypeOf
Reflect.setPrototypeOf effectively deprecates Object.setPrototypeOf
Reflect.preventExtensions effectively deprecates Object.preventExtensions

FunctionのcallapplyReflect.applyに書き換えなさいとのこと。

ESLintのルールに従い、素直に書き換えよう!とは思ったのですが、速度が激遅にならないかが気になりました。

そこで超ざっくり計測

console.timeにてざっくりと。

'use strict';

function log(i) {
    return String(i);
}

for (let n = 1; n <= 10; n++) {
  console.log(`------------${n}回目-----------`);

  console.time('Function.apply');
  for (let i = 0; i < 100000; i++) {
      log.apply(log, [i]);
  }
  console.timeEnd('Function.apply');

  console.time('Reflect.apply');
  for (let i = 0; i < 100000; i++) {
      Reflect.apply(log, undefined, [i]);
  }
  console.timeEnd('Reflect.apply');
}

結果。

------------1回目-----------
Function.apply: 13.736ms
Reflect.apply: 11.770ms
------------2回目-----------
Function.apply: 13.979ms
Reflect.apply: 8.198ms
------------3回目-----------
Function.apply: 10.134ms
Reflect.apply: 9.899ms
------------4回目-----------
Function.apply: 9.800ms
Reflect.apply: 11.548ms
------------5回目-----------
Function.apply: 11.939ms
Reflect.apply: 7.979ms
------------6回目-----------
Function.apply: 8.048ms
Reflect.apply: 8.330ms
------------7回目-----------
Function.apply: 11.209ms
Reflect.apply: 12.215ms
------------8回目-----------
Function.apply: 11.397ms
Reflect.apply: 8.974ms
------------9回目-----------
Function.apply: 9.948ms
Reflect.apply: 10.000ms
------------10回目-----------
Function.apply: 8.923ms
Reflect.apply: 11.215ms

結論

遅いということはなさそうなので、安心して使えそう。