Class: Syskit::DependencyInjectionContext

Inherits:
Object
  • Object
show all
Defined in:
lib/syskit/dependency_injection_context.rb

Overview

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

Defined Under Namespace

Classes: StackLevel

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(base = nil) ⇒ DependencyInjectionContext

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.



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# 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

Instance Attribute Details

#savepointsObject (readonly)

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



29
30
31
# File 'lib/syskit/dependency_injection_context.rb', line 29

def savepoints
  @savepoints
end

#stackObject (readonly)

The stack of StackLevel objects added with #push



20
21
22
# File 'lib/syskit/dependency_injection_context.rb', line 20

def stack
  @stack
end

#stateObject (readonly)

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.



24
25
26
# File 'lib/syskit/dependency_injection_context.rb', line 24

def state
  @state
end

Instance Method Details

#concat(context) ⇒ Object

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



70
71
72
73
74
# 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_stateObject

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



115
116
117
# File 'lib/syskit/dependency_injection_context.rb', line 115

def current_state
    stack.last.resolver
end

#empty?Boolean

Returns:

  • (Boolean)


172
173
174
# File 'lib/syskit/dependency_injection_context.rb', line 172

def empty?
    stack.size == 1
end

#has_selection_for?(name) ⇒ Boolean

True if there is an explicit selection for the given name

Returns:

  • (Boolean)


143
144
145
# File 'lib/syskit/dependency_injection_context.rb', line 143

def has_selection_for?(name)
    current_state.has_selection_for?(name)
end

#initialize_copy(obj) ⇒ Object



58
59
60
61
62
# 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) ⇒ Object

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



156
157
158
# File 'lib/syskit/dependency_injection_context.rb', line 156

def instance_selection_for(name, requirements)
    current_state.instance_selection_for(name, requirements)
end

#popStackLevel?

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

Returns:



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# 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) ⇒ Object



64
65
66
# File 'lib/syskit/dependency_injection_context.rb', line 64

def pretty_print(pp)
    current_state.pretty_print(pp)
end

#push(spec) ⇒ Object

Adds a new dependency injection context on the stack



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# 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) ⇒ Object



160
161
162
163
164
165
166
167
168
169
170
# 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

#restoreObject

The opposite of #save

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



122
123
124
125
126
127
128
129
130
131
132
# 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

#savevoid #savevoid

This method returns an undefined value.

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.

Overloads:

  • #savevoid

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

  • #savevoid

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



97
98
99
100
101
102
103
104
105
106
107
108
# 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) ⇒ Object

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

(see DependencyInjection#selection_for)



138
139
140
# File 'lib/syskit/dependency_injection_context.rb', line 138

def selection_for(name, requirements)
    current_state.selection_for(name, requirements)
end

#topStackLevel

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

Returns:



204
205
206
# File 'lib/syskit/dependency_injection_context.rb', line 204

def top
    stack.last
end