There's an echo in my head

日々のメモ。

`ps -ef | grep [f]oo`というやつ

調べては忘れるのでメモ。

grep [f]ooって結局grep fooと同じなのになんだこれはと思ってたけど、psはgrep自身のプロセスも返してくる(ことがある)ので、

  • ps -ef | grep foo だと foo を探しているうちに grep foo というプロセス自身にマッチしてしまう
  • ps -ef | grep [f]oo も同じように foo を探すけどgrepのプロセスは grep [f]oo なのでマッチしない

ということらしい。

ちなみに[ ]は角括弧、大括弧、square bracketらしい。< >は同じブラケットでも山括弧というんだとか。 cf. 括弧 - Wikipedia

自分は初めて触ったサーバがSolarisなので手癖ではps -efを打ってしまう。やっぱりps auxのほうが世には多いんだろうか。

innobackupex --move-backでファイルの移動

xtrabackupからファイルを復元するとき、ネットの記事とかPerconaの記事とかを見ると--copy-backを目にすることが多いんだけど、コピーするほどのディスク領域などない…!という場合には--move-backを使えばいい。

innobackupex --move-back /path/to/BACKUP-DIR

これならファイルの移動をするだけなので、バックアップファイルの展開ぶんと復元後のデータ領域とで重複してディスクを消費することがなくなる。

なお上記コマンドを実行したあとも展開した場所には中間ファイル?が残るので、それらは削除しても問題ない。

rm -rf /path/to/BACKUP-DIR

備考

調べてる過程で古いバージョンだとログファイルが移動されないという不具合があったような記述を見かけたけど、今回使用した2.3.6では発生しなかった。

rubotyをデーモンとして動かす

Herokuで動かしてたりdockerでデプロイしてたりするとあまり旨味はないのかもしれないけど、普通にcapistranoでデプロイして動かしたいようなときもあるのでやってみた。

下記のスクリプトを例えばyour_ruboty/lib/daemonize.rbみたいな場所に置いておく。

# nochdir: true   - 相対パスでファイルの読み込みをするプラグインがあったときに
#                   不具合の出ないように
# noclose: false  - capistranoでデプロイしたときに制御端末を切り離すために
Process.daemon(true, false)

# killしやすいようにPIDを書き出す
require "fileutils"
pid = File.expand_path("/tmp/ruboty.pid", __FILE__)
FileUtils.mkdir_p(File.dirname(pid))
File.open(pid, "w") { |f| f.write Process.pid }

そして起動時に--loadオプションで上記のスクリプトを指定して読み込んでやればデーモンとして動いてくれる。

$ bundle exec ruboty --dotenv --load lib/daemonize.rb

killしたいときはPIDファイルを見て適当にシグナルを送ってやればいい。

$ kill -TERM $(cat /tmp/ruboty.pid)

追記

PIDファイルデーモン化のオプションがそれぞれ本体に取り込まれたので、追々上記のようなことは不要になる。

追々記

ruboty 1.3.0から--daemonオプションと--pid <path>オプションが導入された。

routes.rbでワイルドカードに引っ掛けたパスを別のサブドメインにリダイレクトする

http://aerial.st/archive/...に来たアクセスを一律にhttp://archive.aerial.st/archive/...にリダイレクトするようにした。

Rails.application.routes.draw do
  # (snip)
  get "archive/*path", to: redirect(subdomain: "archive", path: "/archive/%{path}")
end

