Class: Roby::Queries::TaskMatcher

Inherits:
PlanObjectMatcher show all
Includes:
DRoby::V5::Queries::TaskMatcherDumper
Defined in:
lib/roby/queries/task_matcher.rb,
lib/roby/droby/enable.rb

Overview

This class represents a predicate which can be used to filter tasks. To filter plan-related properties, use Query.

A TaskMatcher object is a AND combination of various tests against tasks.

For instance, if one does

matcher = TaskMatcher.new.which_fullfills(Tasks::Simple).pending

Then

matcher === task

will return true if task is an instance of the Tasks::Simple model and is pending (not started yet), and false if one of these two characteristics is not true.

Direct Known Subclasses

Query

Instance Attribute Summary collapse

Attributes inherited from PlanObjectMatcher

#children, #indexed_neg_predicates, #indexed_predicates, #instance, #model, #owners, #parents

Attributes inherited from MatcherBase

#neg_predicates, #predicates

Instance Method Summary collapse

Methods included from DRoby::V5::Queries::TaskMatcherDumper

#droby_dump

Methods inherited from PlanObjectMatcher

#executable?, #filter, #handle_parent_child_match, #indexed_sets, match_predicate, #not_self_owned, #owned_by, #self_owned, #with_child, #with_instance, #with_model, #with_parent

Methods included from DRoby::V5::Queries::PlanObjectMatcherDumper

#droby_dump

Methods inherited from MatcherBase

#&, declare_class_methods, #describe_failed_match, #each, #match, match_predicate, match_predicates, #negate, #|

Constructor Details

#initializeTaskMatcher

Initializes an empty TaskMatcher object



27
28
29
30
31
# File 'lib/roby/queries/task_matcher.rb', line 27

def initialize
    super
    @arguments            = Hash.new
    @interruptible        = nil
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(m, *args) ⇒ Object



316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/roby/queries/task_matcher.rb', line 316

