動かざることバグの如し

近づきたいよ 君の理想に

dockerで特定のログをひたすらtailし続けるコンテナを作る

環境

  • docker-compose

やりたいこと

いや、大したことじゃないんだけど、docker logでは各プロセスの標準出力が流れる。で、自前のアプリケーションのlogを流したかった話

コード

  image: "alpine:3.8"
  command: /bin/sh -c "mkdir -p /app/rails && touch /app/rails/log/server.log && tail -F /app/rails/log/server.log"
  volumes:
    - "./:/app/rails"

すると /app/rails/log/server.log のログがdocker logに流れる。td-agent使えよ

Rails開発をもっと楽しくする便利メソッド集

この記事はRuby on Rails Advent Calendar 2018の7日目です。

はじめに

今回は知らなくても生きていけるけど、知っておいたほうがお得で便利なRailsメソッドを紹介していく。RailsといいつつActiveSupportやんけ

squish

冒頭と末尾のホワイトスペースを削除して、連続したホワイトスペースを1つだけになるように整形してくれる。

" hoge \n \t piyo   fuga\n".squish
=> "hoge piyo fuga"

try

知ってる人は常識だけど未だに知らない人多い。例えば以下のようにpostに紐づくuserのlogin.idを有れば取得する場合、

post = Post.last
post.user.present? && post.user.login.present? && post.user.login.id

以下のように短くできる。

post = Post.last
post.try(:user).try(:login).try(:id)

presence

便利なのに、present? に比べて presence知名度が低い印象。

nilかどうかチェックしてnilならAを、違うならそのまま返したいときに

object.present? ? object : 'nanasi'

と書くところを簡単にできる。ぜひ使ってほしい。

name.presence || 'nanasi'

presence_in

これも present? に比べて知名度低い。引数のリスト中にあればそれ自身を返す。なければnil いわゆるホワイトリストに使える。

'admin'.presence_in %w(admin staff viewer)
=> "admin"
'invalid'.presence_in %w(admin staff viewer)
=> nil

all_week, all_month, all_year

例えば今月の初日から末尾までをrangeで欲しい場合、普通に書くと

Date.today.beginning_of_month..Date.today.end_of_month

となるが、all_month で一発

Date.today.all_month
=> Sat, 01 Dec 2018..Mon, 31 Dec 2018

とできる。 他にも all_year も便利。

Date.today.all_week
=> Mon, 03 Dec 2018..Sun, 09 Dec 2018
Date.today.all_year
=> Mon, 01 Jan 2018..Mon, 31 Dec 2018
Date.today.all_year.count
=> 365 # 当然w

all_day

all_day はその日まるごとの時間をrangeで取得できる。

Date.today.all_day
=> Wed, 05 Dec 2018 00:00:00 JST +09:00..Wed, 05 Dec 2018 23:59:59 JST +09:00

to_s(:db)

さっきの all_*** と組み合わせると強い。

(Date.today..Date.tomorrow).to_s(:db)
=> "BETWEEN '2018-12-05' AND '2018-12-06'"

overlaps?

2つのrangeに対して、重複している部分があるかをチェックする。

(1..10).overlaps?(3..6)
=> true
(1..10).overlaps?(11..20)
=> false

rangeに対して使えるので、日付とかでもOK。

(Date.new(2018,12,1)..Date.new(2018,12,10)).overlaps?(Date.new(2018,12,3)..Date.new(2018,12,6))
=> true

Node.jsでコマンドライン引数処理を行うならcommand-line-argsがよさげ

最近だとコマンドラインツールはGolangで書かれることが多くなっていたが、Nodejsでコマンドラインで処理を受け付けたい、ということも全然あると思う。

そのときに必ず当たる壁がコマンドライン引数の処理である。

引数のパース、必須項目の扱い、ヘルプの表示等々自前で実装するのは色々ツラい。

そこでargvを使ってる人も多いと思う。が、最終更新が6年前でそれ以降一切メンテがされていない。。。流石に不安になる。

ってことで代役を探していたら、「command-line-args」というよさげなライブラリを見つけた。

インストール

yarn add command-line-args

or

npm install command-line-args

サンプル

const commandLineArgs = require('command-line-args');

const optionDefinitions = [
  {
    name: 'verbose',
    alias: 'v',
    type: Boolean
  },
  {
    name: 'src',
    type: String,
  },
  {
    name: 'timeout',
    alias: 't',
    type: Number,
    defaultValue: 3
  }
];
const options = commandLineArgs(optionDefinitions);

console.log(options);

で、実行するとoptionsに値が入っている

$node index.js --src image.png -t 10
{ src: 'image.png', timeout: 10 }