Redirect /archive/* accesses to archive subdomain by a2ikm · Pull Request #10 · a2ikm/aerial.st · GitHub

%{foo}params[:foo]の値が取れるみたい。

ちなみにデフォルトだと"301 Moved Parmanently"になるらしく、特定のステータスコードを指定する場合はstatus: 302とかあわせて指定してくれとのこと。

参考

acts_like?でduck typing

1年ぐらい前に社内ブログにメモしてた内容をまんま転載。 はて、これはduck typingなのか?

概要

Object#acts_like?を使うとクラスの異なるオブジェクトが同じ振る舞いをするかどうかの判定が手軽にできるようになる。

具体例

ActiveSupportではTimeとDateTime、ActiveSupport::TimeWithZoneに共通のメソッドを実装し、それらが入れ替わってもうまく動くようになっている。 これを実装するにあたってにTimeっぽく振る舞うかどうかを判定するためにacts_like?を使っている。

require "active_support/all"

# Timeっぽく動くやつら
Time.now.acts_like?(:time) #=> true
DateTime.now.acts_like?(:time) #=> true
Time.zone.now.acts_like?(:time) #=> true

# Timeじゃないやつ
Date.today.acts_like?(:time) #=> false

仕組み

すごく単純で、acts_like_time?が実装されていればacts_like?(:time)がtrueになる。

require "active_support/core_ext/object/acts_like"

class Bakeneko
  def acts_like_human?
    true
  end
end

Bakeneko.new.acts_like?(:human) #=> true

ちなみに実装されているかだけを見ているので、その返り値は見られない。 でもわかりやすさのためにもtrueを返すのが良いと思う。

使いドコロ

例えばSimpleDelegatorの継承と組み合わせると便利。

通常、SimpleDelegatorを継承したクラスはデリゲート先のクラスとはis_aの関係にならない。

require "delegate"

class Neko; end
class Bakeneko < SimpleDelegator; end

neko = Neko.new
bakeneko = Bakeneko.new(neko)

neko.is_a?(Neko) #=> true
bakeneko.is_a?(Neko) #=> false

とはいえここでいうbakenekonekoと同じように振る舞うことができるのだから、それを判別したい。 ここでacts_like?の出番。

require "active_support/core_ext/object/acts_like"

class Neko
  def acts_like_neko?
    true
  end
end

class Bakeneko < SimpleDelegator
  # SimpleDelegator側がオリジナル側のrespond_to?も見てくれるので
  # 実際はこのケースでは別途定義する必要はない
  def acts_like_neko?
    true
  end
end

neko.acts_like?(:neko) #=> true
bakeneko.acts_like?(:neko) #=> true

このようにすることで、acts_like?を使うことで異なるクラスでも同じ振る舞いをするオブジェクトの判定ができるようになる。

それ以外で言うと

ひとつのクラスが複数の振る舞いを持つ場合にも対応できる。 例えばDateTimeはDateのようにもTimeのようにも振る舞える。

DateTime.now.acts_like?(:date) #=> true
DateTime.now.acts_like?(:time) #=> true

振る舞いの判定がクラスとは別個に行えるので自由度が増える。 (とはいえ全く違う役割をもたせるのはクラス設計的にどうなのという感じではある)

FAQ

acts_like_xxx?を直接呼べばよいのでは

レシーバに実際にそのメソッドが定義されていない場合にエラーになるのでNG。

require "active_support/all"

Date.today.acts_like_time? #=> NoMethodError

一方でacts_like?は内部的にrespond_to?を呼んでいるだけなので、上記のような心配をする必要が無い。

knife-soloでControlMasterが煩わしかったのでなんとかした

knife-solo v0.5.0から

  1. 最初はrootでknife solo bootstrap my.host.jp -x rootする
  2. 続けてそのときに作ったユーザsome_userknife solo cook my.host.jp -x some_userする

としたときにエラーが発生するようになった。

原因はknife-solo v0.5.0でSSHのControlMasterがサポートされるようになったことで、これがあると最初にsshしたときのコネクションをホストごとに使いまわし続けてしまうためらしい。

対応方法としては3つあって、

1. --ssh-control-masterオプションを指定する

knife-soloを叩くときに指定できる。

$ knife solo cook my.host.jp --ssh-control-master no

ただ毎回は面倒くさいので設定でなんとかしたい。

2. ControlMasterを無効化する

~/.ssh/configに次のように記載する。

Host *
  ControlMaster no

ただ無効にしちゃうとControlMasterの恩恵を受けられなくなるので、それはそれで避けたい。

3. ControlPersistでを設定する

OpenSSH 5.9から入ったControlPersistでコネクションのタイムアウトを設定できるようになったとのことなので、~/.ssh/configに次のように記載する。

Host *
  ControlPersist 10 # 10秒で切る

ちなみに手元のsshのバージョンは次のコマンドで調べられる。

$ ssh -V 
OpenSSH_6.9p1, LibreSSL 2.1.8

参考

このブログに出てくるコードスニペッツは、引用あるいは断りがない限りMITライセンスです。