Class: Vagrant::Action::Builder

Inherits:
Object
  • Object
show all
Defined in:
lib/vagrant/action/builder.rb

Overview

Action builder which provides a nice DSL for building up a middleware sequence for Vagrant actions. This code is based heavily off of Rack::Builder and ActionDispatch::MiddlewareStack in Rack and Rails, respectively.

Usage

Building an action sequence is very easy:

app = Vagrant::Action::Builder.new.tap do |b|
  b.use MiddlewareA
  b.use MiddlewareB
end

Vagrant::Action.run(app)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeBuilder

Returns a new instance of Builder.



35
36
37
# File 'lib/vagrant/action/builder.rb', line 35

def initialize
  @stack = []
end

Instance Attribute Details

#stackArray (readonly)

This is the stack of middlewares added. This should NOT be used directly.

Returns:

  • (Array)


24
25
26
# File 'lib/vagrant/action/builder.rb', line 24

def stack
  @stack
end

Class Method Details

.build(middleware, *args, &block) ⇒ Builder

This is a shortcut for a middleware sequence with only one item in it. For a description of the arguments and the documentation, please see #use instead.

Returns:



31
32
33
# File 'lib/vagrant/action/builder.rb', line 31

def self.build(middleware, *args, &block)
  new.use(middleware, *args, &block)
end

Instance Method Details

#call(env) ⇒ Object

Runs the builder stack with the given environment.



115
116
117
# File 'lib/vagrant/action/builder.rb', line 115

def call(env)
  to_app(env).call(env)
end

#delete(index) ⇒ Object

Deletes the given middleware object or index



109
110
111
112
# File 'lib/vagrant/action/builder.rb', line 109

def delete(index)
  index = self.index(index) unless index.is_a?(Integer)
  stack.delete_at(index)
end

#flattenObject

Returns a mergeable version of the builder. If use is called with the return value of this method, then the stack will merge, instead of being treated as a separate single middleware.



50
51
52
53
54
# File 'lib/vagrant/action/builder.rb', line 50

def flatten
  lambda do |env|
    self.call(env)
  end
end

#index(object) ⇒ Integer

Returns the numeric index for the given middleware object.

Parameters:

  • object (Object)

    The item to find the index for

Returns:

  • (Integer)


123
124
125
126
127
128
129
130
# File 'lib/vagrant/action/builder.rb', line 123

def index(object)
  stack.each_with_index do |item, i|
    return i if item[0] == object
    return i if item[0].respond_to?(:name) && item[0].name == object
  end

  nil
end

#initialize_copy(original) ⇒ Object

Implement a custom copy that copies the stack variable over so that we don't clobber that.



41
42
43
44
45
# File 'lib/vagrant/action/builder.rb', line 41

def initialize_copy(original)
  super

  @stack = original.stack.dup
end

#insert(index, middleware, *args, &block) ⇒ Object Also known as: insert_before

Inserts a middleware at the given index or directly before the given middleware object.



74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/vagrant/action/builder.rb', line 74

def insert(index, middleware, *args, &block)
  index = self.index(index) unless index.is_a?(Integer)
  raise "no such middleware to insert before: #{index.inspect}" unless index

  if middleware.kind_of?(Builder)
    middleware.stack.reverse.each do |stack_item|
      stack.insert(index, stack_item)
    end
  else
    stack.insert(index, [middleware, args, block])
  end
end

#insert_after(index, middleware, *args, &block) ⇒ Object

Inserts a middleware after the given index or middleware object.



90
91
92
93
94
# File 'lib/vagrant/action/builder.rb', line 90

def insert_after(index, middleware, *args, &block)
  index = self.index(index) unless index.is_a?(Integer)
  raise "no such middleware to insert after: #{index.inspect}" unless index
  insert(index + 1, middleware, *args, &block)
end

#replace(index, middleware, *args, &block) ⇒ Object

Replaces the given middlware object or index with the new middleware.



98
99
100
101
102
103
104
105
106
# File 'lib/vagrant/action/builder.rb', line 98

def replace(index, middleware, *args, &block)
  if index.is_a?(Integer)
    delete(index)
    insert(index, middleware, *args, &block)
  else
    insert_before(index, middleware, *args, &block)
    delete(index)
  end
end

#to_app(env) ⇒ Object

Converts the builder stack to a runnable action sequence.

Parameters:

  • env (Hash)

    The action environment hash

Returns:

  • (Object)

    A callable object



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/vagrant/action/builder.rb', line 136

def to_app(env)
  app_stack = nil

  # If we have action hooks, then we apply them
  if env[:action_hooks]
    builder = self.dup

    # These are the options to pass into hook application.
    options = {}

    # If we already ran through once and did append/prepends,
    # then don't do it again.
    if env[:action_hooks_already_ran]
      options[:no_prepend_or_append] = true
    end

    # Specify that we already ran, so in the future we don't repeat
    # the prepend/append hooks.
    env[:action_hooks_already_ran] = true

    # Apply all the hooks to the new builder instance
    env[:action_hooks].each do |hook|
      hook.apply(builder, options)
    end

    # The stack is now the result of the new builder
    app_stack = builder.stack.dup
  end

  # If we don't have a stack then default to using our own
  app_stack ||= stack.dup

  # Wrap the middleware stack with the Warden to provide a consistent
  # and predictable behavior upon exceptions.
  Warden.new(app_stack, env)
end

#use(middleware, *args, &block) ⇒ Object

Adds a middleware class to the middleware stack. Any additional args and a block, if given, are saved and passed to the initializer of the middleware.

Parameters:

  • middleware (Class)

    The middleware class



61
62
63
64
65
66
67
68
69
70
# File 'lib/vagrant/action/builder.rb', line 61

def use(middleware, *args, &block)
  if middleware.kind_of?(Builder)
    # Merge in the other builder's stack into our own
    self.stack.concat(middleware.stack)
  else
    self.stack << [middleware, args, block]
  end

  self
end