langis 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|