Enumerable#each_with_object でハマった
[1, 2, 3].each_with_object(Array.new) do |i, memo| memo += [i, i * 2] end # => [ ]
あれ!?
[[1, 2], [2, 4], [3, 6]]
になると思ったのに!?
もうさんざん既出ですね!
Enumerable#each_with_object
は引数に対して破壊的な変更を加えなければダメなんですね。
つまり
[1, 2, 3].each_with_object(Array.new) do |i, memo| memo.push([i, i * 2]) end
のように破壊的なメソッドを利用すれば OK ですね。
もしくは今回のケースだと Enumerable#inject
を利用して、以下のようにすれば OK ですね。
[1, 2, 3].inject(Array.new) do |memo, i| memo + [i, i * 2] end
ハマったのでメモとして。
ブロック内でのみ有効な Redis のインスタンスを起動したい
最近は Ruby をちょくちょく使っています。
Ruby で Redis を使ったテストを書く場合は fakeredis や mock_redis を使うのが一般的かと思いますが、ちょっとだけ Fake や Mock ではなく本物の Redis を使いたい時ってありませんか?
例えば Redis やその周辺ライブラリの挙動をちょっと確認したい時とか、手元で Redis の RDB だけを仕込んでおきたい時とか…
ないですね。
と、とにかく、そんな要望が個人的にはあったので、Ruby の勉強を兼ねつつ Redispot
という Gem を作ってみました。
Redispot
は引数としてブロックを取り、そのブロック内でのみ有効な Redis のインスタンスを起動します。こんな感じ。
require 'redis' require 'redispot' redis = nil Redispot::Server.new do |connect_info| # このブロック内でのみ Redis が起動する redis = Redis.new(connect_info) redis.ping # => "PONG" end # ブロックの外なので無効 redis.ping # => raise Errno::ENOENT
上記例ではコンストラクタにブロックを渡して実行していますが、Redispot::Server#start
というメソッドもあり、こちらにブロックを渡すこともできます。
server = Redispot::Server.new server.start do |connect_info| # このブロック内でのみ Redis が起動する redis = Redis.new(connect_info) redis.set("key", "value") redis.save end server.start do |connect_info| # このブロックの Redis は上記とは別の新規インスタンス redis = Redis.new(connect_info) redis.get("key") # => nil end
コンストラクタに redis.conf
に指定したいパラメータを Hash で渡すことができるので、以下のように指定すれば、Redis の RDB を手元に残すこともできます。
config = { dir: File.expand_path('../', __FILE__), dbfilename: 'dump.rdb', } # Ruby のスクリプトと同じ場所に "dump.rdb" というファイルが残る Redispot::Server.new(config: config) do |connect_info| redis = Redis.new(connect_info) redis.set('key', 'value') redis.save # save を忘れると dump.rdb に保存されないよね end
スポットインスタンスのように「ちょっとだけ使う時のために」ということで Redispot と適当に名前をつけましたが、元ネタは @typester さんが書いて @songmuさんが現在メンテナンスをしている Perl の Test::RedisServer というモジュールです。
ということで、もし、もし、仮にちょっとだけ Redis インスタンスが使いたいなぁ。と思うことがあればご利用ください。
本当に PHP の DoS 脆弱性 (CVE-2015-4024) キツくない?
hakaikosen.hateblo.jp
上記記事を「あら大変(棒読み)」とか思いながら読んでいたけれど、PHP の BTS の方を読んでみたら確かに原理から再現手順まで細かく記載されていて
「なんかこれまずそう」と思ったので、docker を使って検証してみることに。
mp3 の ID3 Tag をファイルパスに基づいて更新する
アーティスト名/アルバム名/01-曲名.mp3
というファイルパスのルールで保存してある mp3 に対して、ファイルパスに基づいて適切な ID3 タグを一括で設定する Script を Ruby の taglib-ruby を使って書いてみました。
taglib-ruby は C++ で書かれた ID3 Tag 編集用のライブラリ TagLib の Ruby バインディングなので、別途 TagLib 本体をインストールする必要があります。
インストール方法は上記 taglib-ruby のページに OS 別に記載されていますので割愛。
あとは、bundler 用に以下のような Gemfile を書いてインストールすれば OK。
できあがったプログラムはこんな感じ。
あとは、対象のディレクトリを指定して (複数指定可) 実行するだけ。
$ ./updid3.rb /path/to/artist/album /other/artist ./music
これで指定したディレクトリ以下の拡張子が ".mp3" のファイルを探し出し、ファイルパスに基づいた ID3 Tag を設定します。
適当に --dry-run オプションも実装していて、これを渡すと ID3 Tag がどう設定されるかの表示だけを行い、実際の処理は行いません。
$ ./updid3.rb --dry-run /path/to/artist Artist: アーティスト名 Album: アルバム名 Title: 曲名 Track: 1 -------------------------------------------------- (...略...)
そんなわけで、無事に古い CD に対しても ID3 タグを埋め込むことができました。
やったね。
String#to_s と 文字列リテラル内の式展開と - その2
Float と Symbol についても気になったので追加してみました。
どれも予想通り Object#to_s
の方が "#{Object}"
の書き方よりも早いですね。
Float#to_s のコストは他と比べると比較的高いんですね。 といっても 100万回やってこの値なので、本当に気にする必要があるかはよく考えた方がいいですね。
追記
「Float が小数点を考えると1文字分多いんじゃないか?」と。
なるほどごもっとも。
ということで、1桁減らしてもう一度
そうね。早くなったね。ほんの少し。
ただの興味本位でやっていることなので、念のためもう一度。
100万回やってこの値なので、本当に気にする必要があるかはよく考えた方がいいですね。
本当に必要な人もいると思うけれどね。
桁数でどう変わるかもちょっと調べてみよっと。
String#to_s と 文字列リテラル内の式展開と
やっぱ、こういうものですよね。
String#to_s
(文字列を文字列にする) か Fixnum#to_s
(数値を文字列にする) かでも、当然だけれど違いは出るんですね。