Class: Spider::Model::Condition

Inherits:
Hash
  • Object
show all
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.author.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

Class Method Summary collapse

Instance Method Summary collapse

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
# File 'lib/spiderfw/model/condition.rb', line 108

def initialize(*params, &proc)
    @conjunction = :and
    @comparisons = {}
    @subconditions = []
    params.reject!{ |p| p.nil? }
    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

#comparisonsHash (readonly)

An hash of comparisons for each element name

Returns:

  • (Hash)


32
33
34
# File 'lib/spiderfw/model/condition.rb', line 32

def comparisons
  @comparisons
end

#conjunctCondition

A pointer to the last Condition used in condition blocks

Returns:



39
40
41
# File 'lib/spiderfw/model/condition.rb', line 39

def conjunct
  @conjunct
end

#conjunctionSymbol

The top level conjunction for the Condition (:or or :and; new Conditions are initialized with :or)

Returns:



26
27
28
# File 'lib/spiderfw/model/condition.rb', line 26

def conjunction
  @conjunction
end

#polymorphClass<BaseModel]

Polymorph model: used to tell the mapper the condition is on a subclass of the queried model.

Returns:



29
30
31
# File 'lib/spiderfw/model/condition.rb', line 29

def polymorph
  @polymorph
end

#subconditionsArray (readonly)

An Array of subconditions

Returns:

  • (Array)


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.

Returns:



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_regexpRegexp

Regexp to parse comparison operators

Returns:

  • (Regexp)


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

Used by and and or methods

Parameters:



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.

Returns:



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.

Returns:



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).

Parameters:

Returns:



186
187
188
189
190
191
192
193
# File 'lib/spiderfw/model/condition.rb', line 186

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.

Parameters:



198
199
200
201
202
203
204
205
206
207
# File 'lib/spiderfw/model/condition.rb', line 198

def <<(condition)
    if (condition.class == self.class)
        @subconditions << condition
    elsif (condition.is_a?(Hash))
        @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.

Parameters:

Returns:

  • (bool)

    True if the two conditions have the same comparisions and conjunction



387
388
389
390
391
392
393
394
395
# File 'lib/spiderfw/model/condition.rb', line 387

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

Parameters:

Returns:



261
262
263
264
265
266
# File 'lib/spiderfw/model/condition.rb', line 261

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)

Parameters:

Returns:

  • (self)


254
255
256
# File 'lib/spiderfw/model/condition.rb', line 254

def []=(key, value)
    set(key, '=', value)
end

#all_each_with_comparisonvoid

This method returns an undefined value.

Yields each key, value and comparison, for this condition and its subconditions



170
171
172
173
174
175
# File 'lib/spiderfw/model/condition.rb', line 170

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.

Parameters:

  • other (Condition) (defaults to: nil)

    Condition to conjuct to this

  • proc (Proc)

    Block to create the other condition

Returns:

  • (Condition)

    The resulting Condition (self, or the new Condition created)



352
353
354
# File 'lib/spiderfw/model/condition.rb', line 352

def and(other=nil, &proc)
    return conj(:and, other, &proc)
end

#cloneCondition

Returns a deep copy.

Returns:



419
420
421
422
423
424
425
426
427
428
429
430
# File 'lib/spiderfw/model/condition.rb', line 419

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_arrayArray

Returns An array of all conditions, expressed as [key, value, comparison].

Returns:

  • (Array)

    An array of all conditions, expressed as [key, value, comparison]



152
153
154
155
156
157
# File 'lib/spiderfw/model/condition.rb', line 152

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.

Parameters:

  • element_names (*Symbol)

    A list of element names

Returns:

  • (Array)

    An array of matching conditions



453
454
455
456
457
458
459
460
461
462
463
# File 'lib/spiderfw/model/condition.rb', line 453

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.

Parameters:

  • conjunction (Symbol)

    :and | :or

  • other (Condition) (defaults to: nil)

    Condition to conjuct to this

  • proc (Proc)

    Block to create the other condition

