composite_primary_keysのバグ修正で思ったこと。
見たくもないレガシーDBにアクセスする用事があるんですよこれが。(あー触りたくない)
railsでは使うことのない複合プライマリキーが使われてたので
これに対応するためにプラグインのcomposite_primary_keysを使いました。
ただちょいとバグがありで、rails2.1から追加されたdirtyでの差分アップデートに対応してませんでした。なのでモンキーパッチを書いて修正しました。
複合プライマリキーに対応するためdb更新時のメソッドActiveRecord::Base#updateを再定義してるんですが問題が2点
1・差分更新に必要な引数をとってなかった。
恐らくrais1の時代から変えてなかったのかもしれません。
2・update_without_callbacksを再定義していたんですが、その前にalias_method_chainでupdate_without_dirtyやupdate_without_lockがあるのでそれらの機能が使えなってる様子。
とりあえず調べたところではupdate_without_lockが最初のaliasっぽいのでそちらを再定義するように変更。
この2番を対応してて気づいたのがalias_method_chainを複数回した時の一番最初のオリジナルのメソッドを再定義したいときってどうやれば綺麗に実装できるの?と思いました。aliasの元をたどれる仕組みがあったらいいなと思いました。
修正パッチ
module CompositePrimaryKeys module ActiveRecord #:nodoc: module Base #:nodoc: module CompositeInstanceMethods remove_method :update_without_callbacks def update_without_lock(attribute_names = @attributes.keys) quoted_attributes = attributes_with_quotes(false, false, attribute_names) return 0 if quoted_attributes.empty? where_clause_terms = [self.class.primary_key, quoted_id].transpose.map do |pair| "(#{connection.quote_column_name(pair[0])} = #{pair[1]})" end where_clause = where_clause_terms.join(" AND ") connection.update( "UPDATE #{self.class.quoted_table_name} " + "SET #{quoted_comma_pair_list(connection, quoted_attributes)} " + "WHERE #{where_clause}", "#{self.class.name} Update" ) return true end end end end end
オリジナル
def update_without_callbacks where_clause_terms = [self.class.primary_key, quoted_id].transpose.map do |pair| "(#{connection.quote_column_name(pair[0])} = #{pair[1]})" end where_clause = where_clause_terms.join(" AND ") connection.update( "UPDATE #{self.class.quoted_table_name} " + "SET #{quoted_comma_pair_list(connection, attributes_with_quotes(false))} " + "WHERE #{where_clause}", "#{self.class.name} Update" ) return true end