Class: Spider::Model::Condition
- Defined in:
- lib/spiderfw/model/condition.rb
Overview
The Condition behaves like a ModelHash, and as such contains key-value pairs: a simple equality condition can be set with
condition[:element_name] = value
The Condition object also holds comparisons: a comparison different from equality can be set with
condition.set(:element_name, '>', value)
Finally, it contains subconditions, which can be added with
conditions << subcondition
Subconditions will be created automatically when using #set twice on the same element. If you want to change the condition, use #delete and set it again.
Conditions also support an SQL-like block syntax for setting conditions:
books_condition = Condition.new{ |book|
((book.year < 1970) | (book.price > 1000)) & (book..name .like 'John')
}
The Condition object, like the Request, doesn’t hold a reference to a model; so no check will be made that the conditions elements are meaningful.
Instance Attribute Summary collapse
-
#comparisons ⇒ Hash
readonly
An hash of comparisons for each element name.
-
#conjunct ⇒ Condition
A pointer to the last Condition used in condition blocks.
-
#conjunction ⇒ Symbol
The top level conjunction for the Condition (:or or :and; new Conditions are initialized with :or).
-
#polymorph ⇒ Class<BaseModel]
Polymorph model: used to tell the mapper the condition is on a subclass of the queried model.
-
#subconditions ⇒ Array
readonly
An Array of subconditions.
Class Method Summary collapse
-
.and(*params, &proc) ⇒ Condition
Instantiates a Condition with :and conjunction.
-
.comparison_operators_regexp ⇒ Regexp
Regexp to parse comparison operators.
-
.conj(conjunction, a, b) ⇒ Object
Used by Condition.and and Condition.or methods.
-
.no_conjunction(*params, &proc) ⇒ Condition
Instantiates a Condition with no conjunction.
-
.or(*params, &proc) ⇒ Condition
Instantiates a Condition with :or conjunction.
Instance Method Summary collapse
-
#+(condition) ⇒ Condition
Returns the result of merging the condition with another one (does not modify the original condition).
-
#<<(condition) ⇒ void
Adds a subcondtion.
-
#==(other) ⇒ bool
True if the two conditions have the same comparisions and conjunction.
-
#[](key) ⇒ Object
Gets the value of a condition.
-
#[]=(key, value) ⇒ self
Sets an equality comparison.
-
#all_each_with_comparison ⇒ void
Yields each key, value and comparison, for this condition and its subconditions.
-
#and(other = nil, &proc) ⇒ Condition
(also: #&, #AND)
Joins the condition to another with an “and” conjunction.
-
#clone ⇒ Condition
Returns a deep copy.
-
#conditions_array ⇒ Array
An array of all conditions, expressed as [key, value, comparison].
-
#conditions_for(*element_names) ⇒ Array
Returns, from self and subconditions, all those who define a condition for one of the given element names.
-
#conj(conjunction, other = nil, &proc) ⇒ Condition
Returns the conjunction with another condition.
-
#delete(field) ⇒ Array
Deletes a field from the Condition.
-
#each_with_comparison ⇒ Object
Yields each key, value and comparison.
-
#empty? ⇒ bool
True if there are no comparisons and no subconditions.
-
#eql?(other) ⇒ bool
See #==.
-
#get_deep_obj ⇒ Condition
See #ModelHash.get_deep_obj.
-
#hash ⇒ String
Keying hash.
-
#hash_clone ⇒ Object
Alias for Hash#clone.
-
#hash_empty? ⇒ Object
:nodoc:.
-
#hash_replace ⇒ Object
Alias to the original Hash#replace.
-
#hash_set ⇒ Object
Original hash value assignment.
-
#initialize(*params, &proc) ⇒ Condition
constructor
Instantiates a new Condition, with :and conjunction.
-
#inspect ⇒ String
A String representation of the condition.
-
#or(other = nil, &proc) ⇒ Condition
(also: #|, #OR)
Joins the condition to another with an “or” conjunction.
-
#parse_block(&proc) ⇒ Object
Parses a condition block.
-
#polymorphs ⇒ Array
An array of polymorphic conditions.
-
#primary_keys_only?(model) ⇒ bool
True if the condition has only primary keys for the given model.
-
#range(field, lower, upper) ⇒ self
Adds a range condition.
-
#replace(other) ⇒ self
Replace all the content of this Condition with another one.
-
#set(field, comparison, value) ⇒ self
Adds a single condition for an element.
-
#simplify ⇒ self
Traverses the tree removing useless conditions.
-
#uniq! ⇒ Object
Removes duplicate subcondtions.
Constructor Details
#initialize(*params, &proc) ⇒ Condition
Instantiates a new Condition, with :and conjunction. If given a Hash, will set all keys = values. If given multiple params, will convert each to a Condition if needed, and append them to the returned instance. If a block is given, it will be processed by #parse_block
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/spiderfw/model/condition.rb', line 108 def initialize(*params, &proc) @conjunction = :and @comparisons = {} @subconditions = [] params.reject!{ |p| p.nil? } if params[0].is_a?(Proc) params[0] = params[0].call end if (params.length == 1 && params[0].is_a?(Hash) && !params[0].is_a?(Condition)) params[0].each do |k, v| set(k, '=', v) end else # FIXME: must have an instantiate method params.each{ |item| self << (item.is_a?(self.class) ? item : self.class.new(item)) } end parse_block(&proc) if (block_given?) end |
Instance Attribute Details
#comparisons ⇒ Hash (readonly)
An hash of comparisons for each element name
32 33 34 |
# File 'lib/spiderfw/model/condition.rb', line 32 def comparisons @comparisons end |
#conjunct ⇒ Condition
A pointer to the last Condition used in condition blocks
39 40 41 |
# File 'lib/spiderfw/model/condition.rb', line 39 def conjunct @conjunct end |
#conjunction ⇒ Symbol
The top level conjunction for the Condition (:or or :and; new Conditions are initialized with :or)
26 27 28 |
# File 'lib/spiderfw/model/condition.rb', line 26 def conjunction @conjunction end |
#polymorph ⇒ Class<BaseModel]
Polymorph model: used to tell the mapper the condition is on a subclass of the queried model.
29 30 31 |
# File 'lib/spiderfw/model/condition.rb', line 29 def polymorph @polymorph end |
#subconditions ⇒ Array (readonly)
An Array of subconditions
35 36 37 |
# File 'lib/spiderfw/model/condition.rb', line 35 def subconditions @subconditions end |
Class Method Details
.and(*params, &proc) ⇒ Condition
Instantiates a Condition with :and conjunction. See Condition.new for arguments.
79 80 81 82 83 |
# File 'lib/spiderfw/model/condition.rb', line 79 def self.and(*params, &proc) c = self.new(*params, &proc) c.conjunction = :and return c end |
.comparison_operators_regexp ⇒ Regexp
Regexp to parse comparison operators
60 61 62 |
# File 'lib/spiderfw/model/condition.rb', line 60 def self.comparison_operators_regexp @comparison_operators_regexp end |
.conj(conjunction, a, b) ⇒ Object
69 70 71 72 73 74 |
# File 'lib/spiderfw/model/condition.rb', line 69 def self.conj(conjunction, a, b) # :nodoc: c = Condition.new c.conjunction = conjunction c << a c << b end |
.no_conjunction(*params, &proc) ⇒ Condition
Instantiates a Condition with no conjunction. See Condition.new for arguments.
97 98 99 100 101 |
# File 'lib/spiderfw/model/condition.rb', line 97 def self.no_conjunction(*params, &proc) c = self.new(*params, &proc) c.conjunction = nil return c end |
.or(*params, &proc) ⇒ Condition
Instantiates a Condition with :or conjunction. See Condition.new for arguments.
88 89 90 91 92 |
# File 'lib/spiderfw/model/condition.rb', line 88 def self.or(*params, &proc) c = self.new(*params, &proc) c.conjunction = :or return c end |
Instance Method Details
#+(condition) ⇒ Condition
Returns the result of merging the condition with another one (does not modify the original condition).
189 190 191 192 193 194 195 196 |
# File 'lib/spiderfw/model/condition.rb', line 189 def +(condition) res = self.clone @subconditions += condition.subconditions condition.each_with_comparison do |k, v, c| res.set(k, v, c) end return res end |
#<<(condition) ⇒ void
This method returns an undefined value.
Adds a subcondtion.
201 202 203 204 205 206 207 208 209 210 |
# File 'lib/spiderfw/model/condition.rb', line 201 def <<(condition) if condition.class == self.class @subconditions << condition elsif condition.is_a?(Hash) || condition.is_a?(Proc) @subconditions << self.class.new(condition) elsif condition.class == String key, val, comparison = parse_comparison(condition) set(key, val, comparison) end end |
#==(other) ⇒ bool
Returns True if the two conditions have the same comparisions and conjunction.
389 390 391 392 393 394 395 396 397 |
# File 'lib/spiderfw/model/condition.rb', line 389 def ==(other) return false unless other.class == self.class return false unless super return false unless @subconditions == other.subconditions return false unless @comparisons == other.comparisons return false unless @polymorph == other.polymorph return false unless @conjunction == other.conjunction return true end |
#[](key) ⇒ Object
Gets the value of a condition
263 264 265 266 267 268 |
# File 'lib/spiderfw/model/condition.rb', line 263 def [](key) # TODO: deep key = key.name if key.is_a?(Element) key = key.to_sym if key.respond_to?(:to_sym) # might be a QueryFunc super(key) end |
#[]=(key, value) ⇒ self
Sets an equality comparison.
Equivalent to #set(key, ‘=’, value)
256 257 258 |
# File 'lib/spiderfw/model/condition.rb', line 256 def []=(key, value) set(key, '=', value) end |
#all_each_with_comparison ⇒ void
This method returns an undefined value.
Yields each key, value and comparison, for this condition and its subconditions
173 174 175 176 177 178 |
# File 'lib/spiderfw/model/condition.rb', line 173 def all_each_with_comparison self.each_with_comparison{ |k, v, c| yield k, v, c } @subconditions.each do |sub| sub.all_each_with_comparison{ |k, v, c| yield k, v, c } end end |
#and(other = nil, &proc) ⇒ Condition Also known as: &, AND
Joins the condition to another with an “and” conjunction. See #conj.
354 355 356 |
# File 'lib/spiderfw/model/condition.rb', line 354 def and(other=nil, &proc) return conj(:and, other, &proc) end |
#clone ⇒ Condition
Returns a deep copy.
421 422 423 424 425 426 427 428 429 430 431 432 |
# File 'lib/spiderfw/model/condition.rb', line 421 def clone c = self.class.new c.conjunction = @conjunction c.polymorph = @polymorph self.each_with_comparison do |key, val, comparison| c.set(key, comparison, val) end @subconditions.each do |sub| c << sub.clone end return c end |
#conditions_array ⇒ Array
Returns An array of all conditions, expressed as [key, value, comparison].
155 156 157 158 159 160 |
# File 'lib/spiderfw/model/condition.rb', line 155 def conditions_array self.hash_clone.map do |k, v| k = k.to_sym if k.respond_to?(:to_sym) [k, v, (@comparisons[k] || '==')] end end |
#conditions_for(*element_names) ⇒ Array
Returns, from self and subconditions, all those who define a condition for one of the given element names.
455 456 457 458 459 460 461 462 463 464 465 |
# File 'lib/spiderfw/model/condition.rb', line 455 def conditions_for(*element_names) conds = [] element_names.each do |el| if self.key?(el) conds << self break end end @subconditions.map{ |s| s.conditions_for(*element_names) }.each{ |c| conds += c } conds end |
#conj(conjunction, other = nil, &proc) ⇒ Condition
Returns the conjunction with another condition. If this condition already has the required conjunction, the other will be added as a subcondition; otherwise, a new condition will be created and both will be added to it.
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 |
# File 'lib/spiderfw/model/condition.rb', line 322 def conj(conjunction, other=nil, &proc) self.conjunction = conjunction if (!self.conjunction) if (self.conjunction == conjunction) c = self else c = Condition.new c.conjunction = conjunction c << self end if (!other && proc) other = Condition.new(&proc) end c << other other.conjunct = true return c end |
#delete(field) ⇒ Array
Deletes a field from the Condition.
285 286 287 288 289 290 291 292 |
# File 'lib/spiderfw/model/condition.rb', line 285 def delete(field) field = field.to_sym return nil unless self[field] || @comparisons[field] cur = [self[field], @comparisons[field]] super @comparisons.delete(field) cur end |
#each_with_comparison ⇒ Object
Yields each key, value and comparison.
164 165 166 167 168 169 |
# File 'lib/spiderfw/model/condition.rb', line 164 def each_with_comparison self.each do |k, v| k = k.to_sym if k.respond_to?(:to_sym) yield k, v, @comparisons[k] || '=' end end |
#empty? ⇒ bool
True if there are no comparisons and no subconditions.
364 365 366 367 368 369 370 |
# File 'lib/spiderfw/model/condition.rb', line 364 def empty? return false unless super @subconditions.each do |sub| return false unless sub.empty? end return true end |
#eql?(other) ⇒ bool
Returns See #==.
400 401 402 |
# File 'lib/spiderfw/model/condition.rb', line 400 def eql?(other) self == other end |
#get_deep_obj ⇒ Condition
See #ModelHash.get_deep_obj
45 46 47 48 49 |
# File 'lib/spiderfw/model/condition.rb', line 45 def get_deep_obj # :nodoc: c = self.class.new c.conjunction = @conjunction return c end |
#hash ⇒ String
Returns Keying hash.
405 406 407 |
# File 'lib/spiderfw/model/condition.rb', line 405 def hash ([self.keys, self.values, @comparisons.values, @polymorph] + @subconditions.map{ |s| s.hash}).hash end |
#hash_clone ⇒ Object
Alias for Hash#clone
417 |
# File 'lib/spiderfw/model/condition.rb', line 417 alias :hash_clone :clone |
#hash_empty? ⇒ Object
:nodoc:
360 |
# File 'lib/spiderfw/model/condition.rb', line 360 alias :hash_empty? :empty? |
#hash_replace ⇒ Object
Alias to the original Hash#replace
373 |
# File 'lib/spiderfw/model/condition.rb', line 373 alias :hash_replace :replace |
#hash_set ⇒ Object
Original hash value assignment
41 |
# File 'lib/spiderfw/model/condition.rb', line 41 alias :hash_set :[]= |
#inspect ⇒ String
Returns A String representation of the condition.
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
# File 'lib/spiderfw/model/condition.rb', line 295 def inspect str = "" cnt = 0 each do |key, value| str += " #{@conjunction} " if cnt > 0 cnt += 1 comparison = @comparisons[key] || '=' cond = "#{comparison} #{value.inspect}" str += "#{key.inspect} #{cond}" end #str += ' [raw:'+raw.inspect+']' unless raw.empty? first = true if @subconditions.length > 0 str += ' '+@conjunction.to_s+' ' if str.length > 0 str += @subconditions.map{ |sub| sub.inspect }.reject{ |sub| sub.empty? }.join(' '+@conjunction.to_s+' ') end str = "(#{str})" if cnt + @subconditions.length > 1 return str end |
#or(other = nil, &proc) ⇒ Condition Also known as: |, OR
Joins the condition to another with an “or” conjunction. See #conj.
344 345 346 |
# File 'lib/spiderfw/model/condition.rb', line 344 def or(other=nil, &proc) return conj(:or, other, &proc) end |
#parse_block(&proc) ⇒ Object
Parses a condition block. Inside the block, an SQL-like language can be used.
Example:
condition.parse_block{ (element1 == val1) & ( (element2 > 'some string') | (element3 .not nil) ) }
All comparisons must be parenthesized; and/or conjunctions are expressed with a single &/|.
Available comparisions are: ==, >, <, >=, <=, .not, .like, .ilike (case insensitive like).
Note: for .like and .ilike comparisons, the SQL ‘%’ syntax must be used.
139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/spiderfw/model/condition.rb', line 139 def parse_block(&proc) res = nil if proc.arity == 1 res = proc.call(ConditionContext.new) else context = eval "self", proc.binding res = context.dup.extend(ConditionMixin).instance_eval(&proc) end self.replace(res) @conjunction = res.conjunction @comparisons = res.comparisons @subconditions = res.subconditions @polymorph = res.polymorph end |
#polymorphs ⇒ Array
Returns An array of polymorphic conditions.
446 447 448 449 450 |
# File 'lib/spiderfw/model/condition.rb', line 446 def polymorphs pol = [] pol << @polymorph if @polymorph return pol + @subconditions.inject([]){ |arr, s| arr += s.polymorphs } end |
#primary_keys_only?(model) ⇒ bool
Returns True if the condition has only primary keys for the given model.
182 183 184 |
# File 'lib/spiderfw/model/condition.rb', line 182 def primary_keys_only?(model) self.select{ |key, value| !model.elements[key] || !model.elements[key].primary_key? }.empty? end |
#range(field, lower, upper) ⇒ self
Adds a range condition. This creates a subcondition with >= and <= conditions.
275 276 277 278 279 280 |
# File 'lib/spiderfw/model/condition.rb', line 275 def range(field, lower, upper) c = self.class.and c.set(field, '>=', lower) c.set(field, '<=', upper) self << c end |
#replace(other) ⇒ self
Replace all the content of this Condition with another one.
378 379 380 381 382 383 384 385 |
# File 'lib/spiderfw/model/condition.rb', line 378 def replace(other) hash_replace(other) @subconditions = other.subconditions @conjunction = other.conjunction @polymorph = other.polymorph @comparisons = other.comparisons self end |
#set(field, comparison, value) ⇒ self
Adds a single condition for an element
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'lib/spiderfw/model/condition.rb', line 217 def set(field, comparison, value) if value.is_a?(QuerySet) value = value.to_a end if value.is_a?(Array) && comparison != 'between' multi_cond = comparison == '<>' ? self.class.and : self.class.or value.uniq.each do |v| multi_cond.set(field, comparison, v) end @subconditions << multi_cond return self end parts = [] unless field.is_a?(Spider::QueryFuncs::Function) field = field.to_s parts = field.split('.', 2) parts[0] = parts[0].to_sym field = field.to_sym unless parts[1] end if (parts[1]) hash_set(parts[0], get_deep_obj()) unless self[parts[0]] self[parts[0]].set(parts[1], comparison, value) elsif (self[field]) c = Condition.new c.set(field, comparison, value) @subconditions << c else hash_set(field, value) @comparisons[field] = comparison end return self end |
#simplify ⇒ self
Traverses the tree removing useless conditions.
436 437 438 439 440 441 442 443 |
# File 'lib/spiderfw/model/condition.rb', line 436 def simplify @subconditions.each{ |sub| sub.simplify } if (hash_empty? && @subconditions.length == 1) self.replace(@subconditions[0]) end @subconditions.uniq! return self end |
#uniq! ⇒ Object
Removes duplicate subcondtions.
411 412 413 414 |
# File 'lib/spiderfw/model/condition.rb', line 411 def uniq! @subconditions.uniq! self end |