phaedra 0.4.0 → 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +116 -20
- data/example/Dockerfile +46 -0
- data/example/Gemfile +1 -1
- data/example/README.md +19 -0
- data/example/api/env.rb +15 -0
- data/example/api/{the-time.rb → params.rb} +4 -4
- data/example/config.ru +1 -1
- data/example/docker-compose.yml +8 -0
- data/example/phaedra/initializers.rb +14 -0
- data/lib/phaedra.rb +2 -4
- data/lib/phaedra/base.rb +6 -2
- data/lib/phaedra/concerns/callbacks_actionable.rb +6 -6
- data/lib/phaedra/initializers_module.rb +69 -0
- data/lib/phaedra/rack_app.rb +11 -1
- data/lib/phaedra/rack_middleware.rb +2 -0
- data/lib/phaedra/{middleware → rack_middleware}/not_found.rb +0 -0
- data/lib/phaedra/{middleware → rack_middleware}/static.rb +1 -1
- data/lib/phaedra/startup_initializers.rb +9 -0
- data/lib/phaedra/version.rb +1 -1
- metadata +14 -8
- data/example/api/simple.rb +0 -10
- data/lib/phaedra/middleware.rb +0 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 65ebfc3c9c8023c4a680704a085d9b4b62505fce1df1a82ca6d57bd9785c5ec2
|
4
|
+
data.tar.gz: d3384f2fa5d94ba594427970aad49cabe47b1d59d6f2a3289ba608c413e8d3db
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c40e0c8017d3bb172a144b365ecaf62bc1d9b602c575740fefff8e0876ab54d853d1c5434aa2da8b8f02cda11d8f43084b5354d86464195374ee76799fd2e9c5
|
7
|
+
data.tar.gz: 763f60081477658fd4cfad3461cc8f9148c3c1c3e4d7f20d4571e1cf4d136c24c55a6aecfbc2c39c4ec3471bfadf7cca101be51dc7f3bcceb72c5e4eb21a2cdd
|
data/README.md
CHANGED
@@ -1,9 +1,15 @@
|
|
1
1
|
# Phaedra: Serverless Ruby Functions
|
2
2
|
|
3
|
-
Phaedra is a web microframework for writing serverless Ruby functions. They are isolated pieces of logic which respond to HTTP requests (GET, POST, etc.) and typically get mounted at a particular URL path. They can be tested locally and deployed to a supported serverless hosting platform or to any [Rack-compatible web server](https://github.com/rack/rack).
|
3
|
+
Phaedra is a web microframework for writing serverless Ruby functions. They are isolated pieces of logic which respond to HTTP requests (GET, POST, etc.) and typically get mounted at a particular URL path. They can be tested locally and deployed to a supported serverless hosting platform, using a container via Docker & Docker Compose, or to any [Rack-compatible web server](https://github.com/rack/rack).
|
4
|
+
|
5
|
+
Phaedra is well-suited for building an API layer which you attach to a static site (aka [the Jamstack](https://www.bridgetownrb.com/docs/jamstack)) to provide dynamic functionality accessible any time after the static site loads in the browser.
|
4
6
|
|
5
7
|
Serverless compatibility is presently focused on [Vercel](https://vercel.com) and [OpenFaaS](https://openfaas.com), but there are likely additional platforms we'll be adding support for in the future.
|
6
8
|
|
9
|
+
For swift deployment via Docker, we recommend [Fly.io](https://fly.io).
|
10
|
+
|
11
|
+
(P.S. Wondering how you can deploy a static site on [Netlify](https://www.netlify.com) and still use a Ruby API? Scroll down for a suggested approach!)
|
12
|
+
|
7
13
|
## Installation
|
8
14
|
|
9
15
|
Add this line to your application's Gemfile:
|
@@ -24,18 +30,26 @@ Or install it yourself as:
|
|
24
30
|
$ gem install phaedra
|
25
31
|
```
|
26
32
|
|
33
|
+
## Examples
|
34
|
+
|
35
|
+
[Here's an example](https://github.com/whitefusionhq/phaedra/tree/master/example) of what the structure of a typical Phaedra app looks like. It includes `config.ru` for booting it up as a Rack app using Puma, as well as a `Dockerfile` and `docker-compose.yml` so you can run the app containerized in virtually any development or production hosting environment.
|
36
|
+
|
37
|
+
[Here's a demo](https://phaedra-demo.whitefusion.design/api/env) of one of the functions. [And another one.](https://phaedra-demo.whitefusion.design/api/params?search=Waiting%20for%20Guffman)
|
38
|
+
|
27
39
|
## Usage
|
28
40
|
|
29
41
|
Functions are single Ruby files which respond to a URL path (aka `/api/path/to/function`). The path is determined by the location of the file on the filesystem relative to the functions root (aka `api`). So, given a path of `./api/folder/run-me.rb`, the URL path would be `/api/folder/run-me`.
|
30
42
|
|
31
43
|
Functions are written as subclasses of `Phaedra::Base` using the name `PhaedraFunction`. The `params` argument is a Hash containing the parsed contents of the incoming query string, form data, or body JSON. The response object returned by your function is typically a Hash which will be transformed into JSON output automatically, but it can also be plain text.
|
32
44
|
|
45
|
+
Code to be run once upon function initialization and shared between multiple functions should be placed in the `phaedra/initializers.rb` file (see more on that below).
|
46
|
+
|
33
47
|
Some platforms such as Vercel require the function class name to be `Handler`, so you can put that at the bottom of your file for full compatibility.
|
34
48
|
|
35
49
|
Here's a basic example:
|
36
50
|
|
37
51
|
```ruby
|
38
|
-
|
52
|
+
require_relative "../phaedra/initializers"
|
39
53
|
|
40
54
|
class PhaedraFunction < Phaedra::Base
|
41
55
|
def get(params)
|
@@ -60,17 +74,23 @@ Functions can define `action` callbacks:
|
|
60
74
|
```ruby
|
61
75
|
class PhaedraFunction < Phaedra::Base
|
62
76
|
before_action :do_stuff_before
|
63
|
-
after_action
|
77
|
+
after_action do
|
78
|
+
# process response object further...
|
79
|
+
end
|
64
80
|
around_action :do_it_all_around
|
65
81
|
|
66
82
|
def do_stuff_before
|
67
|
-
#
|
83
|
+
# process request object before action handler...
|
68
84
|
end
|
69
85
|
|
70
|
-
|
86
|
+
def do_it_all_around
|
87
|
+
# run code before
|
88
|
+
yield
|
89
|
+
# run code after
|
90
|
+
end
|
71
91
|
|
72
92
|
def get(params)
|
73
|
-
# this will be run within the callback chain
|
93
|
+
# this will be run within the entire callback chain
|
74
94
|
end
|
75
95
|
end
|
76
96
|
```
|
@@ -79,34 +99,51 @@ You can modify the `request` object in a `before_action` callback to perform set
|
|
79
99
|
|
80
100
|
### Shared Code You Only Want to Run Once
|
81
101
|
|
82
|
-
|
102
|
+
Phaedra provides a default location to place shared modules and code that should be run once upon first deployment of your functions. This is particularly useful when setting up a database connection or performing expensive operations you only want to do once, rather than for every request.
|
103
|
+
|
104
|
+
Here's an example of how that works:
|
83
105
|
|
84
106
|
```ruby
|
85
107
|
# api/run-it-once.rb
|
86
108
|
|
87
|
-
|
88
|
-
require_relative "../lib/shared_code"
|
109
|
+
require_relative "../phaedra/initializers"
|
89
110
|
|
90
111
|
class PhaedraFunction < Phaedra::Base
|
91
112
|
def get(params)
|
92
|
-
"Run it once! #{
|
113
|
+
"Run it once! #{Phaedra::Shared.run_once} / #{Time.now}"
|
93
114
|
end
|
94
115
|
end
|
95
116
|
```
|
96
117
|
|
97
118
|
```ruby
|
98
|
-
#
|
119
|
+
# phaedra/initializers.rb
|
120
|
+
|
121
|
+
module Phaedra
|
122
|
+
module Shared
|
123
|
+
Initializers.register self do
|
124
|
+
run_once
|
125
|
+
end
|
99
126
|
|
100
|
-
|
101
|
-
|
102
|
-
|
127
|
+
def self.run_once
|
128
|
+
@only_once ||= Time.now
|
129
|
+
end
|
103
130
|
end
|
104
131
|
end
|
105
132
|
```
|
106
133
|
|
107
|
-
Now each time you invoke the function at `/api/run-it-once`, the
|
134
|
+
Now each time you invoke the function at `/api/run-it-once`, the timestamp will never change until the next redeployment.
|
135
|
+
|
136
|
+
**NOTE:** When running in a Rack-based configuration (see below), Ruby's `load` method is invoked for every request to any Phaedra function. This means Ruby has to parse and compile the code in your function each time. For small functions this happens extremely quickly, but if you find yourself writing a large function and seeing some performance slowdowns, consider extracting most of the function code to additional Ruby files and using the `require_relative` technique as mentioned above. The Ruby code in those required files will only be compiled once and all classes/modules/etc. will be saved in memory until the next redeployment.
|
137
|
+
|
138
|
+
## Environment
|
139
|
+
|
140
|
+
You can set the environment of your Phaedra app using the `PHAEDRA_ENV` environment variable. That is then available via the `Phaedra.environment` method. By default, the value is `:development`.
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
# ENV["PHAEDRA_ENV"] == "production"
|
108
144
|
|
109
|
-
|
145
|
+
Phaedra.environment == :production # true
|
146
|
+
```
|
110
147
|
|
111
148
|
## Deployment
|
112
149
|
|
@@ -118,7 +155,13 @@ All you have to do is create a static site repo ([Bridgetown](https://www.bridge
|
|
118
155
|
|
119
156
|
We recommend using OpenFaaS' dockerfile template so you can define your own `Dockerfile` to book Rack + Phaedra using the Puma web server. This also allows you to customize the Docker image configuration to install and configure other tools as necessary.
|
120
157
|
|
121
|
-
First
|
158
|
+
First make sure you've added Puma to your Gemfile:
|
159
|
+
|
160
|
+
```
|
161
|
+
gem "puma"
|
162
|
+
```
|
163
|
+
|
164
|
+
Then make sure you've pulled down the OpenFaaS template:
|
122
165
|
|
123
166
|
```sh
|
124
167
|
faas-cli template store pull dockerfile
|
@@ -174,7 +217,7 @@ Next add the `config.ru` file to boot Rack:
|
|
174
217
|
```ruby
|
175
218
|
# testphaedra/config.ru
|
176
219
|
|
177
|
-
require "phaedra"
|
220
|
+
require "phaedra/rack_app"
|
178
221
|
|
179
222
|
run Phaedra::RackApp.new
|
180
223
|
```
|
@@ -210,7 +253,7 @@ In case you're wondering: yes, with Phaedra you can write multiple Ruby function
|
|
210
253
|
Booting Phaedra up as Rack app is very simple. All you need to do is add a `config.ru` file alongside your `api` folder:
|
211
254
|
|
212
255
|
```ruby
|
213
|
-
require "phaedra"
|
256
|
+
require "phaedra/rack_app"
|
214
257
|
|
215
258
|
run Phaedra::RackApp.new
|
216
259
|
```
|
@@ -246,6 +289,45 @@ server {
|
|
246
289
|
|
247
290
|
Change the `server_name`, `root`, and `passenger_ruby` paths to your particular setup and you'll be good to go. (If you run into any errors, double-check there's a `config.ru` in the parent folder of your site destination folder.)
|
248
291
|
|
292
|
+
### Docker
|
293
|
+
|
294
|
+
[In the example app provided](https://github.com/whitefusionhq/phaedra/tree/master/example), there is a `config.ru` file for booting it up as a Rack app using Puma. The `Dockerfile` and `docker-compose.yml` files allow you to easily build and deploy the app at port 8080 (but that can easily be changed). Using the Docker Compose commands:
|
295
|
+
|
296
|
+
```sh
|
297
|
+
# Build (if necessary) and deploy:
|
298
|
+
docker-compose up
|
299
|
+
|
300
|
+
# Get information on the running container:
|
301
|
+
docker-compose ps
|
302
|
+
|
303
|
+
# Inspect the output logs:
|
304
|
+
docker-compose logs
|
305
|
+
|
306
|
+
# Exit the running container:
|
307
|
+
docker-compose down
|
308
|
+
|
309
|
+
# If you make changes to the code and need to rebuild:
|
310
|
+
docker-compose up --build
|
311
|
+
```
|
312
|
+
|
313
|
+
#### Fly.io
|
314
|
+
|
315
|
+
Deploying your Phaedra app's Docker container via [Fly.io](https://fly.io) couldn't be easier. Simply create a new app and deploy using Fly.io's command line utility:
|
316
|
+
|
317
|
+
```sh
|
318
|
+
# Create the new app using your Fly.io account:
|
319
|
+
flyctl apps create
|
320
|
+
|
321
|
+
# Deploy using the Dockerfile:
|
322
|
+
flyctl deploy
|
323
|
+
|
324
|
+
# Print out the URL and other info on your new app:
|
325
|
+
flyctl info
|
326
|
+
|
327
|
+
# Change the Phaedra app environment:
|
328
|
+
flyctl secrets set PHAEDRA_ENV=production
|
329
|
+
```
|
330
|
+
|
249
331
|
### WEBrick
|
250
332
|
|
251
333
|
Integrating Phaedra into a WEBrick server is pretty straightforward. Given a `server` object, it can be accomplished thusly:
|
@@ -280,7 +362,21 @@ load File.join(Dir.pwd, "api", "func.rb")
|
|
280
362
|
@server.mount '/path', Handler
|
281
363
|
```
|
282
364
|
|
283
|
-
This method precludes any automatic routing by Phaedra, so it's discouraged unless you are using WEBrick within a larger setup that utilizes its own routing method. (Interestingly enough, that's how Vercel works under the hood.)
|
365
|
+
This method precludes any automatic routing by Phaedra, so it's discouraged unless you are using WEBrick within a larger setup that utilizes its own routing method. (Interestingly enough, [that's how Vercel works under the hood](https://github.com/vercel/vercel/blob/master/packages/now-ruby/now_init.rb).)
|
366
|
+
|
367
|
+
## Connecting a Static Site on Netlify to a Phaedra API
|
368
|
+
|
369
|
+
[Netlify](https://www.netlify.com) is a popular hosting solution for Jamstack (static) sites, but its serverless functions feature doesn't support Ruby. However, using proxy rewrites, you can deploy the static site part of your repository to Netlify and set the `/api` endpoint to route requests to your Phaedra app on the fly (hosted elsewhere).
|
370
|
+
|
371
|
+
For example, if your Phaedra app is hosted on Fly.io (see above), you'll want Netlify's CDN to proxy all requests to `/api/*` to that app's URL. We can accomplish that by adding a `_redirects` file to the static site's source folder (for Bridgetown sites, that's `src`):
|
372
|
+
|
373
|
+
```
|
374
|
+
/api/* https://super-awesome-phaedra-api.fly.dev/api/:splat 200
|
375
|
+
```
|
376
|
+
|
377
|
+
Once that deploys, you can go to your Netlify site URL, append `/api/whatever`, and under-the-hood that will connect to `https://super-awesome-phaedra-api.fly.dev/api/whatever` in a completely transparent manner.
|
378
|
+
|
379
|
+
If you want to change the proxy URL for different contexts (staging vs. production, etc.), you can follow Netlify's "[Separate _redirects files for separate contexts or branches](https://community.netlify.com/t/support-guide-making-redirects-work-for-you-troubleshooting-and-debugging/13433)" instructions here.
|
284
380
|
|
285
381
|
----
|
286
382
|
|
data/example/Dockerfile
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
FROM ruby:2.6-alpine3.11 as builder
|
2
|
+
|
3
|
+
RUN apk add --no-cache --virtual \\
|
4
|
+
#
|
5
|
+
# required
|
6
|
+
bash tzdata build-base libffi-dev \\
|
7
|
+
#
|
8
|
+
# nice to haves
|
9
|
+
curl git
|
10
|
+
|
11
|
+
FROM builder as phaedra-app
|
12
|
+
|
13
|
+
# This is to fix an issue on Linux with permissions issues
|
14
|
+
ARG USER_ID=${USER_ID:-1000}
|
15
|
+
ARG GROUP_ID=${GROUP_ID:-1000}
|
16
|
+
ARG DOCKER_USER=${DOCKER_USER:-user}
|
17
|
+
ARG APP_DIR=${APP_DIR:-/home/user/phaedra-app}
|
18
|
+
|
19
|
+
# Change with --build-arg PHAEDRA_ENV=production
|
20
|
+
ARG PHAEDRA_ENV=development
|
21
|
+
ENV PHAEDRA_ENV=$PHAEDRA_ENV
|
22
|
+
|
23
|
+
# Create a non-root user
|
24
|
+
RUN addgroup -g $GROUP_ID -S $GROUP_ID
|
25
|
+
RUN adduser --disabled-password -G $GROUP_ID --uid $USER_ID -S $DOCKER_USER
|
26
|
+
|
27
|
+
# Create and then own the directory to fix permissions issues
|
28
|
+
RUN mkdir -p $APP_DIR
|
29
|
+
RUN chown -R $USER_ID:$GROUP_ID $APP_DIR
|
30
|
+
|
31
|
+
# Define the user running the container
|
32
|
+
USER $USER_ID:$GROUP_ID
|
33
|
+
|
34
|
+
# . now == $APP_DIR
|
35
|
+
WORKDIR $APP_DIR
|
36
|
+
|
37
|
+
# COPY is run as a root user, not as the USER defined above, so we must chown it
|
38
|
+
COPY --chown=$USER_ID:$GROUP_ID Gemfile* ./
|
39
|
+
RUN gem install bundler
|
40
|
+
RUN bundle install
|
41
|
+
|
42
|
+
COPY --chown=$USER_ID:$GROUP_ID . .
|
43
|
+
|
44
|
+
EXPOSE 8080
|
45
|
+
|
46
|
+
CMD ["bundle", "exec", "rackup", "-p", "8080", "-o", "0.0.0.0"]
|
data/example/Gemfile
CHANGED
data/example/README.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Example Phaedra App
|
2
|
+
|
3
|
+
This will spin up a Phaedra demo API running on port 8080 (which can be changed).
|
4
|
+
|
5
|
+
## Local Exec
|
6
|
+
|
7
|
+
Requires Ruby 2.6 or greater.
|
8
|
+
|
9
|
+
```sh
|
10
|
+
bundle install
|
11
|
+
|
12
|
+
bundle exec rackup -p 8080
|
13
|
+
```
|
14
|
+
|
15
|
+
## Docker Compose
|
16
|
+
|
17
|
+
```sh
|
18
|
+
docker-compose up
|
19
|
+
```
|
data/example/api/env.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require_relative "../phaedra/initializers"
|
2
|
+
|
3
|
+
class PhaedraFunction < Phaedra::Base
|
4
|
+
def get(params)
|
5
|
+
response["Content-Type"] = "text/html; charset=utf-8"
|
6
|
+
<<~HTML
|
7
|
+
<p>Hello friend! 😃</p>
|
8
|
+
<p>Startup Time: #{Phaedra::Shared.the_time}</p>
|
9
|
+
<p>Environment: #{Phaedra.environment}</p>
|
10
|
+
<p>Current Time: #{Time.new}</p>
|
11
|
+
HTML
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
Handler = PhaedraFunction
|
@@ -1,10 +1,10 @@
|
|
1
|
-
|
1
|
+
require_relative "../phaedra/initializers"
|
2
2
|
|
3
3
|
class PhaedraFunction < Phaedra::Base
|
4
4
|
before_action :earlier_stuff
|
5
5
|
|
6
6
|
def get(params)
|
7
|
-
"The
|
7
|
+
"The ?search= param is #{params[:search] || "-missing-"}"
|
8
8
|
end
|
9
9
|
|
10
10
|
def post(params)
|
@@ -14,10 +14,10 @@ class PhaedraFunction < Phaedra::Base
|
|
14
14
|
private
|
15
15
|
|
16
16
|
def earlier_stuff
|
17
|
-
request.query["search"] += "
|
17
|
+
request.query["search"] += " (nice!)" if request.query["search"]
|
18
18
|
|
19
19
|
if request.body
|
20
|
-
request.body.sub!("Works", "
|
20
|
+
request.body.sub!("Works", "Totally Works")
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
data/example/config.ru
CHANGED
@@ -0,0 +1,14 @@
|
|
1
|
+
require "phaedra"
|
2
|
+
require "securerandom"
|
3
|
+
|
4
|
+
module Phaedra
|
5
|
+
module Shared
|
6
|
+
Initializers.register self do
|
7
|
+
the_time SecureRandom.hex(10)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.the_time(init = nil)
|
11
|
+
@the_time ||= "#{Time.now} (random seed: #{init})"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/phaedra.rb
CHANGED
data/lib/phaedra/base.rb
CHANGED
@@ -7,6 +7,10 @@ module Phaedra
|
|
7
7
|
class Base
|
8
8
|
include CallbacksActionable
|
9
9
|
|
10
|
+
before_action do
|
11
|
+
Initializers.run
|
12
|
+
end
|
13
|
+
|
10
14
|
# Used by WEBrick
|
11
15
|
def self.get_instance(server, *options)
|
12
16
|
self.new(server, *options)
|
@@ -112,7 +116,7 @@ module Phaedra
|
|
112
116
|
|
113
117
|
def set_initial_status
|
114
118
|
@res.status = 200
|
115
|
-
@res["Content-Type"] = "application/json"
|
119
|
+
@res["Content-Type"] = "application/json; charset=utf-8"
|
116
120
|
end
|
117
121
|
|
118
122
|
def call_method_action(params)
|
@@ -123,7 +127,7 @@ module Phaedra
|
|
123
127
|
def complete_response
|
124
128
|
if @res.body.is_a?(String) && !@res["Content-Type"].start_with?("text/")
|
125
129
|
@res["Content-Type"] = "text/plain; charset=utf-8"
|
126
|
-
elsif @res["Content-Type"]
|
130
|
+
elsif @res["Content-Type"].start_with? "application/json"
|
127
131
|
@res.body = @res.body.to_json
|
128
132
|
end
|
129
133
|
end
|
@@ -11,14 +11,14 @@ module Phaedra
|
|
11
11
|
end
|
12
12
|
|
13
13
|
module ClassMethods
|
14
|
-
def before_action(*args)
|
15
|
-
set_callback :action, :before, *args
|
14
|
+
def before_action(*args, &block)
|
15
|
+
set_callback :action, :before, *args, &block
|
16
16
|
end
|
17
|
-
def after_action(*args)
|
18
|
-
set_callback :action, :after, *args
|
17
|
+
def after_action(*args, &block)
|
18
|
+
set_callback :action, :after, *args, &block
|
19
19
|
end
|
20
|
-
def around_action(*args)
|
21
|
-
set_callback :action, :around, *args
|
20
|
+
def around_action(*args, &block)
|
21
|
+
set_callback :action, :around, *args, &block
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Phaedra
|
2
|
+
module Initializers
|
3
|
+
Registration = Struct.new(
|
4
|
+
:origin,
|
5
|
+
:priority,
|
6
|
+
:block,
|
7
|
+
keyword_init: true
|
8
|
+
) do
|
9
|
+
def to_s
|
10
|
+
"#{owner}:#{priority} for #{block}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
DEFAULT_PRIORITY = 20
|
15
|
+
|
16
|
+
PRIORITY_MAP = {
|
17
|
+
low: 10,
|
18
|
+
normal: 20,
|
19
|
+
high: 30,
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
# initial empty hooks
|
23
|
+
@registry = []
|
24
|
+
|
25
|
+
NotAvailable = Class.new(RuntimeError)
|
26
|
+
Uncallable = Class.new(RuntimeError)
|
27
|
+
|
28
|
+
# Ensure the priority is a Fixnum
|
29
|
+
def self.priority_value(priority)
|
30
|
+
return priority if priority.is_a?(Integer)
|
31
|
+
|
32
|
+
PRIORITY_MAP[priority] || DEFAULT_PRIORITY
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.register(origin, priority: DEFAULT_PRIORITY, &block)
|
36
|
+
raise Uncallable, "Initializers must respond to :call" unless block.respond_to? :call
|
37
|
+
|
38
|
+
@registry << Registration.new(
|
39
|
+
origin: origin,
|
40
|
+
priority: priority_value(priority),
|
41
|
+
block: block
|
42
|
+
)
|
43
|
+
|
44
|
+
block
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.remove(origin)
|
48
|
+
@registry.delete_if { |item| item.origin == origin }
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.run(force: false)
|
52
|
+
if !@initializers_ran || force
|
53
|
+
prioritized_initializers.each do |initializer|
|
54
|
+
initializer.block.call
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
@initializers_ran = true
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.prioritized_initializers
|
62
|
+
# sort initializers according to priority and load order
|
63
|
+
grouped_initializers = @registry.group_by(&:priority)
|
64
|
+
grouped_initializers.keys.sort.reverse.map do |priority|
|
65
|
+
grouped_initializers[priority]
|
66
|
+
end.flatten
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/phaedra/rack_app.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require "rack"
|
2
|
+
require "phaedra"
|
3
|
+
|
1
4
|
module Phaedra
|
2
5
|
class Request < Rack::Request
|
3
6
|
def query
|
@@ -5,11 +8,18 @@ module Phaedra
|
|
5
8
|
end
|
6
9
|
|
7
10
|
def header
|
8
|
-
@env.dup.transform_keys do |key|
|
11
|
+
@transformed_headers ||= @env.dup.transform_keys do |key|
|
9
12
|
key.respond_to?(:downcase) ? key.downcase : key
|
13
|
+
end.tap do |headers|
|
14
|
+
# TODO: normalize a few common headers
|
15
|
+
headers["authorization"] = headers["http_authorization"]
|
10
16
|
end
|
11
17
|
end
|
12
18
|
|
19
|
+
def [](key)
|
20
|
+
header[key]
|
21
|
+
end
|
22
|
+
|
13
23
|
def body
|
14
24
|
@request_body ||= "" + get_header(Rack::RACK_INPUT).read
|
15
25
|
end
|
File without changes
|
@@ -6,7 +6,7 @@ module Phaedra
|
|
6
6
|
class Static
|
7
7
|
def initialize(app, options)
|
8
8
|
@app = app
|
9
|
-
@try = [".html", "index.html", "/index.html"
|
9
|
+
@try = ["", ".html", "index.html", "/index.html", *options[:try]]
|
10
10
|
@static = Rack::Static.new(
|
11
11
|
lambda { |_| [404, {}, []] },
|
12
12
|
options)
|
data/lib/phaedra/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: phaedra
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jared White
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-06-
|
11
|
+
date: 2020-06-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -80,17 +80,23 @@ files:
|
|
80
80
|
- LICENSE.txt
|
81
81
|
- README.md
|
82
82
|
- Rakefile
|
83
|
+
- example/Dockerfile
|
83
84
|
- example/Gemfile
|
84
|
-
- example/
|
85
|
-
- example/api/
|
85
|
+
- example/README.md
|
86
|
+
- example/api/env.rb
|
87
|
+
- example/api/params.rb
|
86
88
|
- example/config.ru
|
89
|
+
- example/docker-compose.yml
|
90
|
+
- example/phaedra/initializers.rb
|
87
91
|
- lib/phaedra.rb
|
88
92
|
- lib/phaedra/base.rb
|
89
93
|
- lib/phaedra/concerns/callbacks_actionable.rb
|
90
|
-
- lib/phaedra/
|
91
|
-
- lib/phaedra/middleware/not_found.rb
|
92
|
-
- lib/phaedra/middleware/static.rb
|
94
|
+
- lib/phaedra/initializers_module.rb
|
93
95
|
- lib/phaedra/rack_app.rb
|
96
|
+
- lib/phaedra/rack_middleware.rb
|
97
|
+
- lib/phaedra/rack_middleware/not_found.rb
|
98
|
+
- lib/phaedra/rack_middleware/static.rb
|
99
|
+
- lib/phaedra/startup_initializers.rb
|
94
100
|
- lib/phaedra/version.rb
|
95
101
|
- phaedra.gemspec
|
96
102
|
homepage: https://github.com/whitefusionhq/phaedra
|
@@ -112,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
112
118
|
- !ruby/object:Gem::Version
|
113
119
|
version: '0'
|
114
120
|
requirements: []
|
115
|
-
rubygems_version: 3.0.
|
121
|
+
rubygems_version: 3.0.8
|
116
122
|
signing_key:
|
117
123
|
specification_version: 4
|
118
124
|
summary: Write serverless Ruby functions via a REST-like microframework compatible
|
data/example/api/simple.rb
DELETED
data/lib/phaedra/middleware.rb
DELETED