ActiverecordのためのCriteriaをつくる
Rubyクックブックのレシピを参照にして(半分真似して)、Activerecordのfindメソッド用のまいCriteriaをつくってみました。
クラスの実装
以下のような実装にしました。
ANDやORがネストしたものも対応できるよう、クライテリアがネストした構造をとれるよう実装しました。
# Activerecordのfindメソッドのに条件として渡す配列を生成するためのクライテリア class Criteria # 個々の条件を格納 class Condition attr_accessor :field, :value, :operation def initialize(field, value, operation) @field = field @value = value @operation = operation end end def initialize() @nested_criteria = nil @conditions = [] end public attr_reader :operation,:nested_criteria,:conditions protected attr_writer :operation public # 条件を追加,selfを返す def add(field, value = nil, operation = '=') @conditions << Condition.new(field, value, operation) self end # 他のクライテリアをANDで連結,selfを返す def and(criteria) @nested_criteria ||= [] criteria.operation = 'AND' @nested_criteria << criteria self end # 他のクライテリアをORで連結,selfを返す def or(criteria) @nested_criteria ||= [] criteria.operation = 'OR' @nested_criteria << criteria self end # findに渡す条件配列を返す。 # [sql文, 値, 値...]という形式の配列となる。 def to_where_clause sql = [] values = [] @conditions.each do |condition| if condition.value.is_a?(Array) then sql << '(' + condition.to_str + ')' elsif condition.value.nil? if condition.operation then sql << "#{condition.field} #{condition.operation} " end else sql << condition.to_str end values << condition.value if condition.value end sql = sql.join(' AND ') if nested_criteria then nested_criteria.each do |c| nested_where = c.to_where_clause logic_operation = c.operation sql = "( #{sql} ) #{logic_operation} #{nested_where.shift }" values += nested_where end end values.unshift(sql) end end
使い方
以下のメソッドを使います。
add(field, value, operatin)
条件を追加。テーブルのカラム(フィールド)名、カラム値、演算子(=,> ..など。省略すると=) を指定します。また、valueに配列を指定するとIN句を生成することになります。
and(criteira)
他のクライテリアとAND条件を作る。
or(criteria)
他のクライテリアとOR条件を作る。
to_where_clause
結果を配列で返す。0個目の要素が置換付きの条件句で1個目の要素が置換変数の値の配列です。
テスト
以下のようにして簡単なテストをしてみる。
criteria = Criteria.new criteria.add('sex', 'female' ) criteria.add('city', 'osaka', '=' ) criteria2 = Criteria.new criteria2.add('age', [21,22,23] , 'IN') criteria2.add('from', 'tokyo', '=' ) criteria3 = Criteria.new criteria3.add('sex' , nil, 'IS NULL' ) criteria3.add('city', nil, 'IS NOT NULL' ) criteria.and(criteria2).or(criteria3) p criteria.to_where_clause
でとりあえず、こんなかんじでできている。
["( ( sex = ? AND city = ? ) AND (age IN ?) AND from = ? ) OR sex IS NULL AND city IS NOT NULL ", "female", "osaka", [21, 22, 23], "tokyo"]
まだ、実際にActiverecordに適用していませんが..