class Syskit::DependencyInjectionContext

Representation of a selection context, as a stack of DependencyInjection objects

This represents a prioritized set of selections (as DependencyInjection objects). It is mainly used during instanciation to find what should be instanciated.

In the stack, the latest selection added with push takes priority over everything that has been added before it. During resolution, if nothing is found at a certain level, then the previous levels will be queried.

Use selection_for and candidates_for to query the selection. Use save, restore and push to manage the stack

Constants

StackLevel

Attributes

savepoints[R]

The list of savepoints

They are stored as sizes of stack. I.e. restore simply resizes stack and state to the size stored in save.last

stack[R]

The stack of StackLevel objects added with push

state[R]

The resolved selections. When a query is made at a certain level of the stack, it gets resolved into one single explicit selection hash, to optimize repeated queries.

Public Class Methods

new(base = nil) click to toggle source

Creates a new dependency injection context

base is the root selection context (can be nil). It can either be a hash or a DependencyInjection object. In the first case, it is interpreted as a selection hash usable in DependencyInjection#use, and is converted to the corresponding DependencyInjection object this way.

# File lib/syskit/dependency_injection_context.rb, line 38
def initialize(base = nil)
    @stack = []
    @state = []
    @savepoints = []

    # Add a guard on the stack, so that #push does not have to care
    stack << StackLevel.new(DependencyInjection.new, DependencyInjection.new)

    case base
    when Hash
        deps = DependencyInjection.new(base)
        push(deps)
    when DependencyInjection
        push(base)
    when NilClass
    else
        raise ArgumentError, "expected either a selection hash or a DependencyInjection object as base selection, got #{base}"
    end
end

Public Instance Methods

concat(context) click to toggle source

Push all DI information in the given context at the top of the stack of this context

# File lib/syskit/dependency_injection_context.rb, line 70
def concat(context)
    context.stack.each do |di|
        push(di.added_info.dup)
    end
end
current_state() click to toggle source

Returns the resolved state of the selection stack, as a DependencyInjection object.

Calling candidates_for and selection_for on the resolved object is equivalent to resolving the complete stack

# File lib/syskit/dependency_injection_context.rb, line 115
def current_state
    stack.last.resolver
end
empty?() click to toggle source
# File lib/syskit/dependency_injection_context.rb, line 172
def empty?
    stack.size == 1
end
has_selection_for?(name) click to toggle source

(see Syskit::DependencyInjection#has_selection_for?)

# File lib/syskit/dependency_injection_context.rb, line 143
def has_selection_for?(name)
    current_state.has_selection_for?(name)
end
initialize_copy(obj) click to toggle source
# File lib/syskit/dependency_injection_context.rb, line 58
def initialize_copy(obj)
    @stack = obj.stack.dup
    @state = obj.state.dup
    @savepoints  = obj.savepoints.dup
end
instance_selection_for(name, requirements) click to toggle source

Returns a non-ambiguous selection for the given criteria

Returns nil if no selection is defined, or if there is an ambiguity (i.e. multiple candidates exist)

See DependencyInjection#candidates_for for the format of criteria

See also candidates_for

# File lib/syskit/dependency_injection_context.rb, line 156
def instance_selection_for(name, requirements)
    current_state.instance_selection_for(name, requirements)
end
pop() click to toggle source

Removes the last dependency injection context stored on the stack, and returns it.

Will stop at the last saved context (saved with save). Returns nil in this case

@return [StackLevel,nil]

# File lib/syskit/dependency_injection_context.rb, line 215
def pop
    if stack.size == 1
        return StackLevel.new(DependencyInjection.new, DependencyInjection.new)
    end

    expected_size = @savepoints.last
    if expected_size && expected_size == stack.size
        return
    end

    result = stack.pop
    if state.size > stack.size
        @state = state[0, stack.size]
    end
    result
end
pretty_print(pp) click to toggle source
# File lib/syskit/dependency_injection_context.rb, line 64
def pretty_print(pp)
    current_state.pretty_print(pp)
end
push(spec) click to toggle source

Adds a new dependency injection context on the stack

# File lib/syskit/dependency_injection_context.rb, line 177
def push(spec)
    if spec.empty?
        stack << StackLevel.new(stack.last.resolver, DependencyInjection.new)
        return
    end

    spec = DependencyInjection.new(spec)

    new_state = stack.last.resolver.dup
    # Resolve all names
    unresolved = spec.resolve_names(new_state.explicit)
    if !unresolved.empty?
        raise NameResolutionError.new(unresolved), "could not resolve names while pushing #{spec} on #{self}"
    end
    # Resolve recursive selection, and default selections
    spec.resolve_default_selections
    # Finally, add it to the new state
    new_state.add(spec)
    new_state.resolve!
    # ... and to the stack
    stack << StackLevel.new(new_state, spec)
end
push_mask(mask) click to toggle source
# File lib/syskit/dependency_injection_context.rb, line 160
def push_mask(mask)
    if mask.empty?
        stack << StackLevel.new(stack.last.resolver, DependencyInjection.new)
        return
    end
    spec = DependencyInjection.new
    spec.add_mask(mask)
    new_state = stack.last.resolver.dup
    new_state.add_mask(mask)
    stack << StackLevel.new(new_state, spec)
end
restore() click to toggle source

The opposite of {#save}

Save and restore calls are paired. See save for more information.

# File lib/syskit/dependency_injection_context.rb, line 122
def restore
    expected_size = @savepoints.pop
    if !expected_size
        raise ArgumentError, "save/restore stack is empty"
    end

    @stack = stack[0, expected_size]
    if state.size > expected_size
        @state = state[0, expected_size]
    end
end
save() { || ... } click to toggle source

Pushes the current state of the context on the save stack. restore will go back to this exact state, regardless of the number of {push} calls, and {pop} will stop at the last savepoint

The save/restore mechanism is stack-based, so when doing

save
save
restore
restore

The first restore returns to the state in the second save and the second restore returns to the state in thef first save.

@overload save()

adds a savepoint that is going to be restored by the matching
{#restore} call

@overload save() { }

saves the current state, executes the block and calls {#restore}
when the execution quits the block

@return [void]

# File lib/syskit/dependency_injection_context.rb, line 97
def save
    if !block_given?
        @savepoints << stack.size
    else
        save
        begin
            yield
        ensure
            restore
        end
    end
end
selection_for(name, requirements) click to toggle source

Returns all the candidates that match criteria in the current state of this context

(see Syskit::DependencyInjection#selection_for)

# File lib/syskit/dependency_injection_context.rb, line 138
def selection_for(name, requirements)
    current_state.selection_for(name, requirements)
end
top() click to toggle source

Returns the StackLevel object representing the last added level on the stack

@return [StackLevel]

# File lib/syskit/dependency_injection_context.rb, line 204
def top
    stack.last
end