lib/sakuramochi/predicate_builder.rb in sakuramochi-0.5.5 vs lib/sakuramochi/predicate_builder.rb in sakuramochi-0.5.6
- old
+ new
@@ -4,19 +4,62 @@
module Sakuramochi
module PredicateBuilder
extend ActiveSupport::Concern
included do
+ unless respond_to? :build
+ model_class = defined?(ActiveRecord::Model) ? ActiveRecord::Model : ActiveRecord::Base
+
+ def self.build(attribute, value)
+ case value
+ when ActiveRecord::Relation
+ value = value.select(value.klass.arel_table[value.klass.primary_key]) if value.select_values.empty?
+ attribute.in(value.arel.ast)
+ when Array, ActiveRecord::Associations::CollectionProxy
+ values = value.to_a.map {|x| x.is_a?(model_class) ? x.id : x}
+ ranges, values = values.partition {|v| v.is_a?(Range)}
+
+ values_predicate = if values.include?(nil)
+ values = values.compact
+
+ case values.length
+ when 0
+ attribute.eq(nil)
+ when 1
+ attribute.eq(values.first).or(attribute.eq(nil))
+ else
+ attribute.in(values).or(attribute.eq(nil))
+ end
+ else
+ attribute.in(values)
+ end
+
+ array_predicates = ranges.map { |range| attribute.in(range) }
+ array_predicates << values_predicate
+ array_predicates.inject { |composite, predicate| composite.or(predicate) }
+ when Range
+ attribute.in(value)
+ when model_class
+ attribute.eq(value.id)
+ when Class
+ # FIXME: I think we need to deprecate this behavior
+ attribute.eq(value.name)
+ else
+ attribute.eq(value)
+ end
+ end
+ end
+
instance_eval do
alias :build_from_hash_without_predicate :build_from_hash
alias :build_from_hash :build_from_hash_with_predicate
end
end
module ClassMethods
def build_from_hash_with_predicate(engine, attributes, default_table)
- predicates = attributes.map do |column, value|
+ attributes.map do |column, value|
table = default_table
if value.is_a?(Hash)
table = Arel::Table.new(column, engine)
build_from_hash_with_predicate(engine, value, table)
@@ -26,70 +69,33 @@
if column.include?('.')
table_name, column = column.split('.', 2)
table = Arel::Table.new(table_name, engine)
end
- column_name, predicate = Predicate.detect(column.to_s)
+ column_name, predicate = Sakuramochi::Predicate.detect(column.to_s)
attribute = table[column_name.to_sym]
if predicate
- build_attribute_with_predicate(attribute, value, predicate)
+ build_with_predicate(attribute, value, predicate)
else
- build_attribute(attribute, value)
+ build(attribute, value)
end
end
- end
-
- predicates.flatten.compact
+ end.flatten
end
private
- def build_attribute_with_predicate(attribute, value, predicate)
+ def build_with_predicate(attribute, value, predicate)
if predicate.validate(value)
if predicate.converter
attribute.send(predicate.arel_predicate, predicate.convert(value))
else
- relation = build_attribute(attribute, value)
+ relation = build(attribute, value)
attribute.send(predicate.arel_predicate, relation.right)
end
else
''
- end
- end
-
- def build_attribute(attribute, value)
- case value
- when ActiveRecord::Relation
- value = value.select(value.klass.arel_table[value.klass.primary_key]) if value.select_values.empty?
- attribute.in(value.arel.ast)
- when Array, ActiveRecord::Associations::CollectionProxy
- values = value.to_a.map {|x| x.is_a?(ActiveRecord::Base) ? x.id : x}
- ranges, values = values.partition {|v| v.is_a?(Range) || v.is_a?(Arel::Relation)}
-
- array_predicates = ranges.map {|range| attribute.in(range)}
-
- if values.include?(nil)
- values = values.compact
- if values.empty?
- array_predicates << attribute.eq(nil)
- else
- array_predicates << attribute.in(values.compact).or(attribute.eq(nil))
- end
- else
- array_predicates << attribute.in(values)
- end
-
- array_predicates.inject {|composite, predicate| composite.or(predicate)}
- when Range, Arel::Relation
- attribute.in(value)
- when ActiveRecord::Base
- attribute.eq(value.id)
- when Class
- # FIXME: I think we need to deprecate this behavior
- attribute.eq(value.name)
- else
- attribute.eq(value)
end
end
end
end