あとは公式ドキュメントを見ればなんとかなる(投げやりのスタイル

デフォルトオプションを定義したい

defaultValueを使う。さっきの例だと、timeoutのdefaultValueを3にしたのでtimeoutを指定しないで実行すると、

node index.js --src image.png
{ timeout: 3, src: 'image.png' }

となる。

1つのオプションで複数受け付けられるようにしてほしい

multipleをつける。さっきの例でいうと

{
  name: 'src',
  type: String,
  multiple: true
},

にすると

node index.js --src image1.png --src image2.png
{ timeout: 3, src: [ 'image1.png', 'image2.png' ] }

とできる。逆にmultiple指定指定してない状態で複数指定すると、 ALREADY_SET: Singular option already set [src=image1.png] のように例外エラーになる。

ヘルプ(usage)を表示したい

command-line-argsの特徴的な点として、それ自体には表示機能は実装されていない点。

じゃあどうすんのって話だが同じ作者のcommand-line-usageを入れればおk

yarn add command-line-usage

さっきの例をベースに改良したバージョンが以下

const commandLineArgs = require('command-line-args');
const commandLineUsage = require('command-line-usage');

const optionDefinitions = [
  {
    name: 'verbose',
    alias: 'v',
    type: Boolean
  },
  {
    name: 'src',
    type: String,
    defaultOption: true,
    multiple: true,
    description: 'file path'
  },
  {
    name: 'timeout',
    alias: 't',
    type: Number,
    defaultValue: 3,
    description: 'convert timeout'
  },
  {
    name: 'help',
    alias: 'h',
    type: Boolean,
    description: 'show help'
  }
];

const sections = [
  {
    header: 'Sample app',
    content: 'this is sample app for command-line-args'
  },
  {
    header: 'Options',
    optionList: optionDefinitions
  }
];

const options = commandLineArgs(optionDefinitions);
if(options.help) {
  const usage = commandLineUsage(sections);
  console.log(usage);
  process.exit(0);
}

console.log(options);

すると以下のようなヘルプが出来上がる。

~/tmp/node_options $node index.js --help

Sample app

  this is sample app for command-line-args

Options

  -v, --verbose
  --src string[]         file path
  -t, --timeout number   convert timeout
  -h, --help             show help

こっちに関しても結構カスタマイズできるので詳しくは公式ドキュメントを見てほしい。

バリデーション機能をつけたい

自分で実装しろ

と、いうのもcommand-line-args自体がそもそも対応してないし、対応する気もない。その理由は公式ドキュメントに書かれており、

Because this module specialises in one task - finding and extracting values set against defined options in process.argv.

要は昔は対応してた時期もあったけど、対応し始めるとキリなくてメンテできへんしつけねぇって話らしい。

IDCFクラウドを300円運用する

メモになってしまうが、

  • マシンタイプを一番安い「Light.S1」で選択
  • イメージをおすすめ Template ではなく、 ISOタブにある任意のISOを選択
    • 今回はUbuntu 18.04を選択したがうまくいった
  • ディスクを5GBにする

これでマシン=200円、ディスク=100円で300円で国内にサーバーを持てる。最強か

ただデメリットはテンプレートによるインストールではないのでインストールはされていない

自分でIDCFのWEBコンソールに入ってUbuntuのインストールする必要がある。

その他

任意のイメージをアップして使うこともできるらしい。以下はSnappy Ubuntu Coreでインストールした例

参考リンク

シミュレーター使っても最低15GBしか指定できなかったし、この方法裏技だと思ったけど結構これについて書いてる記事が多い。結構有名な話だったのか。。。

Chrome拡張機能で特定のHeaderのときだけブロックする方法

Chrome拡張機能はすごくて、chrome.webRequestのイベントを駆使すると特定の条件のURLをブロックしたり、リダイレクトすることができる

以下はexample.comを含む画像を白紙にするコード 画像かどうかはtypesの「images」で見てる

var pattern = "https://example.com*";

function redirect(details) {
  console.log('blocked', details.url);
  return {cancel: true};
}

chrome.webRequest.onBeforeRequest.addListener(
  redirect,
  {urls:[pattern], types:["image"]},
  ["blocking"]
);

が、↑のようにchrome.webRequest.onBeforeRequest.addListener()は実はURLしか条件に入れることが出来ない だからヘッダーを見て〜のときはブロック、といったことができない

解決策

Headerを見れるchrome.webRequest.onHeadersReceived.addListener()を使う

以下はサイズが1MB以下のJPEG画像をブロックする例

'use strict';
function blockImages(details) {
  var responseHeaders = details.responseHeaders;
  var flag = 0;
  for (var i = 0; i < responseHeaders.length; i++) {
    if (responseHeaders[i].name.toLowerCase() == 'content-length') {
      if(responseHeaders[i].value < 100000) {
        flag++;
      }
    }
  }
  for (var i = 0; i < responseHeaders.length; i++) {
    if (responseHeaders[i].name.toLowerCase() == 'content-type') {
      if(responseHeaders[i].value == 'image/jpeg') {
        flag++;
      }
    }
  }
  if(flag == 2) {
    console.log('blocked', details.url);
    return {cancel: true};
  } else {
    return { responseHeaders: responseHeaders };
  }
}

chrome.webRequest.onHeadersReceived.addListener(
  blockImages,
  {urls: ['https://example.com*']},
  ['responseHeaders', 'blocking']
);

なんでわざわざfor文で回しているかって話だが、responseHeadersはヘッダー名前がそのままKeyになってくれないので何番目かわからない。よって回すしかない。。。

ちなみに監視対象URLをすべてにする場合は

urls: ['<all_urls>'],

とすればいい