def method_missing(m, *args)
    if m =~ /_event$/
        event_name = $`
        model = find_event(event_name)
        if !model
            task_models =
                if !@model.empty?
                    @model
                else [Roby::Task]
                end
            raise NoMethodError.new(m), "no event '#{event_name}' in match model #{task_models.map(&:to_s).join(", ")}, use #which_fullfills to narrow the task model"
        elsif !args.empty?
            raise ArgumentError, "#{m} expected zero arguments, got #{args.size}"
        else
            return TaskEventGeneratorMatcher.new(self, event_name)
        end
    else
        super
    end
end

Instance Attribute Details

#argumentsHash (readonly)

Set of arguments that should be tested on the task



24
25
26
# File 'lib/roby/queries/task_matcher.rb', line 24

def arguments
  @arguments
end

Instance Method Details

#===(task) ⇒ Object

True if task matches all the criteria defined on this object.



285
286
287
288
289
# File 'lib/roby/queries/task_matcher.rb', line 285

def ===(task)
    return unless task.kind_of?(Roby::Task)
    return unless task.arguments.slice(*arguments.keys) == arguments
    return super
end

#abstract?Object

:method: not_finishing

Matches if the task is not finishing

See also #finishing, Task#finishing?



260
261
# File 'lib/roby/queries/task_matcher.rb', line 260

match_predicates :abstract?, :partially_instanciated?, :fully_instanciated?,
:starting?, :pending?, :running?, :finished?, :success?, :failed?, :interruptible?

#find_event(event_name) ⇒ Object



298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/roby/queries/task_matcher.rb', line 298

def find_event(event_name)
    event_name = event_name.to_sym
    models = if !@model.empty?
                 @model
             else [Roby::Task]
             end
    models.each do |m|
        if event_m = m.find_event(event_name)
            return TaskEventGeneratorMatcher.new(self, event_name)
        end
    end
    nil
end

#handle_parent_child_arguments(other_query, relation, relation_options) ⇒ Object

Helper method for #with_child and #with_parent



271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'lib/roby/queries/task_matcher.rb', line 271

def handle_parent_child_arguments(other_query, relation, relation_options) # :nodoc:
    if !other_query.kind_of?(TaskMatcher) && !other_query.kind_of?(Task)
        if relation.kind_of?(Hash)
            arguments = relation
            relation         = (arguments.delete(:relation) || arguments.delete('relation'))
            relation_options = (arguments.delete(:relation_options) || arguments.delete('relation_options'))
        else
            arguments = Hash.new
        end
        other_query = TaskMatcher.which_fullfills(other_query, arguments)
    end
    return relation, [other_query, relation_options]
end

#indexed_query?Boolean

Returns true if filtering with this TaskMatcher using #=== is equivalent to calling #filter() using a Index. This is used to avoid an explicit O(N) filtering step after filter() has been called



294
295
296
# File 'lib/roby/queries/task_matcher.rb', line 294

def indexed_query?
    arguments.empty? && super
end

#respond_to_missing?(m, include_private) ⇒ Boolean



312
313
314
# File 'lib/roby/queries/task_matcher.rb', line 312

def respond_to_missing?(m, include_private)
    m =~ /_event$/ || super
end

#to_sObject



33
34
35
36
37
38
39
# File 'lib/roby/queries/task_matcher.rb', line 33

def to_s
    result = super
    if !arguments.empty?
        result << ".with_arguments(#{arguments.map { |k, v| ":#{k} => #{v}" }.join(", ")})"
    end
    result
end

#which_fullfills(model, arguments = nil) ⇒ Object

Filters on task model and arguments

Will match if the task is an instance of model or one of its subclasses, and if parts of its arguments are the ones provided. Set arguments to nil if you don’t want to filter on arguments.



46
47
48
49
50
51
52
# File 'lib/roby/queries/task_matcher.rb', line 46

def which_fullfills(model, arguments = nil)
    with_model(model)
    if arguments
        with_model_arguments(arguments)
    end
    self
end

#with_arguments(arguments) ⇒ Object

Filters on the arguments that are declared in the model

Will match if the task arguments for which there is a value in arguments are set to that very value. Unlike #with_model_arguments, all values set in arguments are considered.

See also #with_model_arguments

Example:

class TaskModel < Roby::Task
  argument :a
  argument :b
end
task = TaskModel.new(a: 10, b: 20)

# Matches on :a, :b is ignored altogether
TaskMatcher.new.
    with_arguments(a: 10) === task # => true
# Looks for both :a and :b
TaskMatcher.new.
    with_arguments(a: 10, b: 30) === task # => false
# Looks for both :a and :c, even though :c is not declared in TaskModel
TaskMatcher.new.
    with_arguments(a: 10, c: 30) === task # => false


116
117
118
119
120
121
122
123
124
125
# File 'lib/roby/queries/task_matcher.rb', line 116

def with_arguments(arguments)
    @arguments ||= Hash.new
    self.arguments.merge!(arguments) do |k, old, new| 
        if old != new
            raise ArgumentError, "a constraint has already been set on the #{k} argument" 
        end
        old
    end
    self
end

#with_model_arguments(arguments) ⇒ Object

Filters on the arguments that are declared in the model

Will match if the task arguments for which there is a value in arguments are set to that very value, only looking at arguments that are defined in the model set by #with_model.

See also #with_arguments

Example:

class TaskModel < Roby::Task
  argument :a
  argument :b
end
task = TaskModel.new(a: 10, b: 20)

# Matches on :a, :b is ignored altogether
TaskMatcher.new.
    with_model(TaskModel).
    with_model_arguments(a: 10) === task # => true
# Looks for both :a and :b
TaskMatcher.new.
    with_model(TaskModel).
    with_model_arguments(a: 10, b: 30) === task # => false
# Matches on :a, :c is ignored as it is not an argument of +TaskModel+
TaskMatcher.new.
    with_model(TaskModel).
    with_model_arguments(a: 10, c: 30) === task # => true

In general, one would use #which_fullfills, which sets both the model and the model arguments



85
86
87
88
89
# File 'lib/roby/queries/task_matcher.rb', line 85

def with_model_arguments(arguments)
    valid_arguments = model.inject(Array.new) { |set, model| set | model.arguments.to_a }
    with_arguments(arguments.slice(*valid_arguments))
    self
end