Returns:

  • (Condition)

    The resulting Condition (self, or the new Condition created)



320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/spiderfw/model/condition.rb', line 320

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.

Parameters:

Returns:

  • (Array)

    A pair containing the deleted value and comparison



283
284
285
286
287
288
289
290
# File 'lib/spiderfw/model/condition.rb', line 283

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_comparisonObject

Yields each key, value and comparison.

Returns:

  • void



161
162
163
164
165
166
# File 'lib/spiderfw/model/condition.rb', line 161

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.

Returns:

  • (bool)

    True if the Condition is empty, false otherwise



362
363
364
365
366
367
368
# File 'lib/spiderfw/model/condition.rb', line 362

def empty?
    return false unless super
    @subconditions.each do |sub|
        return false unless sub.empty?
    end
    return true
end

#eql?(other) ⇒ bool

Returns See #==.

Returns:

  • (bool)

    See #==



398
399
400
# File 'lib/spiderfw/model/condition.rb', line 398

def eql?(other)
    self == other
end

#get_deep_objCondition

See #ModelHash.get_deep_obj

Returns:



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

#hashString

Returns Keying hash.

Returns:

  • (String)

    Keying hash



403
404
405
# File 'lib/spiderfw/model/condition.rb', line 403

def hash
    ([self.keys, self.values, @comparisons.values, @polymorph] + @subconditions.map{ |s| s.hash}).hash
end

#hash_cloneObject

Alias for Hash#clone



415
# File 'lib/spiderfw/model/condition.rb', line 415

alias :hash_clone :clone

#hash_empty?Object

:nodoc:



358
# File 'lib/spiderfw/model/condition.rb', line 358

alias :hash_empty? :empty?

#hash_replaceObject

Alias to the original Hash#replace



371
# File 'lib/spiderfw/model/condition.rb', line 371

alias :hash_replace :replace

#hash_setObject

Original hash value assignment



41
# File 'lib/spiderfw/model/condition.rb', line 41

alias :hash_set :[]=

#inspectString

Returns A String representation of the condition.

Returns:

  • (String)

    A String representation of the condition



293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/spiderfw/model/condition.rb', line 293

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.

Parameters:

  • other (Condition) (defaults to: nil)

    Condition to conjuct to this

  • proc (Proc)

    Block to create the other condition

Returns:

  • (Condition)

    The resulting Condition (self, or the new Condition created)



342
343
344
# File 'lib/spiderfw/model/condition.rb', line 342

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.

Parameters:

  • proc (Proc)

    The condition block



136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/spiderfw/model/condition.rb', line 136

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

#polymorphsArray

Returns An array of polymorphic conditions.

Returns:

  • (Array)

    An array of polymorphic conditions



444
445
446
447
448
# File 'lib/spiderfw/model/condition.rb', line 444

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.

Parameters:

  • Class<BaseModel] (Class<BaseModel] The model on which the Condition is based on.)

    The model on which the Condition is based on.

Returns:

  • (bool)

    True if the condition has only primary keys for the given model



179
180
181
# File 'lib/spiderfw/model/condition.rb', line 179

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.

Parameters:

Returns:

  • (self)


273
274
275
276
277
278
# File 'lib/spiderfw/model/condition.rb', line 273

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.

Parameters:

Returns:

  • (self)


376
377
378
379
380
381
382
383
# File 'lib/spiderfw/model/condition.rb', line 376

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

Parameters:

Returns:

  • (self)


214
215
216
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
# File 'lib/spiderfw/model/condition.rb', line 214

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)
        debugger if parts[0].blank?
        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

#simplifyself

Traverses the tree removing useless conditions.

Returns:

  • (self)


434
435
436
437
438
439
440
441
# File 'lib/spiderfw/model/condition.rb', line 434

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.

Returns:

  • self



409
410
411
412
# File 'lib/spiderfw/model/condition.rb', line 409

def uniq!
    @subconditions.uniq!
    self
end