ratalada 1.0.1 → 2.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f80ed576156e0bc9751e4c89c18c8901b5ba75b72801b526f7ade9b9affa7a26
4
- data.tar.gz: 0b2e102f641cd58328435fc665296b00122806ef7e5d931df171ce820777f725
3
+ metadata.gz: a9932c4c02d4733e5a9b9ea33cc28f284b24bf9556eaae84fb70c6e8c2175ff9
4
+ data.tar.gz: b40aa17c1c6f96247fb8fdb965d97830cba834cb65dba986f562476d8fd65569
5
5
  SHA512:
6
- metadata.gz: 19a103db2359a8993ca9953312614a3b861a9a46472f1b37340f27b4f523a238d523970e9617f8bcf6cbf84cbbca6a7a621d65417818dd33ecd3475ce3fbb424
7
- data.tar.gz: bf6ee3f9bfbdfc8aebac210fc14ce72745607487c1019018393cd2e1ba44e4956a05b29ab49e722ab0470b4cd68c66da3fb11738b2b1e94688510644d9c429bf
6
+ metadata.gz: 42c4fe9a65c44ee9801bbfc61e1366fa87d5545a56b634565998f307a5bd591bd460d9e0fde0b26213f4c5684a5d4fad93365e3cd0b96807cf039a5e978dac06
7
+ data.tar.gz: c5bb3fb1395ba10ba2e7aa58ce514631fe97c10bcc25d24a0048949a8c8ded24bbc410397c2ec8845fae5e2d4d0cf52723387a6e5db35852e58793b667f4a4d2
data/README.md CHANGED
@@ -26,17 +26,20 @@ The core `ratalada` gem is the router, the backends, and the DSL. It has no
26
26
  runtime dependencies of its own — install whichever server you run on (`puma`
27
27
  or `falcon`).
28
28
 
29
- The Sinatra and Grape DSLs are optional add-ons, each its own gem:
29
+ The Sinatra, Grape, Hanami::API and Roda DSLs are optional add-ons, each its
30
+ own gem:
30
31
 
31
32
  ```bash
32
33
  gem install ratalada-sinatra # enables require "ratalada/sinatra"
33
34
  gem install ratalada-grape # enables require "ratalada/grape"
35
+ gem install ratalada-hanami # enables require "ratalada/hanami"
36
+ gem install ratalada-roda # enables require "ratalada/roda"
34
37
  ```
35
38
 
36
39
  They ship separately because their dependencies conflict (Grape needs
37
- `mustermann` 4, Sinatra needs `mustermann` 3), so bundling both into `ratalada`
38
- would force you to pick one. **The require path is the same either way**:
39
- install `ratalada-grape`, then `require "ratalada/grape"`. The adapter file
40
+ `mustermann` 4; Sinatra and Hanami::API need `mustermann` 3), so bundling them
41
+ into `ratalada` would force you to pick one. **The require path is the same
42
+ either way**: install `ratalada-grape`, then `require "ratalada/grape"`. The adapter file
40
43
  lives under the shared `ratalada/` namespace on the load path, so the `require`
41
44
  never names the gem — only the file you want.
42
45
 
@@ -100,6 +103,102 @@ Server.run do
100
103
  end
