phaedra 0.3.2 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +9 -3
- data/example/Dockerfile +46 -0
- data/example/api/simple.rb +2 -2
- data/example/api/the-time.rb +2 -2
- data/example/config.ru +1 -1
- data/example/docker-compose.yml +8 -0
- data/example/phaedra/initializers.rb +11 -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 +3 -0
- data/lib/phaedra/rack_middleware.rb +2 -0
- data/lib/phaedra/rack_middleware/not_found.rb +21 -0
- data/lib/phaedra/rack_middleware/static.rb +26 -0
- data/lib/phaedra/startup_initializers.rb +9 -0
- data/lib/phaedra/version.rb +1 -1
- metadata +11 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 00d5ba6f3bc10860baef3bf63867665db4ef610b7cf14a8281495cf929339bc3
|
4
|
+
data.tar.gz: 5d1c500a8cbe79a89182497e7881fdeba3d129e5e2f2ddcce19012f9ae713171
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3f3a8145df4a02f5362dfffb66f1de44ccd650425504007f2f49fa666c68bd9d228d0e8ade98daa2d318c57b220013009bd2c9b37d10d375a7cecca2cee0c4ac
|
7
|
+
data.tar.gz: f0effbf926248e56c67da411bd688cc3f6783d7710bdedf141029d523b4e55667df18b1e509cb68aa55795a01b74369ac8e007a91754ac9b3c6046741d166258
|
data/README.md
CHANGED
@@ -118,7 +118,13 @@ All you have to do is create a static site repo ([Bridgetown](https://www.bridge
|
|
118
118
|
|
119
119
|
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
120
|
|
121
|
-
First
|
121
|
+
First make sure you've added Puma to your Gemfile:
|
122
|
+
|
123
|
+
```
|
124
|
+
gem "puma"
|
125
|
+
```
|
126
|
+
|
127
|
+
Then make sure you've pulled down the OpenFaaS template:
|
122
128
|
|
123
129
|
```sh
|
124
130
|
faas-cli template store pull dockerfile
|
@@ -174,7 +180,7 @@ Next add the `config.ru` file to boot Rack:
|
|
174
180
|
```ruby
|
175
181
|
# testphaedra/config.ru
|
176
182
|
|
177
|
-
require "phaedra"
|
183
|
+
require "phaedra/rack_app"
|
178
184
|
|
179
185
|
run Phaedra::RackApp.new
|
180
186
|
```
|
@@ -210,7 +216,7 @@ In case you're wondering: yes, with Phaedra you can write multiple Ruby function
|
|
210
216
|
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
217
|
|
212
218
|
```ruby
|
213
|
-
require "phaedra"
|
219
|
+
require "phaedra/rack_app"
|
214
220
|
|
215
221
|
run Phaedra::RackApp.new
|
216
222
|
```
|
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=staging
|
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/api/simple.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
|
1
|
+
require_relative "../phaedra/initializers"
|
2
2
|
|
3
3
|
class PhaedraFunction < Phaedra::Base
|
4
4
|
def get(params)
|
5
5
|
response["Content-Type"] = "text/html; charset=utf-8"
|
6
|
-
"<p
|
6
|
+
"<p>😁 #{Phaedra.the_time} - #{ENV["PHAEDRA_ENV"]} - #{Time.new}</p>"
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
data/example/api/the-time.rb
CHANGED
@@ -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]}"
|
8
8
|
end
|
9
9
|
|
10
10
|
def post(params)
|
data/example/config.ru
CHANGED
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
@@ -0,0 +1,21 @@
|
|
1
|
+
module Phaedra
|
2
|
+
module Middleware
|
3
|
+
class NotFound
|
4
|
+
def initialize(app, path, content_type = 'text/html; charset=utf-8')
|
5
|
+
@app = app
|
6
|
+
@content = File.read(path)
|
7
|
+
@length = @content.bytesize.to_s
|
8
|
+
@content_type = content_type
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
response = @app.call(env)
|
13
|
+
if response[0] == 404
|
14
|
+
[404, {'Content-Type' => @content_type, 'Content-Length' => @length}, [@content]]
|
15
|
+
else
|
16
|
+
response
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Phaedra
|
2
|
+
module Middleware
|
3
|
+
# Based on Rack::TryStatic middleware
|
4
|
+
# https://github.com/rack/rack-contrib/blob/master/lib/rack/contrib/try_static.rb
|
5
|
+
|
6
|
+
class Static
|
7
|
+
def initialize(app, options)
|
8
|
+
@app = app
|
9
|
+
@try = ["", ".html", "index.html", "/index.html", *options[:try]]
|
10
|
+
@static = Rack::Static.new(
|
11
|
+
lambda { |_| [404, {}, []] },
|
12
|
+
options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
orig_path = env['PATH_INFO']
|
17
|
+
found = nil
|
18
|
+
@try.each do |path|
|
19
|
+
resp = @static.call(env.merge!({'PATH_INFO' => orig_path + path}))
|
20
|
+
break if !(403..405).include?(resp[0]) && found = resp
|
21
|
+
end
|
22
|
+
found or @app.call(env.merge!('PATH_INFO' => orig_path))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
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.2
|
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-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -80,14 +80,22 @@ files:
|
|
80
80
|
- LICENSE.txt
|
81
81
|
- README.md
|
82
82
|
- Rakefile
|
83
|
+
- example/Dockerfile
|
83
84
|
- example/Gemfile
|
84
85
|
- example/api/simple.rb
|
85
86
|
- example/api/the-time.rb
|
86
87
|
- example/config.ru
|
88
|
+
- example/docker-compose.yml
|
89
|
+
- example/phaedra/initializers.rb
|
87
90
|
- lib/phaedra.rb
|
88
91
|
- lib/phaedra/base.rb
|
89
92
|
- lib/phaedra/concerns/callbacks_actionable.rb
|
93
|
+
- lib/phaedra/initializers_module.rb
|
90
94
|
- lib/phaedra/rack_app.rb
|
95
|
+
- lib/phaedra/rack_middleware.rb
|
96
|
+
- lib/phaedra/rack_middleware/not_found.rb
|
97
|
+
- lib/phaedra/rack_middleware/static.rb
|
98
|
+
- lib/phaedra/startup_initializers.rb
|
91
99
|
- lib/phaedra/version.rb
|
92
100
|
- phaedra.gemspec
|
93
101
|
homepage: https://github.com/whitefusionhq/phaedra
|
@@ -109,7 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
109
117
|
- !ruby/object:Gem::Version
|
110
118
|
version: '0'
|
111
119
|
requirements: []
|
112
|
-
rubygems_version: 3.0.
|
120
|
+
rubygems_version: 3.0.8
|
113
121
|
signing_key:
|
114
122
|
specification_version: 4
|
115
123
|
summary: Write serverless Ruby functions via a REST-like microframework compatible
|