ActiveRecord で実行される sql を確認する
ActiveRecord で実行される sql を確認したい場合、 #to_sql
メソッドが利用できます。
# このリレーションでレコードを読み込む場合に実行される SQL を文字列で取得する p Blog.joins(:articles).where(articles: { name: "hoge" }).to_sql # => "SELECT \"blogs\".* FROM \"blogs\" INNER JOIN \"articles\" ON \"articles\".\"blog_id\" = \"blogs\".\"id\" WHERE \"articles\".\"name\" = 'hoge'"
また、 puts
で出力する場合は "
はエスケープされないのでより読みやすくなります。
puts Blog.joins(:articles).where(articles: { name: "hoge" }).to_sql # => SELECT "blogs".* FROM "blogs" INNER JOIN "articles" ON "articles"."blog_id" = "blogs"."id" WHERE "articles"."name" = 'hoge'
ActiveSupport::TimeWithZone#+ に数値を渡したときの挙動
ActiveSupport::TimeWithZone#+
に値を渡すと『渡した時間を加算した時刻』を返します。
# 現在の時刻 current_time = Time.current pp current_time # => Mon, 06 Jan 2020 20:10:15 JST +09:00 # + 1日 pp current_time + 1.days # => Tue, 07 Jan 2020 20:10:15 JST +09:00 # + 10秒 pp current_time + 10.seconds # => Mon, 06 Jan 2020 20:10:25 JST +09:00
ActiveSupport::TimeWithZone#+
に数値を渡したときの挙動
ActiveSupport::TimeWithZone#+
に数値を渡した場合、数値を秒数として計算します。
# 現在の時刻 current_time = Time.current pp current_time # => Mon, 06 Jan 2020 20:11:28 JST +09:00 # + 10.seconds と同じ意味 pp current_time + 10 # => Mon, 06 Jan 2020 20:11:38 JST +09:00
これ、意図としてわかりづらいので数値はエラーにしてしまったほうがいいんじゃないですかねー。
Vim 8.2 で Vim script に -> 演算子が追加された
Vim 8.2 で Vim script に -> 演算子が追加されました。
これは f(x, y)
を x->f(y)
という風に書くための演算子になります。
例えば
echo map(filter(range(1, 10), { -> v:val % 2 == 0 }), { -> v:val * v:val })
というようなネストしている関数呼び出しを
echo 1 \ ->range(10) \ ->filter({ -> v:val % 2 == 0 }) \ ->map({ -> v:val * v:val })
のように記述する事ができます。
これはユーザ定義関数でも利用する事ができます。
function! s:plus(a, b) return a:a + a:b endfunction echo 1->s:plus(2)->s:plus(3)
便利。
注意
組み込み関数によっては f(x, y)
が x->f(y)
ではなくて y->f(x)
になる場合があるので注意する必要があります。
例えば printf(fmt, a)
は a->printf(fmt)
となります。
echo 42->printf("%04x") " output: 002a
関数によって ->
の挙動が異なるの、マジで頭がおかしいと思うのでどうにかしてほしい…なんでこんなひどい仕様にしてしまったのか…。
こういうのはちゃんと統一性を保ってほしいんですがどうにかなりませんかね…。
やっぱり Vim script はクソ
Vim 8.2 で Vim script の辞書のキーの定義が簡略化できるようになった
Vim script で辞書を定義する場合、次のようにキーを文字列リテラルで定義していました。
let dict = { "one" : 1, "two" : 2, "three" : 3 } echo dict " => {'one': 1, 'two': 2, 'three': 3}
Vim 8.2 では上記のような定義を以下のように定義する事ができます。
" #{ から始めることで `キー名: 値` で定義できる let dict = #{ one: 1, two: 2, three: 3 } echo dict " => {'one': 1, 'two': 2, 'three': 3}
こんな感じで辞書リテラルを #{
から始めることで キー名: 値
で定義できるようになります。
キー名には ASCII文字 + 数字 + -
+ _
が使用できます。
let dict = #{ one: 1, two_key: 1, three-key: 2, 444: 4 } echo dict " => {'one': 1, 'three-key': 2, '444': 4, 'two_key': 1}
また 444: 4
とした場合、キーは "444"
になるので注意する必要があります。
これ自体は便利なんですがなぜ #{}
なんていうクソダサいリテラルにしてしまったのか…。もっといい書き方はなかったんですかねえ。
C++20 で指示付き初期化子を使ったキーワード引数
C++20 で指示付き初期化子が実装されました。
指示付き初期化子はクラスを初期化する時に『任意のメンバ変数』を指定して初期化する事ができます。
struct pos{ int x; int y; int z; }; // .メンバ変数名 = で初期化できる pos p{ .x = 1, .y = 2, .z = 3 };
これを利用してキーワード引数っぽい関数を定義してみました。
#include <iostream> const auto disp = [] { // 引数を受け取るクラスを定義しておく // クラス定義を公開したくないので致し方なくクロージャ化 struct pos_t { int x; int y; int z = 0; }; return [](pos_t pos) { std::cout << "x = " << pos.x << std::endl; std::cout << "y = " << pos.y << std::endl; std::cout << "z = " << pos.z << std::endl; }; }(); int main(){ // 指示付き初期化子を利用してキーワード引数っぽく渡す disp({ .x = 1, .y = 2 }); std::cout << "---" << std::endl; disp({ .x = 1, .z = 3 }); return 0; } /* output: x = 1 y = 2 z = 0 --- x = 1 y = 0 z = 3 */
引数として受け取るクラスを定義しておいて、そのクラスに対して指示付き初期化子で値を渡すようにしています。
本当は
auto disp(struct { int x; int y; int z = 0; } pos) { // ... }
みたいに引数で直接匿名クラスが定義できればいいんですが、こういう書き方はできないので仕方なくラムダのスコープ内でクラスを定義するようにしています。
あと指示付き初期化子の制限で『指示子の順序が宣言の順序と一致しない』とエラーになるので次のようにもかけません。
// Error: 指示子の順序が宣言の順序と一致しない disp({ .y = 2, .x = 1 });
うーんキーワード引数で順序によってエラーになってしまうのはちょっと微妙ですねえ。
C++ で気持ちよくキーワード引数をかける日は来るのだろうか…。
参照
Vim 8.2 で Vim script に :const が追加された
Vim 8.2 で Vim script に :const
が追加されました。
これは変数を定義する :let
と同じように使用し、定義した変数を不変にします。
例えば
const x = 42
は
let x = 42 lockvar 1 x
と同等になります( lockvar
の 1
は depth
の指定
これにより const
を使うことで不変となる変数を簡単に定義することができます。
" 以下と同等 " let x = 10 " lockvar 1 x const x = 10 " Error: 再代入不可 " let x = 42
また、 :unlet
や :unlockvar
を使うことで変数に再代入や再定義することができます。
const x = 10 " unlet や unlockvar すると再代入可能 unlet x const x = 42
ちなみに暗黙的に :lockvar
の depth
に 1
が設定されているので辞書の場合は要素に対して再代入可能です。
const data = { "x" : 10 } " NG: 再代入不可 " const data = {} " OK: 再代入可能 let data.x = 42
詳しくは :help lockvar
を参照してください。
今まで :lockvar
するのが手間だったので使ってなかったんですが、これからは :const
を使うようにするとよさそうですね。
Vim 8.2 で Vim script にヒアドキュメントが追加された
Vim 8.2 で Vim script でヒアドキュメントがかけるになりました。
使い方は以下の通り
" END ~ END の間のテキストを行ごとのリストとして返す let s:text =<< END if cond echo "homu" endif END echo s:text " => [' if cond', ' echo "homu"', ' endif'] " trim をつけると先頭のインデントを削除する let s:text2 =<< trim END if cond echo "homu" endif END echo s:text2 " => ['if cond', ' echo "homu"', 'endif']
今までは無理やり文字列をリストで定義して…みたいなことをやっていたんですが、これだと複数行の文字列を定義しやすいですね。
ただし、文字列ではなくてリストを返す点に注意する必要があります。
なんで文字列じゃなくてリストを返すんだろうかこれ。