simple-httpd 0.3.4 → 0.3.5
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/.envrc +7 -0
- data/.rubocop.yml +3 -0
- data/VERSION +1 -1
- data/examples/ex1/root.rb +8 -0
- data/examples/ex3/example_service.rb +1 -3
- data/lib/simple/httpd.rb +11 -11
- data/lib/simple/httpd/base_controller.rb +6 -7
- data/lib/simple/httpd/base_controller/cors.rb +13 -12
- data/lib/simple/httpd/base_controller/error_handling.rb +35 -7
- data/lib/simple/httpd/base_controller/origin.rb +8 -0
- data/lib/simple/httpd/base_controller/result.rb +15 -15
- data/lib/simple/httpd/cli.rb +5 -10
- data/lib/simple/httpd/helpers.rb +19 -1
- data/lib/simple/httpd/rack/dynamic_mount.rb +21 -13
- data/lib/simple/httpd/reloader.rb +61 -0
- data/lib/simple/httpd/server.rb +18 -8
- data/lib/simple/httpd/{service_adapter.rb → service_integration.rb} +53 -25
- data/simple-httpd.gemspec +1 -0
- data/spec/spec_helper.rb +3 -1
- metadata +7 -5
- data/bin/rubocop +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 00457b4ed11c8a501e43a4c4826ef6d78722b072ffff0bab1b0c609c797e14f7
|
4
|
+
data.tar.gz: ed35d788fba032b31b971f29e07b23cf490ffc1d96a3aff9369bc812bda22dde
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0d57c201cad87a16655eb6e05a4b527176603ab063d08878b44711741babd3fb62140224b5e7cfd0c6ff3e2937c5d177c6ffcb4d88143903f99d36bd5d937f02
|
7
|
+
data.tar.gz: a55d03f273b5a634262a55916c15690bf845641f69af2217a82d4a778b84af089cdb3722df89c25d4d1d847a2654f4c858c141900115e895c8fa3d8ef55dbc4b
|
data/.envrc
ADDED
data/.rubocop.yml
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.3.
|
1
|
+
0.3.5
|
data/examples/ex1/root.rb
CHANGED
data/lib/simple/httpd.rb
CHANGED
@@ -3,17 +3,6 @@
|
|
3
3
|
module Simple
|
4
4
|
end
|
5
5
|
|
6
|
-
class Simple::Httpd
|
7
|
-
end
|
8
|
-
|
9
|
-
require "simple/service"
|
10
|
-
require "simple/httpd/helpers"
|
11
|
-
require "simple/httpd/base_controller"
|
12
|
-
require "simple/httpd/version"
|
13
|
-
require "simple/httpd/mount"
|
14
|
-
require "simple/httpd/server"
|
15
|
-
require "simple/httpd/service_adapter"
|
16
|
-
|
17
6
|
class Simple::Httpd
|
18
7
|
SELF = self
|
19
8
|
|
@@ -39,7 +28,18 @@ class Simple::Httpd
|
|
39
28
|
def self.logger=(logger)
|
40
29
|
@logger = logger
|
41
30
|
end
|
31
|
+
end
|
42
32
|
|
33
|
+
require "simple/service"
|
34
|
+
require "simple/httpd/helpers"
|
35
|
+
require "simple/httpd/reloader"
|
36
|
+
require "simple/httpd/base_controller"
|
37
|
+
require "simple/httpd/version"
|
38
|
+
require "simple/httpd/mount"
|
39
|
+
require "simple/httpd/server"
|
40
|
+
require "simple/httpd/service_integration"
|
41
|
+
|
42
|
+
class Simple::Httpd
|
43
43
|
# Converts the passed in args into a Simple::Httpd application.
|
44
44
|
#
|
45
45
|
# The passed in arguments are used to create a Simple::Httpd object.
|
@@ -2,18 +2,17 @@
|
|
2
2
|
# config/routes.rb, **after** this file is loaded. See the end of this file.
|
3
3
|
|
4
4
|
require "sinatra/base"
|
5
|
+
::Sinatra::Request.include(::Simple::Httpd::Helpers::RequestHeader)
|
5
6
|
|
6
7
|
class Simple::Httpd::BaseController < Sinatra::Base
|
7
8
|
set :logging, true
|
8
9
|
|
9
|
-
|
10
|
+
extend Simple::Httpd::Reloader
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
# require "sinatra/reloader"
|
16
|
-
# register Sinatra::Reloader
|
12
|
+
def dispatch!
|
13
|
+
self.class.reload! if ::Simple::Httpd.env == "development"
|
14
|
+
|
15
|
+
super
|
17
16
|
end
|
18
17
|
end
|
19
18
|
|
@@ -1,21 +1,22 @@
|
|
1
1
|
class Simple::Httpd::BaseController
|
2
|
-
|
3
|
-
|
4
|
-
"access-control-allow-headers" => "Origin,X-Requested-With,Content-Type,Accept,Session-Id",
|
5
|
-
"access-control-allow-methods" => "*",
|
6
|
-
"access-control-allow-origin" => "*",
|
7
|
-
# "access-control-expose-headers" => "X-Total-Entries,X-Total-Pages,X-Page,X-Per-Page",
|
8
|
-
# "access-control-max-age" => "-1",
|
9
|
-
"access-control-max-age" => "600",
|
10
|
-
"access-control-request-headers" => "Content-Type",
|
11
|
-
"access-control-request-method" => "GET,POST,PATCH,PUT,DELETE,OPTIONS"
|
12
|
-
}
|
2
|
+
# see https://fetch.spec.whatwg.org/#http-responses
|
3
|
+
# see https://stackoverflow.com/questions/24264574/cors-headers-present-only-on-preflight-or-every-request
|
13
4
|
|
14
5
|
options "*" do
|
6
|
+
# The Access-Control max age setting is cached for up to 1 day. This value
|
7
|
+
# is capped at different values depending on the browser.
|
8
|
+
headers "Access-Control-Max-Age" => "86400"
|
9
|
+
headers "Access-Control-Allow-Methods" => "*"
|
10
|
+
headers "Access-Control-Allow-Headers" => "Origin,X-Requested-With,Content-Type,Accept,Session-Id"
|
11
|
+
headers "Access-Control-Expose-Headers" => "X-Total-Entries,X-Total-Pages,X-Page,X-Per-Page"
|
12
|
+
|
15
13
|
200
|
16
14
|
end
|
17
15
|
|
18
16
|
after do
|
19
|
-
headers
|
17
|
+
# This set of CORS headers must be set on each request.
|
18
|
+
headers "Access-Control-Allow-Credentials" => "true",
|
19
|
+
"Access-Control-Allow-Origin" => origin,
|
20
|
+
"Vary" => "Accept-Encoding, Origin"
|
20
21
|
end
|
21
22
|
end
|
@@ -1,5 +1,20 @@
|
|
1
|
+
# rubocop:disable Metrics/ClassLength, Lint/Void
|
2
|
+
# rubocop:disable Metrics/AbcSize
|
3
|
+
|
1
4
|
require_relative "./json"
|
2
5
|
|
6
|
+
# reimplement Sinatra's NotFound handler.
|
7
|
+
#
|
8
|
+
# The original renders HTML; this is not really useful for us.
|
9
|
+
Sinatra::Base
|
10
|
+
class Sinatra::Base
|
11
|
+
error ::Sinatra::NotFound do
|
12
|
+
content_type "text/plain"
|
13
|
+
|
14
|
+
"Don't know how to handle: #{request.request_method} '#{request.path_info}'"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
3
18
|
class Simple::Httpd::BaseController
|
4
19
|
H = ::Simple::Httpd::Helpers
|
5
20
|
|
@@ -33,10 +48,25 @@ class Simple::Httpd::BaseController
|
|
33
48
|
options["description"] ||= options["title"]
|
34
49
|
options["@type"] = error_type(exc)
|
35
50
|
options["@now"] = Time.now.to_f
|
51
|
+
options["@request"] = "#{request.request_method} #{request.path}"
|
52
|
+
|
53
|
+
if Simple::Httpd.env == "development" || Simple::Httpd.env == "test"
|
54
|
+
options["@headers"] = request_headers_for_debugging
|
55
|
+
options["@backtrace"] = exc.backtrace[0, 10]
|
56
|
+
end
|
36
57
|
|
37
58
|
json options
|
38
59
|
end
|
39
60
|
|
61
|
+
def request_headers_for_debugging
|
62
|
+
request.headers.each_with_object({}) do |(key, value), hsh|
|
63
|
+
next if /^(Host|Version|Connection|User-Agent|Accept-Encoding)$/ =~ key
|
64
|
+
next if key == "Accept" && value == "*/*"
|
65
|
+
|
66
|
+
hsh[key] = value
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
40
70
|
if defined?(Expectation)
|
41
71
|
|
42
72
|
error(Expectation::Matcher::Mismatch) do |exc|
|
@@ -57,8 +87,6 @@ class Simple::Httpd::BaseController
|
|
57
87
|
# end
|
58
88
|
|
59
89
|
error(ArgumentError) do |exc|
|
60
|
-
STDERR.puts "Caught ArgumentError: #{exc}, from\n\t#{exc.backtrace[0, 5].join("\n\t")}"
|
61
|
-
|
62
90
|
render_error exc, status: 422,
|
63
91
|
title: "Invalid input #{exc.inspect}",
|
64
92
|
description: error_description(exc)
|
@@ -75,16 +103,16 @@ class Simple::Httpd::BaseController
|
|
75
103
|
class NotAuthorizedError < RuntimeError
|
76
104
|
end
|
77
105
|
|
106
|
+
def not_authorized!(msg)
|
107
|
+
raise NotAuthorizedError, msg
|
108
|
+
end
|
109
|
+
|
78
110
|
error(NotAuthorizedError) do
|
79
111
|
render_error e, status: 403,
|
80
112
|
title: "Not authorized",
|
81
113
|
description: "You don't have necessary powers to access this page."
|
82
114
|
end
|
83
115
|
|
84
|
-
def not_authorized!(msg)
|
85
|
-
raise NotAuthorizedError, msg
|
86
|
-
end
|
87
|
-
|
88
116
|
# -- login required.---------------------------------------------------------
|
89
117
|
|
90
118
|
class LoginRequiredError < RuntimeError
|
@@ -123,7 +151,7 @@ class Simple::Httpd::BaseController
|
|
123
151
|
|
124
152
|
# -- print unspecified errors.
|
125
153
|
|
126
|
-
if
|
154
|
+
if ::Simple::Httpd.env == "development"
|
127
155
|
error do |exc|
|
128
156
|
content_type :text
|
129
157
|
status 500
|
@@ -1,19 +1,19 @@
|
|
1
1
|
class ::Simple::Httpd::BaseController
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
2
|
+
helpers do
|
3
|
+
# encodes the result, according to its payload.
|
4
|
+
#
|
5
|
+
# This function is used by the service integration code, but
|
6
|
+
# is potentially useful outside.
|
7
|
+
def encode_result(result)
|
8
|
+
case result
|
9
|
+
when Array, Hash
|
10
|
+
json(result)
|
11
|
+
when String
|
12
|
+
content_type :text
|
13
|
+
result
|
14
|
+
else
|
15
|
+
result
|
16
|
+
end
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
data/lib/simple/httpd/cli.rb
CHANGED
@@ -55,6 +55,7 @@ module Simple::Httpd::CLI
|
|
55
55
|
port: port)
|
56
56
|
end
|
57
57
|
|
58
|
+
# rubocop:disable Metrics/AbcSize
|
58
59
|
def routes(*mounts, environment: "development", services: nil)
|
59
60
|
prepare_environment!(environment: environment)
|
60
61
|
app = build_app!(mounts: mounts, services: services)
|
@@ -65,11 +66,11 @@ module Simple::Httpd::CLI
|
|
65
66
|
max_verb_len = routes.map(&:verb).map(&:length).max
|
66
67
|
max_path_len = routes.map(&:path).map(&:length).max
|
67
68
|
|
68
|
-
routes
|
69
|
-
sort_by { |route| [route.path, route.verb] }
|
70
|
-
each
|
69
|
+
routes
|
70
|
+
.sort_by { |route| [route.path, route.verb] }
|
71
|
+
.each do |route|
|
71
72
|
puts format("%#{max_verb_len}s %-#{max_path_len}s %s", route.verb, route.path, route.source_location_str)
|
72
|
-
|
73
|
+
end
|
73
74
|
end
|
74
75
|
|
75
76
|
private
|
@@ -113,12 +114,6 @@ module Simple::Httpd::CLI
|
|
113
114
|
end
|
114
115
|
end
|
115
116
|
|
116
|
-
def stderr_logger
|
117
|
-
logger = ::Logger.new STDERR
|
118
|
-
logger.level = ::Logger::INFO
|
119
|
-
logger
|
120
|
-
end
|
121
|
-
|
122
117
|
def start_simplecov
|
123
118
|
require "simplecov"
|
124
119
|
|
data/lib/simple/httpd/helpers.rb
CHANGED
@@ -1,4 +1,15 @@
|
|
1
1
|
module Simple::Httpd::Helpers
|
2
|
+
module RequestHeader
|
3
|
+
def headers
|
4
|
+
env.each_with_object({}) do |(key, value), hsh|
|
5
|
+
next unless key =~ /\AHTTP_(.*)/
|
6
|
+
|
7
|
+
key = $1.split("_").collect(&:capitalize).join("-")
|
8
|
+
hsh[key] = value
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
2
13
|
extend self
|
3
14
|
|
4
15
|
private
|
@@ -16,6 +27,10 @@ module Simple::Httpd::Helpers
|
|
16
27
|
def shorten_path(path)
|
17
28
|
path = File.absolute_path(path)
|
18
29
|
|
30
|
+
shorten_absolute_path(path)
|
31
|
+
end
|
32
|
+
|
33
|
+
def shorten_absolute_path(path)
|
19
34
|
if path.start_with?(pwd)
|
20
35
|
path = path[pwd.length..-1]
|
21
36
|
path = File.join("./", path) if path =~ /\//
|
@@ -52,8 +67,11 @@ module Simple::Httpd::Helpers
|
|
52
67
|
raise "Missing description" unless description
|
53
68
|
|
54
69
|
subclass = Class.new(klass)
|
70
|
+
subclass.define_singleton_method(:description) { description }
|
55
71
|
subclass.define_method(:inspect) { description } if description
|
56
|
-
|
72
|
+
|
73
|
+
::Simple::Httpd::Reloader.attach(subclass, paths: Array(paths))
|
74
|
+
|
57
75
|
subclass
|
58
76
|
end
|
59
77
|
|
@@ -9,13 +9,17 @@ class Simple::Httpd::Rack::DynamicMount
|
|
9
9
|
|
10
10
|
extend Forwardable
|
11
11
|
|
12
|
-
delegate :call => :@rack_app # rubocop:disable Style/HashSyntax
|
13
|
-
|
14
12
|
def self.build(mount_point, path)
|
15
13
|
expect! path => String
|
16
14
|
new(mount_point, path)
|
17
15
|
end
|
18
16
|
|
17
|
+
def call(env)
|
18
|
+
reload! if ::Simple::Httpd.env == "development"
|
19
|
+
|
20
|
+
@rack_app.call(env)
|
21
|
+
end
|
22
|
+
|
19
23
|
attr_reader :path
|
20
24
|
attr_reader :mount_point
|
21
25
|
|
@@ -24,7 +28,8 @@ class Simple::Httpd::Rack::DynamicMount
|
|
24
28
|
@path = path.gsub(/\/\z/, "") # remove trailing "/"
|
25
29
|
|
26
30
|
setup_paths!
|
27
|
-
|
31
|
+
::Simple::Httpd::Reloader.attach self, paths: service_files, reloading_instance: nil
|
32
|
+
|
28
33
|
@root_controller = build_root_controller # also loads helpers
|
29
34
|
@url_map = build_url_map
|
30
35
|
|
@@ -47,25 +52,26 @@ class Simple::Httpd::Rack::DynamicMount
|
|
47
52
|
logger.info "#{path}: found #{@source_paths.count} sources, #{@helper_paths.count} helpers"
|
48
53
|
end
|
49
54
|
|
50
|
-
def
|
51
|
-
|
55
|
+
def service_files
|
56
|
+
@service_files ||= _service_files
|
57
|
+
end
|
58
|
+
|
59
|
+
def _service_files
|
60
|
+
return [] if path == "." # i.e. mounting current directory
|
52
61
|
|
53
62
|
service_path = "#{path}.services"
|
54
|
-
|
55
|
-
return if service_files.empty?
|
63
|
+
return [] unless Dir.exist?(service_path)
|
56
64
|
|
65
|
+
service_files = Dir.glob("#{service_path}/**/*.rb").sort
|
57
66
|
logger.info "#{service_path}: loading #{service_files.count} service file(s)"
|
58
|
-
service_files
|
59
|
-
logger.debug "Loading service file #{path.inspect}"
|
60
|
-
load path
|
61
|
-
end
|
67
|
+
service_files
|
62
68
|
end
|
63
69
|
|
64
70
|
# wraps all helpers into a Simple::Httpd::BaseController subclass
|
65
71
|
def build_root_controller
|
66
72
|
H.subclass ::Simple::Httpd::BaseController,
|
67
73
|
paths: @helper_paths.sort,
|
68
|
-
description: "root controller
|
74
|
+
description: "<root controller: #{H.shorten_path path}>"
|
69
75
|
end
|
70
76
|
|
71
77
|
def build_url_map
|
@@ -73,7 +79,9 @@ class Simple::Httpd::Rack::DynamicMount
|
|
73
79
|
relative_path = absolute_path[(path.length)..-1]
|
74
80
|
|
75
81
|
relative_mount_point = relative_path == "/root.rb" ? "/" : relative_path.gsub(/\.rb$/, "")
|
76
|
-
controller_class = H.subclass @root_controller,
|
82
|
+
controller_class = H.subclass @root_controller,
|
83
|
+
paths: absolute_path,
|
84
|
+
description: "<controller:#{H.shorten_absolute_path(absolute_path)}>"
|
77
85
|
|
78
86
|
controller_class.route_descriptions.each do |route|
|
79
87
|
route = route.prefix(@mount_point, relative_mount_point)
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Simple::Httpd::Reloader
|
2
|
+
def self.attach(target, paths:, reloading_instance: target)
|
3
|
+
target.extend self
|
4
|
+
target.load!(paths: paths, reloading_instance: reloading_instance)
|
5
|
+
target
|
6
|
+
end
|
7
|
+
|
8
|
+
H = ::Simple::Httpd::Helpers
|
9
|
+
|
10
|
+
attr_accessor :reloading_paths
|
11
|
+
|
12
|
+
def load!(paths:, reloading_instance:)
|
13
|
+
paths = Array(paths)
|
14
|
+
paths = nil if paths.empty?
|
15
|
+
|
16
|
+
@__reload_paths__ = paths
|
17
|
+
@__reloading_instance__ = reloading_instance
|
18
|
+
|
19
|
+
reload_all_changed_files
|
20
|
+
end
|
21
|
+
|
22
|
+
def reload!
|
23
|
+
# if this is a class, and its superclass is also reloadable,
|
24
|
+
# reload the superclass first.
|
25
|
+
if respond_to?(:superclass) && superclass&.respond_to?(:reload!)
|
26
|
+
superclass.reload!
|
27
|
+
end
|
28
|
+
|
29
|
+
reload_all_changed_files
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def reload_all_changed_files
|
35
|
+
return unless @__reload_paths__
|
36
|
+
|
37
|
+
@__reload_paths__.each do |path|
|
38
|
+
reload_file_if_necessary(path)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def reload_file_if_necessary(path)
|
43
|
+
@__source_mtimes_by_path__ ||= {}
|
44
|
+
|
45
|
+
mtime = File.mtime(path)
|
46
|
+
return if @__source_mtimes_by_path__[path] == mtime
|
47
|
+
|
48
|
+
Simple::Httpd.logger.debug do
|
49
|
+
verb = @__source_mtimes_by_path__.key?(path) ? "reloading" : "loading"
|
50
|
+
"#{verb} #{H.shorten_path path}"
|
51
|
+
end
|
52
|
+
|
53
|
+
if @__reloading_instance__
|
54
|
+
@__reloading_instance__.instance_eval File.read(path), path, 1
|
55
|
+
else
|
56
|
+
load path
|
57
|
+
end
|
58
|
+
|
59
|
+
@__source_mtimes_by_path__[path] = mtime
|
60
|
+
end
|
61
|
+
end
|
data/lib/simple/httpd/server.rb
CHANGED
@@ -18,19 +18,29 @@ class Simple::Httpd
|
|
18
18
|
|
19
19
|
::Simple::Httpd.logger.info "Starting httpd server on http://#{host}:#{port}/"
|
20
20
|
|
21
|
+
app = ::Rack::CommonLogger.new(app)
|
21
22
|
app = ::Rack::Lint.new(app) if environment != "production"
|
22
23
|
|
23
24
|
# re/AccessLog: the AccessLog setting points WEBrick's access logging to the
|
24
25
|
# NullLogger object.
|
25
26
|
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
27
|
+
# We do not set the environment. Rack is using this to load different
|
28
|
+
# default middlewares (ShowException, Lint, CommonLogger) depending on
|
29
|
+
# the environment setting (which should be either "development" or
|
30
|
+
# "deployment").
|
31
|
+
server_opts = {
|
32
|
+
app: app,
|
33
|
+
Host: host,
|
34
|
+
Port: port,
|
35
|
+
Logger: build_logger,
|
36
|
+
AccessLog: [[NullLogger, ""]]
|
37
|
+
}
|
38
|
+
|
39
|
+
unless ::Simple::Httpd.env == "development"
|
40
|
+
server_opts.update workers: 4, min_threads: 4
|
41
|
+
end
|
42
|
+
|
43
|
+
::Rack::Server.start server_opts
|
34
44
|
end
|
35
45
|
|
36
46
|
private
|
@@ -1,19 +1,29 @@
|
|
1
1
|
require "simple-service"
|
2
2
|
|
3
|
-
module Simple::Httpd::
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
instance_eval do
|
8
|
-
def dispatch!
|
9
|
-
::Simple::Service.with_context(context)
|
10
|
-
super
|
11
|
-
ensure
|
12
|
-
::Simple::Service.context = nil
|
13
|
-
end
|
3
|
+
module Simple::Httpd::ServiceIntegration
|
4
|
+
class Adapter
|
5
|
+
extend Forwardable
|
14
6
|
|
15
|
-
|
7
|
+
def initialize(simple_service)
|
8
|
+
@simple_service = simple_service
|
16
9
|
end
|
10
|
+
|
11
|
+
def action(action_name)
|
12
|
+
::Simple::Service.action(@simple_service, action_name)
|
13
|
+
end
|
14
|
+
|
15
|
+
def invoke(name, args: {}, flags: {})
|
16
|
+
::Simple::Service.invoke @simple_service, name, args: args, flags: flags
|
17
|
+
end
|
18
|
+
|
19
|
+
def invoke3(name, *args, **flags)
|
20
|
+
::Simple::Service.invoke3 @simple_service, name, *args, **flags
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def mount_service(service)
|
25
|
+
@service = Adapter.new(service)
|
26
|
+
yield(@service)
|
17
27
|
ensure
|
18
28
|
@service = nil
|
19
29
|
end
|
@@ -52,16 +62,18 @@ module Simple::Httpd::ServiceAdapter
|
|
52
62
|
def install_route(verb, path, opts, &block)
|
53
63
|
if service_route?(verb, path, opts, &block)
|
54
64
|
path, action_name = *path.first
|
55
|
-
|
65
|
+
install_service_shortcut(verb, path, action_name)
|
66
|
+
elsif @service
|
67
|
+
install_service_route(verb, path, opts, &block)
|
56
68
|
else
|
57
|
-
|
69
|
+
install_non_service_route(verb, path, opts, &block)
|
58
70
|
end
|
59
71
|
end
|
60
72
|
|
61
|
-
def
|
73
|
+
def install_service_shortcut(verb, path, action_name)
|
62
74
|
# Fetch action's source_location. This also verifies that the action
|
63
75
|
# is defined in the first place.
|
64
|
-
action =
|
76
|
+
action = @service.action(action_name)
|
65
77
|
|
66
78
|
describe_route!(verb: verb, path: path, source_location: action.source_location)
|
67
79
|
|
@@ -72,27 +84,43 @@ module Simple::Httpd::ServiceAdapter
|
|
72
84
|
# define sinatra route.
|
73
85
|
route(verb, path) do
|
74
86
|
::Simple::Service.with_context(context) do
|
75
|
-
result =
|
87
|
+
result = service.invoke(action_name, args: parsed_body, flags: stringified_params)
|
76
88
|
encode_result(result)
|
89
|
+
rescue Exception => e
|
90
|
+
Simple::Httpd.logger.warn "#{e}, from\n #{e.backtrace[0,10].join("\n ")}"
|
91
|
+
raise
|
77
92
|
end
|
78
93
|
end
|
79
94
|
end
|
80
95
|
|
81
|
-
def
|
96
|
+
def install_service_route(verb, path, opts, &block)
|
82
97
|
describe_route!(verb: verb, path: path, source_location: block.source_location) if block
|
83
98
|
|
84
99
|
route(verb, path, opts) do
|
85
|
-
|
86
|
-
|
87
|
-
|
100
|
+
::Simple::Service.with_context(context) do
|
101
|
+
result = instance_eval(&block)
|
102
|
+
unless headers["Content-Type"]
|
103
|
+
result = encode_result(result)
|
104
|
+
end
|
105
|
+
result
|
106
|
+
rescue Exception => e
|
107
|
+
Simple::Httpd.logger.warn "#{e}, from\n #{e.backtrace[0,10].join("\n ")}"
|
108
|
+
raise
|
88
109
|
end
|
89
|
-
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def install_non_service_route(verb, path, opts, &block)
|
114
|
+
describe_route!(verb: verb, path: path, source_location: block.source_location) if block
|
115
|
+
|
116
|
+
route(verb, path, opts) do
|
117
|
+
instance_eval(&block)
|
90
118
|
end
|
91
119
|
end
|
92
120
|
|
93
121
|
module Helpers
|
94
122
|
def stringified_params
|
95
|
-
params.each_with_object({}) do |(k,v), hsh|
|
123
|
+
params.each_with_object({}) do |(k, v), hsh|
|
96
124
|
hsh[k.to_s] = v
|
97
125
|
end
|
98
126
|
end
|
@@ -105,5 +133,5 @@ module Simple::Httpd::ServiceAdapter
|
|
105
133
|
end
|
106
134
|
end
|
107
135
|
|
108
|
-
::Simple::Httpd::BaseController.extend(::Simple::Httpd::
|
109
|
-
::Simple::Httpd::BaseController.helpers(::Simple::Httpd::
|
136
|
+
::Simple::Httpd::BaseController.extend(::Simple::Httpd::ServiceIntegration)
|
137
|
+
::Simple::Httpd::BaseController.helpers(::Simple::Httpd::ServiceIntegration::Helpers)
|
data/simple-httpd.gemspec
CHANGED
@@ -22,6 +22,7 @@ Gem::Specification.new do |gem|
|
|
22
22
|
# dependencies
|
23
23
|
gem.add_dependency "neatjson", "~> 0.8.4"
|
24
24
|
gem.add_dependency "sinatra", "~> 2"
|
25
|
+
# gem.add_dependency "async_sinatra" #, "~> 2"
|
25
26
|
# gem.add_dependency "sinatra-reloader", "~> 1"
|
26
27
|
gem.add_dependency "expectation", "~> 1"
|
27
28
|
gem.add_dependency "simple-cli", "~> 0.3.5"
|
data/spec/spec_helper.rb
CHANGED
@@ -33,7 +33,9 @@ end
|
|
33
33
|
Dir.glob("./spec/support/**/*.rb").sort.each { |path| load path }
|
34
34
|
|
35
35
|
require "simple/httpd"
|
36
|
-
|
36
|
+
#Simple::Httpd.env = "test"
|
37
|
+
if ::Simple::Httpd.env == "development"
|
38
|
+
end
|
37
39
|
RSpec.configure do |config|
|
38
40
|
config.run_all_when_everything_filtered = true
|
39
41
|
config.filter_run focus: (ENV["CI"] != "true")
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple-httpd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- radiospiel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-12-
|
11
|
+
date: 2019-12-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: neatjson
|
@@ -87,6 +87,7 @@ executables:
|
|
87
87
|
extensions: []
|
88
88
|
extra_rdoc_files: []
|
89
89
|
files:
|
90
|
+
- ".envrc"
|
90
91
|
- ".gitignore"
|
91
92
|
- ".rubocop.yml"
|
92
93
|
- ".tm_properties"
|
@@ -99,7 +100,6 @@ files:
|
|
99
100
|
- bin/console
|
100
101
|
- bin/rake
|
101
102
|
- bin/rspec
|
102
|
-
- bin/rubocop
|
103
103
|
- bin/simple-httpd
|
104
104
|
- examples/README.md
|
105
105
|
- examples/ex1.services/ex1_service_module.rb
|
@@ -127,6 +127,7 @@ files:
|
|
127
127
|
- lib/simple/httpd/base_controller/debug.rb
|
128
128
|
- lib/simple/httpd/base_controller/error_handling.rb
|
129
129
|
- lib/simple/httpd/base_controller/json.rb
|
130
|
+
- lib/simple/httpd/base_controller/origin.rb
|
130
131
|
- lib/simple/httpd/base_controller/request.rb
|
131
132
|
- lib/simple/httpd/base_controller/result.rb
|
132
133
|
- lib/simple/httpd/base_controller/x_processing.rb
|
@@ -137,9 +138,10 @@ files:
|
|
137
138
|
- lib/simple/httpd/rack/dynamic_mount.rb
|
138
139
|
- lib/simple/httpd/rack/merger.rb
|
139
140
|
- lib/simple/httpd/rack/static_mount.rb
|
141
|
+
- lib/simple/httpd/reloader.rb
|
140
142
|
- lib/simple/httpd/route.rb
|
141
143
|
- lib/simple/httpd/server.rb
|
142
|
-
- lib/simple/httpd/
|
144
|
+
- lib/simple/httpd/service_integration.rb
|
143
145
|
- lib/simple/httpd/version.rb
|
144
146
|
- log/.gitkeep
|
145
147
|
- scripts/release
|
@@ -179,7 +181,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
179
181
|
- !ruby/object:Gem::Version
|
180
182
|
version: '0'
|
181
183
|
requirements: []
|
182
|
-
rubygems_version: 3.0.
|
184
|
+
rubygems_version: 3.0.6
|
183
185
|
signing_key:
|
184
186
|
specification_version: 4
|
185
187
|
summary: Super-simple HTTPD server
|
data/bin/rubocop
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
#
|
5
|
-
# This file was generated by Bundler.
|
6
|
-
#
|
7
|
-
# The application 'rubocop' is installed as part of a gem, and
|
8
|
-
# this file is here to facilitate running it.
|
9
|
-
#
|
10
|
-
|
11
|
-
require "pathname"
|
12
|
-
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
-
Pathname.new(__FILE__).realpath)
|
14
|
-
|
15
|
-
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
-
|
17
|
-
if File.file?(bundle_binstub)
|
18
|
-
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
-
load(bundle_binstub)
|
20
|
-
else
|
21
|
-
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
-
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
require "rubygems"
|
27
|
-
require "bundler/setup"
|
28
|
-
|
29
|
-
load Gem.bin_path("rubocop", "rubocop")
|