langis 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +14 -0
- data/LICENSE +202 -0
- data/NOTICE +4 -0
- data/README.md +533 -0
- data/Rakefile +57 -0
- data/VERSION +1 -0
- data/generators/langis_config_generator.rb +7 -0
- data/generators/templates/langis_config.rb +44 -0
- data/lib/langis.rb +60 -0
- data/lib/langis/dsl.rb +346 -0
- data/lib/langis/engine.rb +146 -0
- data/lib/langis/middleware.rb +135 -0
- data/lib/langis/rackish.rb +118 -0
- data/lib/langis/sinks.rb +138 -0
- data/spec/langis/dsl_spec.rb +301 -0
- data/spec/langis/engine_spec.rb +168 -0
- data/spec/langis/middleware_spec.rb +196 -0
- data/spec/langis/rackish_spec.rb +33 -0
- data/spec/langis/sinks/delayed_job_sink_spec.rb +227 -0
- data/spec/langis/sinks/redis_resque_sink_spec.rb +232 -0
- data/spec/redis.conf +132 -0
- data/spec/spec_helper.rb +8 -0
- metadata +171 -0
data/Rakefile
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = 'langis'
|
8
|
+
gem.summary = %Q{
|
9
|
+
Langis is a Rack inspired publish-subscribe system for Ruby.}
|
10
|
+
gem.description = %Q{
|
11
|
+
Langis is a Rack inspired publish-subscribe system for Ruby.
|
12
|
+
It has flexible message routing and message handling using a custom
|
13
|
+
Domain Specific Language and a Rack-inspired message handler framework.
|
14
|
+
This can give Rails applications better (and decoupled) visibility
|
15
|
+
and management of the background processes it creates (or executes)
|
16
|
+
in response to its actions (in controllers or models).
|
17
|
+
}
|
18
|
+
gem.email = 'benjaminlyu@gmail.com'
|
19
|
+
gem.homepage = 'http://github.com/byu/langis'
|
20
|
+
gem.authors = ['Benjamin Yu']
|
21
|
+
|
22
|
+
# NOTE: Following development dependencies are commented out here
|
23
|
+
# because we include them in the Gemfile bundle. If we included them
|
24
|
+
# here, then they are required to be installed in the base rubygems
|
25
|
+
# repository instead of the Bundler's installation path.
|
26
|
+
#gem.add_development_dependency 'delayed_job'
|
27
|
+
#gem.add_development_dependency 'redis'
|
28
|
+
#gem.add_development_dependency 'resque'
|
29
|
+
gem.add_development_dependency 'rspec', '>= 1.2.9'
|
30
|
+
#gem.add_development_dependency 'sqlite3-ruby'
|
31
|
+
#gem.add_development_dependency 'temping'
|
32
|
+
gem.add_development_dependency 'yard'
|
33
|
+
|
34
|
+
gem.add_dependency 'blockenspiel'
|
35
|
+
gem.add_dependency 'eventmachine'
|
36
|
+
|
37
|
+
gem.files = FileList[
|
38
|
+
'lib/**/*.rb',
|
39
|
+
'bin/*',
|
40
|
+
'[A-Z]*',
|
41
|
+
'spec/**/*',
|
42
|
+
'features/**/*',
|
43
|
+
'generators/**/*'].to_a
|
44
|
+
end
|
45
|
+
Jeweler::GemcutterTasks.new
|
46
|
+
rescue LoadError
|
47
|
+
puts 'Jeweler (or a dependency) not available. Install it with: gem install jeweler'
|
48
|
+
end
|
49
|
+
|
50
|
+
begin
|
51
|
+
require 'yard'
|
52
|
+
YARD::Rake::YardocTask.new
|
53
|
+
rescue LoadError
|
54
|
+
task :yardoc do
|
55
|
+
abort 'YARD is not available. In order to run yardoc, you must: sudo gem install yard'
|
56
|
+
end
|
57
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# This file requires modifications to be run in your rails app.
|
2
|
+
|
3
|
+
# Create the LangisEngine for the rails app to pump messages into.
|
4
|
+
LangisEngine = (lambda {
|
5
|
+
# Define the routes
|
6
|
+
config = Langis::Dsl.langis_plumbing do
|
7
|
+
intake :default do
|
8
|
+
flow_to :default
|
9
|
+
end
|
10
|
+
|
11
|
+
for_sink :default do
|
12
|
+
end
|
13
|
+
|
14
|
+
check_valve do
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Create an example success callback channel.
|
19
|
+
success_channel = EM::Channel.new
|
20
|
+
success_channel.subscribe(proc do |msg|
|
21
|
+
# TODO: Implement your own success handler.
|
22
|
+
# Rails.logger.info "Success: #{msg.inspect}"
|
23
|
+
end)
|
24
|
+
|
25
|
+
# Create an example error callback channel.
|
26
|
+
error_channel = EM::Channel.new
|
27
|
+
error_channel.subscribe(proc do |msg|
|
28
|
+
# TODO: Implement your own error handler.
|
29
|
+
# Rails.logger.warn "Error: #{msg.inspect}"
|
30
|
+
end)
|
31
|
+
|
32
|
+
# Create and return the actual EventMachine based Langis Engine.
|
33
|
+
return Langis::Engine::EventMachineEngine.new(
|
34
|
+
config.build_pipes,
|
35
|
+
:success_channel => success_channel,
|
36
|
+
:error_channel => error_channel)
|
37
|
+
}).call
|
38
|
+
|
39
|
+
# Start up the EventMachine reactor here if need be.
|
40
|
+
# Note that starting up the EventMachine is different depending on what
|
41
|
+
# web server you are running in.
|
42
|
+
#Thread.new do
|
43
|
+
# EM.run
|
44
|
+
#end
|
data/lib/langis.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
##
|
2
|
+
# The base Langis module namespace is used to define global constants for
|
3
|
+
# this library.
|
4
|
+
module Langis
|
5
|
+
|
6
|
+
##
|
7
|
+
# The key in the Rack-like environment whose value is the intake
|
8
|
+
# that received the message.
|
9
|
+
INTAKE_KEY = 'langis.intake'
|
10
|
+
|
11
|
+
##
|
12
|
+
# The key in the Rack-like environment whose value is the message
|
13
|
+
# that is "pumped" into a Langis engine.
|
14
|
+
MESSAGE_KEY = 'langis.message'
|
15
|
+
##
|
16
|
+
# The key in the Rack-like environment whose value is the message
|
17
|
+
# type of the message that is pumped into the Langis engine. This is
|
18
|
+
# set if the message's responds to the #message_type method.
|
19
|
+
MESSAGE_TYPE_KEY = 'langis.message_type'
|
20
|
+
|
21
|
+
##
|
22
|
+
# The key in the Rack-like return headers whose value is the
|
23
|
+
# caught exception raised by any middleware or application.
|
24
|
+
X_EXCEPTION = 'X-Langis-Exception'
|
25
|
+
##
|
26
|
+
# The key in the Rack-like return headers whose value is the name of the
|
27
|
+
# middleware that prevented the message from propagating further in the
|
28
|
+
# application stack.
|
29
|
+
X_FILTERED_BY = 'X-Langis-Filtered-By'
|
30
|
+
##
|
31
|
+
# The key in the Rack-like return headers whose value is the message type
|
32
|
+
# that was filtered. This is
|
33
|
+
X_FILTERED_TYPE = 'X-Langis-Filtered-Type'
|
34
|
+
|
35
|
+
##
|
36
|
+
# The application stack has completed its function successfully. This
|
37
|
+
# return result doesn't signify that the end application of the Rack-like
|
38
|
+
# stack was called; middleware may have completed its defined function.
|
39
|
+
OK = 200
|
40
|
+
##
|
41
|
+
# The status message that states that there was an internal error.
|
42
|
+
SERVER_ERROR = 500
|
43
|
+
|
44
|
+
##
|
45
|
+
# The base exception class for all errors that Langis will raise.
|
46
|
+
class LangisError < RuntimeError
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# We include the Langis Library's hard dependencies.
|
51
|
+
require 'set'
|
52
|
+
require 'blockenspiel'
|
53
|
+
require 'eventmachine'
|
54
|
+
|
55
|
+
# Now we require the library modules itself.
|
56
|
+
require 'langis/middleware'
|
57
|
+
require 'langis/dsl'
|
58
|
+
require 'langis/engine'
|
59
|
+
require 'langis/sinks'
|
60
|
+
require 'langis/rackish'
|
data/lib/langis/dsl.rb
ADDED
@@ -0,0 +1,346 @@
|
|
1
|
+
module Langis
|
2
|
+
|
3
|
+
##
|
4
|
+
# Module that implements the Domain Specific Language which is used
|
5
|
+
# to define Langis publish-subscribe routes.
|
6
|
+
module Dsl
|
7
|
+
|
8
|
+
##
|
9
|
+
# Error that is raised when there is an error in the definition of
|
10
|
+
# a set of routes in the Dsl.
|
11
|
+
class PipingConfigError < LangisError
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# Method to parse the configuration of the routes.
|
16
|
+
#
|
17
|
+
# config = Langis::Dsl.langis_plumbing do
|
18
|
+
# intake :inbound_name do
|
19
|
+
# flow_to :sink_name, :when => [:message_type1, :message_type2]
|
20
|
+
# flow_to :catch_all
|
21
|
+
# end
|
22
|
+
# intake :inbound_name do
|
23
|
+
# # NOTE that we have a second intake block with the same
|
24
|
+
# # :inbound_name name. The configuration in this block
|
25
|
+
# # will be merged with the previously declared block.
|
26
|
+
# # NOTE that the above :catch_all flow to sink can be overwritten
|
27
|
+
# # if we declare a `flow_to :catch_all, :when => [...]`. The
|
28
|
+
# # catch all will be restricted to the types declared in this
|
29
|
+
# # second flow_to.
|
30
|
+
# # NOTE that if we add a `flow_to :sink_name, :when => :type_3`,
|
31
|
+
# # it will add :type_3 to the list of message types already
|
32
|
+
# # declared in the prior block.
|
33
|
+
# end
|
34
|
+
# for_sink :sink_name do
|
35
|
+
# use SinkSpecificMiddlewareApp, :argument1, :argument2
|
36
|
+
# run MyRealApp.new(:argument1, :argument2)
|
37
|
+
# end
|
38
|
+
# for_sink :catch_all do
|
39
|
+
# run lambda { |env| puts Rails.logger.info(env.inspect) }
|
40
|
+
# end
|
41
|
+
# check_valve do
|
42
|
+
# use GlobalMiddlewareApp, :argument1, :argument2
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
# engine = Langis::Engine::EventMachineEngine.new config.build_pipes
|
46
|
+
# engine.pump MyMessage.new(:someinfo), :inbound_name
|
47
|
+
#
|
48
|
+
# @see RackishConfig
|
49
|
+
# @param &block for the dsl configuration
|
50
|
+
# @return [PipesConfig] the parsed configuration of the defined routes.
|
51
|
+
def langis_plumbing(&block)
|
52
|
+
config = PipesConfig.new
|
53
|
+
Blockenspiel.invoke block, config
|
54
|
+
return config
|
55
|
+
end
|
56
|
+
module_function :langis_plumbing
|
57
|
+
|
58
|
+
##
|
59
|
+
# This represents the configuration of the overall Langis piping.
|
60
|
+
# It is the configuration of the message intakes and their corresponding
|
61
|
+
# Rackish applications.
|
62
|
+
#
|
63
|
+
# @see Langis::Dsl#langis_plumbing
|
64
|
+
class PipesConfig
|
65
|
+
include Blockenspiel::DSL
|
66
|
+
|
67
|
+
##
|
68
|
+
#
|
69
|
+
def initialize
|
70
|
+
@intakes = {}
|
71
|
+
@sinks = {}
|
72
|
+
@check_valve = nil
|
73
|
+
end
|
74
|
+
|
75
|
+
##
|
76
|
+
# Dsl only method that parses a sub-block that defines an "intake".
|
77
|
+
# An intake is the "queue" name that a message is sent to, and whose
|
78
|
+
# defined "sinks" (application stacks) are executed in turn.
|
79
|
+
#
|
80
|
+
# @param [#to_s] name The name of the intake to define.
|
81
|
+
# @param [#to_s] *args Additional named aliases of this intake.
|
82
|
+
# @param [Block] &block The dsl configuration block for this intake.
|
83
|
+
# @return [IntakeConfig] The configuration of this intake block.
|
84
|
+
def intake(name, *args, &block)
|
85
|
+
# We require at least one intake to be defined here, and merge
|
86
|
+
# it into a list where other intake names have been defined.
|
87
|
+
intake_names = args.clone
|
88
|
+
intake_names.unshift name
|
89
|
+
intake_names.map! { |n| n.to_s }
|
90
|
+
|
91
|
+
# Here we launch the blockenspiel dsl processing for the intake block.
|
92
|
+
config = IntakeConfig.new
|
93
|
+
Blockenspiel.invoke block, config
|
94
|
+
sink_type_links = config.sink_type_links
|
95
|
+
|
96
|
+
# Iterate over the returned pipes, then only create intakes that have
|
97
|
+
# actual sinks. We don't want to have intakes without sinks.
|
98
|
+
# This also is set up so that we can use multiple intake dsl blocks
|
99
|
+
# to define a single intake (i.e.- intakes with identical names
|
100
|
+
# are only thought of as a single intake.)
|
101
|
+
sink_type_links.each do |sink_name, message_types|
|
102
|
+
intake_names.each do |intake_name|
|
103
|
+
@intakes[intake_name] ||= {}
|
104
|
+
@intakes[intake_name][sink_name] ||= Set.new
|
105
|
+
@intakes[intake_name][sink_name].merge message_types
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
##
|
111
|
+
# Dsl only method to define a "sink", a Rackish application stack.
|
112
|
+
# Subsequent sinks of the same name will overwrite the configuration
|
113
|
+
# of a previously defined sink of that name.
|
114
|
+
#
|
115
|
+
# @param [#to_s] name The name of the sink to define.
|
116
|
+
# @param [Block] &block The dsl configuration block for this sink.
|
117
|
+
# @return [RackishConfig] The Rackish application stack for this sink.
|
118
|
+
def for_sink(name, &block)
|
119
|
+
config = RackishConfig.new
|
120
|
+
Blockenspiel.invoke block, config
|
121
|
+
@sinks[name.to_s] = config
|
122
|
+
end
|
123
|
+
|
124
|
+
##
|
125
|
+
# Dsl only method to define a Rackish application stack that is
|
126
|
+
# prepended to all sinks defined in the Langis config. This is
|
127
|
+
# so one can define a global intercept patch for functionality like
|
128
|
+
# global custom error handling. Note that one SHOULD only declare
|
129
|
+
# `use Middleware` type statements since this stack will be prepended
|
130
|
+
# to the other sinks. A `run` declaration in here will
|
131
|
+
# terminate the execution, or even more likely fail to wire up correctly.
|
132
|
+
#
|
133
|
+
# @param [#to_s] name The name of the sink to define.
|
134
|
+
# @param [Block] &block The dsl configuration block for this sink.
|
135
|
+
# @return [RackishConfig] The Rackish application stack.
|
136
|
+
def check_valve(&block)
|
137
|
+
config = RackishConfig.new
|
138
|
+
Blockenspiel.invoke block, config
|
139
|
+
@check_valve = config
|
140
|
+
end
|
141
|
+
|
142
|
+
dsl_methods false
|
143
|
+
|
144
|
+
##
|
145
|
+
# Creates the sinks (application stacks) and references them in their
|
146
|
+
# assigned intakes. A create sink can be referenced by multiple intakes.
|
147
|
+
#
|
148
|
+
# @return [{String => Array<#call>}] The intake name to list of
|
149
|
+
# created sinks.
|
150
|
+
# @raise [PipingConfigError] Error raised when an intake references
|
151
|
+
# a non-existent sink; we are unable to wire up an app.
|
152
|
+
def build_pipes
|
153
|
+
# Build the main sinks, which may be added to the end of other
|
154
|
+
# defined middleware.
|
155
|
+
built_sinks = {}
|
156
|
+
@sinks.each do |key, value|
|
157
|
+
built_sinks[key] = value.to_app
|
158
|
+
end
|
159
|
+
|
160
|
+
# Full pipes is the final return hash to the caller. Its keys are the
|
161
|
+
# intake names, and its values are Arrays of the sinks. Each sink
|
162
|
+
# is a Rackish application stack.
|
163
|
+
full_pipes = {}
|
164
|
+
@intakes.each do |intake_name, sink_type_links|
|
165
|
+
# Right now, each intake's value is a list of pairs. Each
|
166
|
+
# pair is a sink name and the set of message types it should watch
|
167
|
+
# for. Empty sets mean to do a catch all.
|
168
|
+
sink_type_links.each do |sink_name, message_types|
|
169
|
+
built_sink = built_sinks[sink_name]
|
170
|
+
# We want to confirm that the sink the intake is referencing
|
171
|
+
# actually exists.
|
172
|
+
unless built_sink
|
173
|
+
raise PipingConfigError.new "Sink not found: #{sink_name}"
|
174
|
+
end
|
175
|
+
|
176
|
+
# If any message type was defined to filter in the intaked block,
|
177
|
+
# then we want to create a middleware to filter out all messages
|
178
|
+
# whose type is not in that list. Otherwise we'll just flow
|
179
|
+
# all messages to this defined sink.
|
180
|
+
if message_types.empty?
|
181
|
+
half_pipe = built_sink
|
182
|
+
else
|
183
|
+
half_pipe = ::Langis::Middleware::MessageTypeFilter.new(
|
184
|
+
built_sink, *message_types)
|
185
|
+
end
|
186
|
+
|
187
|
+
# If we have a check_valve defined in the configuration, then
|
188
|
+
# we want to prepend it to the sink.
|
189
|
+
if @check_valve
|
190
|
+
full_pipe = @check_valve.to_app half_pipe
|
191
|
+
else
|
192
|
+
full_pipe = half_pipe
|
193
|
+
end
|
194
|
+
|
195
|
+
# Now add the wired up sink to the list of sinks to be handled
|
196
|
+
# by given intake.
|
197
|
+
full_pipes[intake_name] ||= []
|
198
|
+
full_pipes[intake_name] << full_pipe
|
199
|
+
end
|
200
|
+
end
|
201
|
+
return full_pipes
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
##
|
206
|
+
# Dsl config class used to define an intake.
|
207
|
+
#
|
208
|
+
# @see Langis::Dsl#langis_plumbing
|
209
|
+
class IntakeConfig
|
210
|
+
include Blockenspiel::DSL
|
211
|
+
|
212
|
+
dsl_methods false
|
213
|
+
|
214
|
+
##
|
215
|
+
# Returns the intake's configuration mapping between its sinks (names)
|
216
|
+
# and the list of types to filter for.
|
217
|
+
#
|
218
|
+
# @return [Hash{String => Set<String>}] The sink name to the set of
|
219
|
+
# message types to filter for.
|
220
|
+
attr_reader :sink_type_links
|
221
|
+
|
222
|
+
##
|
223
|
+
#
|
224
|
+
def initialize
|
225
|
+
@sink_type_links = {}
|
226
|
+
end
|
227
|
+
|
228
|
+
dsl_methods true
|
229
|
+
|
230
|
+
##
|
231
|
+
# Dsl only method to define which sinks to propagate a message to.
|
232
|
+
#
|
233
|
+
# @overload flow_to(...)
|
234
|
+
# Flow all messages for the intake to the given sink names.
|
235
|
+
# @param [Array<#to_s>] ... The list of sink names to push messages to.
|
236
|
+
# @overload flow_to(..., options={})
|
237
|
+
# Flow all messages for the intake to the given sink names, but
|
238
|
+
# may be restricted by type if that option is set.
|
239
|
+
# @param [Array<#to_s>] ... The list of sink names to push messages to.
|
240
|
+
# @option options [Array<#to_s>] :when ([]) The list of message types
|
241
|
+
# that should be sent to the sink, all unlisted types are filtered
|
242
|
+
# out. If a sink has zero listed types at the end of the Dsl config,
|
243
|
+
# then ALL messages will be sent to that sink.
|
244
|
+
def flow_to(*args)
|
245
|
+
# Check to see if we have an options hash. Properly pull out the
|
246
|
+
# options, and then make the list of sinks to push to.
|
247
|
+
case args[-1]
|
248
|
+
when Hash
|
249
|
+
options = args[-1]
|
250
|
+
sink_names = args[0...-1].map! { |name| name.to_s }
|
251
|
+
else
|
252
|
+
options = {}
|
253
|
+
sink_names = args.clone.map! { |name| name.to_s }
|
254
|
+
end
|
255
|
+
|
256
|
+
# Coerce the message types to handle into an Array of strings
|
257
|
+
case options[:when]
|
258
|
+
when Array
|
259
|
+
message_types = options[:when].map { |item| item.to_s }
|
260
|
+
when nil
|
261
|
+
# For the nil case, we have an empty array.
|
262
|
+
# The build_pipes method will interpret a sink with an empty set
|
263
|
+
# of types to actually handle all types.
|
264
|
+
message_types = []
|
265
|
+
else
|
266
|
+
message_types = [ options[:when].to_s ]
|
267
|
+
end
|
268
|
+
|
269
|
+
# We add the types to a Set, one set per sink name. This is for the
|
270
|
+
# multiple intake definitions.
|
271
|
+
sink_names.each do |name|
|
272
|
+
@sink_type_links[name] ||= Set.new
|
273
|
+
@sink_type_links[name].merge message_types
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
##
|
279
|
+
# A Dsl class used to define Rackish application stacks. This classes
|
280
|
+
# implementation is heavily inspired by Rack itself.
|
281
|
+
#
|
282
|
+
# Example:
|
283
|
+
# block = proc do
|
284
|
+
# use Middleware, arg1, arg2
|
285
|
+
# run lambda { |env| return [200, {}, env[:input1]] }
|
286
|
+
# end
|
287
|
+
# config = Langis::RackishConfig.new
|
288
|
+
# Blockenspiel.invoke block, config
|
289
|
+
# my_app = config.to_app
|
290
|
+
#
|
291
|
+
# env = {
|
292
|
+
# :input1 => 'Hello World'
|
293
|
+
# }
|
294
|
+
# results = my_app.call env
|
295
|
+
#
|
296
|
+
# @see Langis::Dsl#langis_plumbing
|
297
|
+
class RackishConfig
|
298
|
+
include Blockenspiel::DSL
|
299
|
+
|
300
|
+
dsl_methods false
|
301
|
+
|
302
|
+
##
|
303
|
+
# @param [#call] app Optional endpoint to declare up front.
|
304
|
+
# If nil, then a "no-op" end-point is used with a very basic return.
|
305
|
+
def initialize(app=nil)
|
306
|
+
@ins = []
|
307
|
+
@app = app ? app : lambda { |env| [OK, {}, [""]] }
|
308
|
+
end
|
309
|
+
|
310
|
+
##
|
311
|
+
# The method that actually wires up each middleware and the end point
|
312
|
+
# into a real Rack stack.
|
313
|
+
#
|
314
|
+
# @param [#call] app Optional endpoint to use instead of the one
|
315
|
+
# previously defined by a `run`.
|
316
|
+
# @return [#call] The Rackish application.
|
317
|
+
def to_app(app=nil)
|
318
|
+
app ||= @app
|
319
|
+
@ins.reverse.inject(app) { |a, e| e.call(a) }
|
320
|
+
end
|
321
|
+
|
322
|
+
dsl_methods true
|
323
|
+
|
324
|
+
##
|
325
|
+
# Dsl only method that defines a piece of Middleware to run, in order,
|
326
|
+
# in this Rack-lik application.
|
327
|
+
#
|
328
|
+
# @param [Class] middleware The middleware class to instantiate.
|
329
|
+
# @param *args The arguments to pass to the initialize method for
|
330
|
+
# the middleware class instantiation.
|
331
|
+
# @param &block A code block to pass to the initialize method for
|
332
|
+
# the middleware class instantiation.
|
333
|
+
def use(middleware, *args, &block)
|
334
|
+
@ins << lambda { |app| middleware.new(app, *args, &block) }
|
335
|
+
end
|
336
|
+
|
337
|
+
##
|
338
|
+
# Dsl only method that defines the end point Rack app handler.
|
339
|
+
#
|
340
|
+
# @param [#call] app The Rackish endpoint for this app.
|
341
|
+
def run(app)
|
342
|
+
@app = app
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|