Ruby、スーパークラスにメソッドが定義されている時だけsuperしたい

あけましておめでとうございます!!!!!!!!!!!!!

表題の機能はinheritedメソッドが定義されてるモジュール複数includeする時などに欲しくなるわけですが、

def inherited(klass)
  super if defined?(super)
end

などとすることで実現できる。

defined?(super)はメソッド呼び出しではなくて太古からある謎の構文です(ref: http://docs.ruby-lang.org/ja/1.9.3/doc/spec=2fdef.html#defined)

Rubyでトポロジカルソートする

トポロジカルソート - Wikipedia

依存関係を定義したグラフを元に処理順決めるときに使ったりするあれです、あれ。
gitのコミットオブジェクト(複数の親を持つ可能性がある)を並び替える必要があったので調べた。

Rubyには tsortというライブラリが標準添付されているので便利。1.7あたりからある模様。

トポロジカルソートには

  • ノードの集合を取得する
  • あるノードが繋がっている先のノードを取得する

というデータ構造に依存する処理が必要で、それを用意してやればtsortがうまいことやってくれる。

require 'tsort'

class Nodes
  include TSort
  def tsort_each_node(&block)
    # ノードを列挙してblockに渡す処理を実装する
  end
  def tsort_each_child(node, &block)
    # node が繋がっている先のノードを列挙してblockに渡す処理を実装する
  end
end

nodes = Nodes.new(...)
nodes.tsort # トポロジカルソートされたノードの配列を取得する

みたいな使い方です。

ActiveRecord、STIする時のクラス決定ルールを自由に決める

前提

実装

クラスの決定はActiveRecord::Inheritance#instantiate(record)内で行ってるので、そこにパッチ当てればいける。


ただ、クラスの決定はロード時にしか行われないわけで、動的に変わるカラムを元にしたSTIを定義すると意味不明状態になるというリスクがある。typeカラムに格納されたクラス名のみを元にするっていうARの設計は妥当なのかも。

Ruby、method_addeみたいに定数が定義されたときフックしたい

けど無理っぽいので本体改造してみた

Module#const_addedを定義しておいて、定数定義する関数の本体が rb_const_set(VALUE klass, ID id, VALUE val) なので、そこに細工して定数セット後に const_added を呼んでやればOK。
ただしrb_const_setRuby起動時のクラス階層初期化時にも呼ばれるので、その際はスルーする必要がある。

というわけでこうするとよさそうだった。
https://github.com/todesking/ruby/commit/ff4112340a149faf18cbe288fee02a419f801bcc

class A
  def self.const_added(id)
    puts "#{self}::#{id} added"
  end
end

A::X = 10
#=> "A::X added"

Rubyの改造、autoconfしてconfigureしてmakeすれば環境整うし、ctagsとvimのタグジャンプがあればだいたいコード追えるのでわりと参入障壁低いかんじだった。皆さんもやってみるといいですよ。

Rails、ActiveRecord+mysql2、SQL実行時にwarning出たらエラーにする

MySQLのwarningって、

  • 文字列が長すぎたから勝手に短くして保存しておいたよ!
  • 数字が大きすぎたから適当な数字を保存しておいたよ!!
  • 数値として解釈できない文字列があったから0とみなして比較したよ!!!

など、無視すると死ぬ系メッセージであることが多いんだけど無視されがちなので困り者。


insert系のwarningについてはsql_modeの設定を変えることでエラーにできるんだけど、その設定がなされてなかったり、selectがヤバイみたいなケースもあったりしてな(;´Д`)


というわけで、ActiveRecord側でチェックするモンキーパッチを書きました(for mysql2)。

1クエリ実行ごとにshow warning投げてるので本番にはおすすめしない。
Mysql2::Client、リリースバージョンにはwarning_countがないので……。HEAD使うか、次バージョンを待ちましょう。