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 +4 -4
- data/README.md +117 -4
- data/lib/ratalada/builder.rb +19 -0
- data/lib/ratalada/version.rb +1 -1
- data/lib/ratalada.rb +47 -18
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a9932c4c02d4733e5a9b9ea33cc28f284b24bf9556eaae84fb70c6e8c2175ff9
|
|
4
|
+
data.tar.gz: b40aa17c1c6f96247fb8fdb965d97830cba834cb65dba986f562476d8fd65569
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
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
|
|
38
|
-
would force you to pick one. **The require path is the same
|
|
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
|
data/lib/ratalada/version.rb
CHANGED
data/lib/ratalada.rb
CHANGED
|
@@ -82,21 +82,21 @@ module Ratalada
|
|
|
82
82
|
|
|
83
83
|
private
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
112
|
-
|
|
118
|
+
Stack.new.run(host: host, port: port, count: count, &block)
|
|
119
|
+
end
|
|
113
120
|
|
|
114
|
-
|
|
115
|
-
|
|
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:
|
|
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
|