101
104
  ```
102
105
 
106
+ ### Middleware
107
+
108
+ `Server.use` chains, and `run` ends the chain:
109
+
110
+ ```ruby
111
+ Server.use(Rack::CommonLogger).use(Rack::Deflater).run do |request|
112
+ case request
113
+ in ["GET", "/"] then "hello\n"
114
+ end
115
+ end
116
+ ```
117
+
118
+ These are plain rack middleware — they wrap the finished app and are handed
119
+ the `env`, so every middleware gem works unchanged — and they are built once at
120
+ boot, not per request. The first `use` in the chain is the outermost, as in
121
+ `Rack::Builder`. It works with any frontend, including Sinatra and Grape.
122
+
123
+ If you want rack's own builder DSL instead, `ratalada/builder` makes the
124
+ `Server.run` block a `Rack::Builder` block — `use`, `map` and `run`, built once:
125
+
126
+ ```ruby
127
+ require "ratalada/puma"
128
+ require "ratalada/builder"
129
+
130
+ Server.run do
131
+ use ExampleMiddleware
132
+ map("/admin") { run AdminApp }
133
+
134
+ run ->(env) { [200, { "content-type" => "text/plain" }, ["hello\n"]] }
135
+ end
136
+ ```
137
+
138
+ That frontend is rack all the way down, so there is no `Request` sugar: `run`
139
+ hands you the raw env, and you return a full triplet yourself. It ships in the
140
+ core gem (rack is already there via whichever backend you run).
141
+
142
+ Or Hanami::API, from `ratalada-hanami`:
143
+
144
+ ```ruby
145
+ require "ratalada/falcon"
146
+ require "ratalada/hanami"
147
+
148
+ Server.run do
149
+ get "/" do
150
+ "hello\n"
151
+ end
152
+
153
+ get "/users/:id" do
154
+ json(id: params[:id])
155
+ end
156
+
157
+ # Hanami::API scopes middleware by path — this wraps /admin only.
158
+ scope "admin" do
159
+ use RequestId
160
+
161
+ get "/" do
162
+ "admin\n"
163
+ end
164
+ end
165
+ end
166
+ ```
167
+
168
+ Or Roda's routing tree, from `ratalada-roda`:
169
+
170
+ ```ruby
171
+ require "ratalada/falcon"
172
+ require "ratalada/roda"
173
+
174
+ Server.run do
175
+ plugin :json
176
+
177
+ route do |r|
178
+ r.root do
179
+ "hello\n"
180
+ end
181
+
182
+ # Branch once, act on the way down, match the verb last.
183
+ r.on "users", Integer do |id|
184
+ user = User[id]
185
+
186
+ r.get "posts" do
187
+ user.posts
188
+ end
189
+
190
+ r.is do
191
+ user
192
+ end
193
+ end
194
+ end
195
+ end
196
+ ```
197
+
198
+ The block is Roda's class body, so `plugin` and `use` are available next to
199
+ `route`. The app is built with `.freeze.app`, Roda's recommended production
200
+ setup.
201
+
103
202
  Requiring a frontend only changes how the block builds the app, not which
104
203
  server runs it. Each of these adapters is a separate gem (`ratalada-sinatra`,
105
204
  `ratalada-grape`), but the `require "ratalada/<name>"` line is all your code
@@ -140,6 +239,20 @@ bin/test # run the tests
140
239
  bin/console # interactive prompt
141
240
  ```
142
241
 
