Skip to content

Commit df1960b

Browse files
dblockCopilot
andcommitted
Expose Rack Server Pages middleware configuration
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 5f688b3 commit df1960b

6 files changed

Lines changed: 93 additions & 3 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
### Changelog
22

3-
### 2.2.2 (Next)
3+
### 2.3.0 (Next)
44

5+
* [#183](https://github.com/slack-ruby/slack-ruby-bot-server/pull/183): Add a `config.server_pages` block to configure page middleware options centrally without changing the installed library's defaults - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/apps/copilot-swe-agent).
56
* Your contribution here.
67

78
### 2.2.1 (2026/10/24)

‎README.md‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Build a complete Slack bot service with Slack button integration, in Ruby.
2727
- [Extensions](#extensions)
2828
- [Service Class](#service-class)
2929
- [HTML Templates](#html-templates)
30+
- [Page Middleware Configuration](#page-middleware-configuration)
3031
- [Access Tokens](#access-tokens)
3132
- [Sample Bots Using Slack Ruby Bot Server](#sample-bots-using-slack-ruby-bot-server)
3233
- [Copyright & License](#copyright--license)
@@ -307,6 +308,25 @@ SlackRubyBotServer.configure do |config|
307308
end
308309
```
309310

311+
#### Page Middleware Configuration
312+
313+
Use `config.server_pages` to configure the underlying [`Rack::ServerPages`](https://github.com/migrs/rack-server-pages) middleware in an initializer, before `SlackRubyBotServer::Api::Middleware.instance` is first accessed. The block receives its configuration object after the framework applies `view_paths`, so it can customize any option supported by your installed `rack-server-pages` version.
314+
315+
For example, with `rack-server-pages` 0.3.0 or newer:
316+
317+
```ruby
318+
SlackRubyBotServer.configure do |config|
319+
config.server_pages do |pages|
320+
pages.cache_control = 'private, no-cache'
321+
pages.static_cache_control = 'public, no-cache'
322+
end
323+
end
324+
```
325+
326+
`cache_control` sets the default `Cache-Control` header for rendered templates, including generated CSS. `static_cache_control` sets it for static files. `no-cache` allows storage but requires revalidation before reuse; use `private, no-store` for sensitive pages. You can also configure `static_etags` and `static_timestamps` with version 0.3.0 or newer. These settings do not change downstream API responses.
327+
328+
Without a block, the framework preserves the installed library's defaults and existing `view_paths` behavior. Registering another block replaces the previous one, and `SlackRubyBotServer::Config.reset!` clears it. The block runs when the middleware stack is built, not on each request. If you change configuration after initialization, rebuild the stack with `SlackRubyBotServer::Api::Middleware.reset!`; configure it before startup in production.
329+
310330
### Access Tokens
311331

312332
By default the implementation of [Team](lib/slack-ruby-bot-server/models/team) stores the value of the token with all the requested OAuth scopes in both `token` and `activated_user_access_token` (for backwards compatibility), along with `oauth_version` and `oauth_scope`. If a legacy Slack bot integration `bot_access_token` is present, it is stored as `token`, and `activated_user_access_token` is the token that has all the requested OAuth scopes.

‎lib/slack-ruby-bot-server/api/middleware.rb‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ def self.instance
3030

3131
use Rack::ServerPages do |config|
3232
config.view_path = SlackRubyBotServer::Config.view_paths
33+
SlackRubyBotServer::Config.server_pages&.call(config)
3334
end
3435

3536
run Middleware.new

‎lib/slack-ruby-bot-server/config.rb‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ def reset!
1414
self.service_class = SlackRubyBotServer::Service
1515
self.oauth_scope = nil
1616
self.oauth_version = :v2
17+
@server_pages = nil
1718

1819
self.view_paths = [
1920
'views',
@@ -56,6 +57,11 @@ def oauth_scope_s
5657
oauth_scope&.join(',')
5758
end
5859

60+
def server_pages(&block)
61+
@server_pages = block if block
62+
@server_pages
63+
end
64+
5965
def activerecord?
6066
database_adapter == :activerecord
6167
end
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module SlackRubyBotServer
2-
VERSION = '2.2.2'.freeze
2+
VERSION = '2.3.0'.freeze
33
end

‎spec/api/middleware_spec.rb‎

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
require 'spec_helper'
2+
require 'tmpdir'
3+
require 'fileutils'
24

35
describe SlackRubyBotServer::Api::Middleware do
6+
before { described_class.reset! }
7+
8+
after { described_class.reset! }
9+
410
def middleware_classes(app)
511
r = [app]
612

@@ -13,11 +19,11 @@ def middleware_classes(app)
1319

1420
context 'overriding view_paths' do
1521
before do
16-
SlackRubyBotServer::Api::Middleware.reset!
1722
SlackRubyBotServer.configure do |config|
1823
config.view_paths << 'custom'
1924
end
2025
end
26+
2127
it 'uses custom view paths' do
2228
server_pages = middleware_classes(SlackRubyBotServer::Api::Middleware.instance)[-2]
2329
expect(server_pages).to be_a Rack::ServerPages
@@ -26,4 +32,60 @@ def middleware_classes(app)
2632
expect(config[:view_path][-1]).to eq 'custom'
2733
end
2834
end
35+
36+
context 'configuring server pages' do
37+
let(:directory) { Dir.mktmpdir('bot-server-pages') }
38+
let(:request) { Rack::MockRequest.new(described_class.instance) }
39+
40+
before do
41+
File.write(File.join(directory, 'index.html.erb'), '<%= "Rendered" %>')
42+
SlackRubyBotServer.config.view_paths = [directory]
43+
end
44+
45+
after { FileUtils.remove_entry(directory) }
46+
47+
it 'preserves default template caching when no block is configured' do
48+
response = request.get('/')
49+
expect(response.body).to eq 'Rendered'
50+
expect(response['Cache-Control']).to be_nil
51+
end
52+
53+
it 'applies the block after the default view paths and only when building the stack' do
54+
calls = []
55+
SlackRubyBotServer.configure do |config|
56+
config.server_pages do |pages|
57+
calls << pages.view_path
58+
pages.cache_control = 'private, no-cache'
59+
end
60+
end
61+
expect(request.get('/')['Cache-Control']).to eq 'private, no-cache'
62+
request.get('/')
63+
expect(calls).to eq [[directory]]
64+
end
65+
66+
it 'allows the block to override the framework view paths' do
67+
SlackRubyBotServer.config.view_paths = []
68+
path = directory
69+
SlackRubyBotServer.config.server_pages { |pages| pages.view_path = path }
70+
expect(request.get('/').body).to eq 'Rendered'
71+
end
72+
73+
it 'replaces a previously registered block' do
74+
SlackRubyBotServer.config.server_pages { |pages| pages.cache_control = 'public' }
75+
SlackRubyBotServer.config.server_pages { |pages| pages.cache_control = 'private, no-store' }
76+
expect(request.get('/')['Cache-Control']).to eq 'private, no-store'
77+
end
78+
79+
it 'clears the block when configuration is reset' do
80+
SlackRubyBotServer.config.server_pages { |pages| pages.cache_control = 'private, no-store' }
81+
SlackRubyBotServer.config.reset!
82+
SlackRubyBotServer.config.view_paths = [directory]
83+
expect(request.get('/')['Cache-Control']).to be_nil
84+
end
85+
86+
it 'does not hide configuration errors' do
87+
SlackRubyBotServer.config.server_pages { raise ArgumentError, 'Invalid page configuration' }
88+
expect { described_class.instance }.to raise_error ArgumentError, 'Invalid page configuration'
89+
end
90+
end
2991
end

0 commit comments

Comments
 (0)