読者です 読者をやめる 読者になる 読者になる

odeの開発メモ日記

プログラマーやってます。今までの道のり ポケコンbasic→C(DirectX5 ネットやろうぜ)→Perl(ちょろっと)→Java→C#→Ruby→Android(Java)

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

実行環境

ruby (1.8.6)
rails (2.3.5)
composite_primary_keys (2.3.5.1)