Skip to content

Commit b7b0ba6

Browse files
Introduce Protocol::HTTP::Middleware.load. (#97)
1 parent 9b3250f commit b7b0ba6

File tree

3 files changed

+209
-7
lines changed

3 files changed

+209
-7
lines changed

lib/protocol/http/middleware/builder.rb

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,22 @@ def initialize(default_app = NotFound)
1818
@app = default_app
1919
end
2020

21+
# Build the middleware application using the given block.
22+
#
23+
# @parameter block [Proc] The block to pass to the middleware constructor.
24+
# @returns [Builder] The builder.
25+
def build(&block)
26+
if block_given?
27+
if block.arity == 0
28+
instance_exec(&block)
29+
else
30+
yield self
31+
end
32+
end
33+
34+
return self
35+
end
36+
2137
# Use the given middleware with the given arguments and options.
2238
#
2339
# @parameter middleware [Class | Object] The middleware class to use.
@@ -44,19 +60,34 @@ def to_app
4460
end
4561

4662
# Build a middleware application using the given block.
47-
def self.build(&block)
48-
builder = Builder.new
63+
def self.build(*arguments, &block)
64+
builder = Builder.new(*arguments)
65+
66+
builder.build(&block)
67+
68+
return builder.to_app
69+
end
70+
71+
# Load a middleware application from the given path.
72+
#
73+
# @parameter path [String] The path to the middleware application.
74+
# @parameter arguments [Array] The arguments to pass to the middleware constructor.
75+
# @parameter options [Hash] The options to pass to the middleware constructor.
76+
# @parameter block [Proc] The block to pass to the middleware constructor.
77+
def self.load(path, *arguments, &block)
78+
builder = Builder.new(*arguments)
79+
80+
binding = Builder::TOPLEVEL_BINDING.call(builder)
81+
eval(File.read(path), binding, path)
4982

5083
if block_given?
51-
if block.arity == 0
52-
builder.instance_exec(&block)
53-
else
54-
yield builder
55-
end
84+
builder.build(&block)
5685
end
5786

5887
return builder.to_app
5988
end
6089
end
6190
end
6291
end
92+
93+
Protocol::HTTP::Middleware::Builder::TOPLEVEL_BINDING = ->(builder){builder.instance_eval{binding}}

releases.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Releases
22

3+
## Unreleased
4+
5+
- Introduce `Protocol::HTTP::Middleware.load` method for loading middleware applications from files.
6+
37
## v0.58.1
48

59
- `Protocol::HTTP::DuplicateHeaderError` now includes the existing and new values for better debugging.

test/protocol/http/middleware/builder.rb

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
require "protocol/http/middleware"
77
require "protocol/http/middleware/builder"
8+
require "tempfile"
89

910
describe Protocol::HTTP::Middleware::Builder do
1011
it "can make an app" do
@@ -41,4 +42,170 @@
4142

4243
expect(app).to be_a(Protocol::HTTP::Middleware)
4344
end
45+
46+
it "can initialize with custom default app" do
47+
builder = Protocol::HTTP::Middleware::Builder.new(Protocol::HTTP::Middleware::Okay)
48+
49+
expect(builder.to_app).to be_equal(Protocol::HTTP::Middleware::Okay)
50+
end
51+
52+
it "can build without block" do
53+
builder = Protocol::HTTP::Middleware::Builder.new
54+
result = builder.build
55+
56+
expect(result).to be_equal(builder)
57+
expect(builder.to_app).to be_equal(Protocol::HTTP::Middleware::NotFound)
58+
end
59+
60+
it "can build with zero-arity block using instance_exec" do
61+
builder = Protocol::HTTP::Middleware::Builder.new
62+
63+
builder.build do
64+
use Protocol::HTTP::Middleware
65+
end
66+
67+
expect(builder.to_app).to be_a(Protocol::HTTP::Middleware)
68+
end
69+
70+
it "can use middleware with arguments" do
71+
middleware_class = Class.new(Protocol::HTTP::Middleware) do
72+
def initialize(app, argument1, argument2)
73+
super(app)
74+
@argument1 = argument1
75+
@argument2 = argument2
76+
end
77+
78+
attr :argument1, :argument2
79+
end
80+
81+
builder = Protocol::HTTP::Middleware::Builder.new(Protocol::HTTP::Middleware::Okay)
82+
builder.use(middleware_class, "test1", "test2")
83+
84+
app = builder.to_app
85+
expect(app).to be_a(middleware_class)
86+
expect(app.argument1).to be == "test1"
87+
expect(app.argument2).to be == "test2"
88+
end
89+
90+
it "can use middleware with options" do
91+
middleware_class = Class.new(Protocol::HTTP::Middleware) do
92+
def initialize(app, option1:, option2:)
93+
super(app)
94+
@option1 = option1
95+
@option2 = option2
96+
end
97+
98+
attr :option1, :option2
99+
end
100+
101+
builder = Protocol::HTTP::Middleware::Builder.new(Protocol::HTTP::Middleware::Okay)
102+
builder.use(middleware_class, option1: "value1", option2: "value2")
103+
104+
app = builder.to_app
105+
expect(app).to be_a(middleware_class)
106+
expect(app.option1).to be == "value1"
107+
expect(app.option2).to be == "value2"
108+
end
109+
110+
it "can use middleware with block" do
111+
middleware_class = Class.new(Protocol::HTTP::Middleware) do
112+
def initialize(app, &block)
113+
super(app)
114+
@block = block
115+
end
116+
117+
attr :block
118+
end
119+
120+
block_called = false
121+
builder = Protocol::HTTP::Middleware::Builder.new(Protocol::HTTP::Middleware::Okay)
122+
builder.use(middleware_class) do
123+
block_called = true
124+
end
125+
126+
app = builder.to_app
127+
expect(app).to be_a(middleware_class)
128+
expect(app.block).to be_a(Proc)
129+
app.block.call
130+
expect(block_called).to be == true
131+
end
132+
133+
it "can run to set default app" do
134+
builder = Protocol::HTTP::Middleware::Builder.new
135+
builder.run(Protocol::HTTP::Middleware::Okay)
136+
137+
expect(builder.to_app).to be_equal(Protocol::HTTP::Middleware::Okay)
138+
end
139+
140+
it "can chain multiple middleware" do
141+
middleware1 = Class.new(Protocol::HTTP::Middleware) do
142+
def initialize(app)
143+
super(app)
144+
@name = "middleware1"
145+
end
146+
147+
attr :name
148+
end
149+
150+
middleware2 = Class.new(Protocol::HTTP::Middleware) do
151+
def initialize(app)
152+
super(app)
153+
@name = "middleware2"
154+
end
155+
156+
attr :name
157+
end
158+
159+
builder = Protocol::HTTP::Middleware::Builder.new(Protocol::HTTP::Middleware::Okay)
160+
builder.use(middleware1)
161+
builder.use(middleware2)
162+
163+
app = builder.to_app
164+
# Middleware are reversed, so first added becomes outermost
165+
expect(app).to be_a(middleware1)
166+
expect(app.delegate).to be_a(middleware2)
167+
expect(app.delegate.delegate).to be_equal(Protocol::HTTP::Middleware::Okay)
168+
end
169+
170+
it "can convert to app directly" do
171+
builder = Protocol::HTTP::Middleware::Builder.new(Protocol::HTTP::Middleware::HelloWorld)
172+
173+
app = builder.to_app
174+
expect(app).to be_equal(Protocol::HTTP::Middleware::HelloWorld)
175+
end
176+
177+
it "can load middleware from file" do
178+
temp_file = Tempfile.new(["middleware", ".rb"])
179+
temp_file.write(<<~RUBY)
180+
use Protocol::HTTP::Middleware
181+
run Protocol::HTTP::Middleware::HelloWorld
182+
RUBY
183+
temp_file.close
184+
185+
app = Protocol::HTTP::Middleware.load(temp_file.path)
186+
187+
expect(app).to be_a(Protocol::HTTP::Middleware)
188+
expect(app.delegate).to be_equal(Protocol::HTTP::Middleware::HelloWorld)
189+
190+
ensure
191+
temp_file&.unlink
192+
end
193+
194+
it "can load middleware from file with block" do
195+
temp_file = Tempfile.new(["middleware", ".rb"])
196+
temp_file.write(<<~RUBY)
197+
use Protocol::HTTP::Middleware
198+
RUBY
199+
temp_file.close
200+
201+
app = Protocol::HTTP::Middleware.load(temp_file.path) do
202+
run Protocol::HTTP::Middleware::HelloWorld
203+
end
204+
205+
expect(app).to be_a(Protocol::HTTP::Middleware)
206+
expect(app.delegate).to be_equal(Protocol::HTTP::Middleware::HelloWorld)
207+
208+
ensure
209+
temp_file&.unlink
210+
end
44211
end

0 commit comments

Comments
 (0)