242
+ One Gemfile covers the whole repository. None of its own gems are in the
243
+ bundle — the devshell puts `lib/` on `RUBYLIB` — so no lockfile records a
244
+ version and a bump has nothing to keep in step.
245
+
246
+ Each gem carries its own version, changelog (`CHANGELOG-<gem>.md`) and tag
247
+ (`<gem>-v1.2.3`), and is released on its own schedule with
248
+ [gem_kit-release](https://github.com/n-at-han-k/gem_kit):
249
+
250
+ ```bash
251
+ gem kit bump patch --gem ratalada-roda # move the version
252
+ gem kit changelog --write --gem ratalada-roda
253
+ gem kit release --gem ratalada-roda # gate, build, push, tag
254
+ ```
255
+
143
256
  ## License
144
257
 
145
258
  [MIT](LICENSE)
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack"
4
+ require_relative "../ratalada"
5
+
6
+ module Ratalada
7
+ module Frontends
8
+ # Rack::Builder as the DSL: the Server.run block is a rack builder block,
9
+ # so `use`, `map` and `run` are rack's own, built once at boot. No Request
10
+ # sugar here — `run` hands the app a raw env, like any rack app.
11
+ module Builder
12
+ def self.build(block)
13
+ ::Rack::Builder.new(&block).to_app
14
+ end
15
+ end
16
+ end
17
+
18
+ self.frontend = Frontends::Builder
19
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ratalada
4
- VERSION = "1.0.1"
4
+ VERSION = "2.0.0"
5
5
  end
data/lib/ratalada.rb CHANGED
@@ -82,21 +82,21 @@ module Ratalada
82
82
 
83
83
  private
84
84
 
85
- def respond(handler, request)
86
- case handler
87
- when nil
88
- [404, { "content-type" => "text/plain" }, ["not found"]]
89
- when Proc, Method
90
- respond(handler.call(request), request)
91
- when String
92
- [200, { "content-type" => "text/plain" }, [handler]]
93
- when Array
94
- status, headers, body = handler
95
- [status, headers, body.is_a?(String) ? [body] : body]
96
- else
97
- handler
85
+ def respond(handler, request)
86
+ case handler
87
+ when nil
88
+ [404, { "content-type" => "text/plain" }, ["not found"]]
89
+ when Proc, Method
90
+ respond(handler.call(request), request)
91
+ when String
92
+ [200, { "content-type" => "text/plain" }, [handler]]
93
+ when Array
94
+ status, headers, body = handler
95
+ [status, headers, body.is_a?(String) ? [body] : body]
96
+ else
97
+ handler
98
+ end
98
99
  end
99
- end
100
100
  end
101
101
  end
102
102
  end
@@ -104,15 +104,44 @@ module Ratalada
104
104
  module Server
105
105
  module_function
106
106
 
107
+ # Server.use(Middleware).use(Other).run { ... } — plain rack middleware
108
+ # (it wraps the built app, so it is handed the env), instantiated once at
109
+ # boot and applied outside whichever frontend built the app.
110
+ def use(middleware, *args, &block)
111
+ Stack.new.use(middleware, *args, &block)
112
+ end
113
+
107
114
  # count runs that many worker processes accepting from a shared socket,
108
115
  # like node's cluster module. Each worker has its own state — anything
109
116
  # shared (sessions, caches) needs an external store or count: 1.
110
117
  def run(host: DEFAULT_HOST, port: DEFAULT_PORT, count: DEFAULT_COUNT, &block)
111
- raise ArgumentError, "Server.run requires a block" unless block
112
- raise ArgumentError, "count must be a positive Integer" unless count.is_a?(Integer) && count.positive?
118
+ Stack.new.run(host: host, port: port, count: count, &block)
119
+ end
113
120
 
114
- app = Ratalada.frontend.build(block)
115
- Ratalada.backend.run(app, host: host, port: port, count: count)
121
+ # The chain Server.use returns. Collects middleware until run ends it.
122
+ class Stack
123
+ def initialize
124
+ @middleware = []
125
+ end
126
+
127
+ def use(middleware, *args, &block)
128
+ @middleware << [middleware, args, block]
129
+ self
130
+ end
131
+
132
+ def run(host: DEFAULT_HOST, port: DEFAULT_PORT, count: DEFAULT_COUNT, &block)
133
+ raise ArgumentError, "Server.run requires a block" unless block
134
+ raise ArgumentError, "count must be a positive Integer" unless count.is_a?(Integer) && count.positive?
135
+
136
+ Ratalada.backend.run(to_app(block), host: host, port: port, count: count)
137
+ end
138
+
139
+ # First `use` in the chain is the outermost, as in Rack::Builder.
140
+ def to_app(block)
141
+ @middleware.reverse.inject(Ratalada.frontend.build(block)) do |inner, (klass, args, blk)|
142
+ klass.new(inner, *args, &blk)
143
+ end
144
+ end
116
145
  end
117
146
  end
118
147
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ratalada
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan K
@@ -107,6 +107,7 @@ files:
107
107
  - README.md
108
108
  - exe/ratalada
109
109
  - lib/ratalada.rb
110
+ - lib/ratalada/builder.rb
110
111
  - lib/ratalada/falcon.rb
111
112
  - lib/ratalada/puma.rb
112
113
  - lib/ratalada/version.rb