Permalink
Browse files

Refactored Query in a massive way. Now have CriteriaHash and OptionsH…

…ash which do all the dirty work. Query just has an instance of each of these.
  • Loading branch information...
1 parent 9aea956 commit 4c7ddbee6699c32d38d482796078415e8658c52b @jnunemaker jnunemaker committed May 12, 2010
View
@@ -3,6 +3,7 @@
require 'plucky/support'
module Plucky
+ autoload :OptionsHash, 'plucky/options_hash'
autoload :CriteriaHash, 'plucky/criteria_hash'
autoload :Query, 'plucky/query'
View
@@ -1,27 +1,113 @@
module Plucky
class CriteriaHash
- attr_reader :hash
+ attr_reader :source
- def initialize(hash)
- @hash = hash
+ def initialize(hash={}, options={})
+ @source, @options = {}, options
+ hash.each { |key, value| self[key] = value }
+ end
+
+ def []=(key, value)
+ normalized_key = normalized_key(key)
+ if key.is_a?(SymbolOperator)
+ operator = "$#{key.operator}"
+ normalized_value = normalized_value(normalized_key, operator, value)
+ source[normalized_key] ||= {}
+ source[normalized_key][operator] = normalized_value
+ else
+ if key == :conditions
+ value.each { |k, v| self[k] = v }
+ else
+ normalized_value = normalized_value(normalized_key, normalized_key, value)
+ source[normalized_key] = normalized_value
+ end
+ end
+ end
+
+ def ==(other)
+ source == other.source
+ end
+
+ def to_hash
+ source
end
def merge(other)
- hash.dup.tap do |target|
- other.keys.each do |key|
- value, other_value = target[key], other[key]
- target[key] =
- if target.key?(key)
- if value.is_a?(Hash)
- self.class.new(value).merge(other_value)
+ target = source.dup
+ other.source.each_key do |key|
+ value, other_value = target[key], other[key]
+ target[key] =
+ if target.key?(key)
+ value_is_hash = value.is_a?(Hash)
+ other_is_hash = other_value.is_a?(Hash)
+
+ if value_is_hash && other_is_hash
+ value.update(other_value) do |key, old_value, new_value|
+ Array(old_value).concat(Array(new_value)).uniq
+ end
+ elsif value_is_hash && !other_is_hash
+ if modifier_key = value.keys.detect { |k| k.to_s[0, 1] == '$' }
+ value[modifier_key].concat(Array(other_value)).uniq!
else
- Array(value).concat(Array(other_value)).uniq
+ # kaboom! Array(value).concat(Array(other_value)).uniq
+ end
+ elsif other_is_hash && !value_is_hash
+ if modifier_key = other_value.keys.detect { |k| k.to_s[0, 1] == '$' }
+ other_value[modifier_key].concat(Array(value)).uniq!
+ else
+ # kaboom! Array(value).concat(Array(other_value)).uniq
end
else
- other_value
+ Array(value).concat(Array(other_value)).uniq
end
- end
+ else
+ other_value
+ end
end
+ self.class.new(target)
end
+
+ def object_ids
+ @options[:object_ids]
+ end
+
+ def object_ids=(value)
+ @options[:object_ids] = value
+ end
+
+ private
+ def method_missing(method, *args, &block)
+ @source.send(method, *args, &block)
+ end
+
+ def object_id?(key)
+ return false if object_ids.nil?
+ object_ids.include?(key.to_sym)
+ end
+
+ def normalized_key(key)
+ key = key.to_sym if key.respond_to?(:to_sym)
+ return normalized_key(key.field) if key.respond_to?(:field)
+ return :_id if key == :id
+ key
+ end
+
+ def normalized_value(parent_key, key, value)
+ case value
+ when Array, Set
+ value.map! { |v| Plucky.to_object_id(v) } if object_id?(parent_key)
+ parent_key == key ? {'$in' => value.to_a} : value.to_a
+ when Time
+ value.utc
+ when String
+ return Plucky.to_object_id(value) if object_id?(key)
+ value
+ when Hash
+ value.each { |k, v| value[k] = normalized_value(key, k, v) }
+ value
+ else
+ value
+ end
+ end
end
end
View
@@ -0,0 +1,97 @@
+module Plucky
+ class OptionsHash
+ attr_reader :source
+
+ def initialize(hash={})
+ @source = {}
+ hash.each { |key, value| self[key] = value }
+ end
+
+ def []=(key, value)
+ key = normalized_key(key)
+ source[key] = normalized_value(key, value)
+ end
+
+ def ==(other)
+ source == other.source
+ end
+
+ def to_hash
+ source
+ end
+
+ private
+ def method_missing(method, *args, &block)
+ @source.send(method, *args, &block)
+ end
+
+ NormalizedKeys = {
+ :order => :sort,
+ :select => :fields,
+ :offset => :skip,
+ :id => :_id,
+ }
+
+ def normalized_key(key)
+ NormalizedKeys.default = key
+ NormalizedKeys[key.to_sym]
+ end
+
+ def normalized_value(key, value)
+ case key
+ when :fields
+ normalized_fields(value)
+ when :sort
+ normalized_sort(value)
+ when :limit, :skip
+ value.nil? ? nil : value.to_i
+ else
+ value
+ end
+ end
+
+ def normalized_fields(value)
+ return nil if value.respond_to?(:empty?) && value.empty?
+ case value
+ when Array
+ value.flatten
+ when Symbol
+ [value]
+ when String
+ value.split(',').map { |v| v.strip }
+ else
+ value
+ end
+ end
+
+ def normalized_sort(value)
+ case value
+ when Array
+ value.compact.map { |v| normalized_sort_piece(v).flatten }
+ else
+ normalized_sort_piece(value)
+ end
+ end
+
+ def normalized_sort_piece(value)
+ case value
+ when SymbolOperator
+ [normalized_direction(value.field, value.operator)]
+ when String
+ value.split(',').map do |piece|
+ normalized_direction(*piece.split(' '))
+ end
+ when Symbol
+ [normalized_direction(value)]
+ else
+ value
+ end
+ end
+
+ def normalized_direction(field, direction=nil)
+ direction ||= 'ASC'
+ direction = direction.upcase == 'ASC' ? 1 : -1
+ [normalized_key(field).to_s, direction]
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit 4c7ddbe

Please sign in to comment.