From bf1558eeb0985fa1b1cfe661426e0d16af8535f3 Mon Sep 17 00:00:00 2001
From: Robert <8934693+0x1eef@users.noreply.github.com>
Date: Sun, 25 Jun 2023 21:03:07 -0300
Subject: [PATCH] Add packages/ruby/server/ (#132)
### Summary
The `server` package implements a static file web server intended to be
used during development. The goal is to have it as close to a production
environment as possible. Not there yet. This is the first step.
---
Rakefile.rb | 5 +-
packages/ruby/server/.gitignore | 8 +++
packages/ruby/server/Gemfile | 4 ++
packages/ruby/server/LICENSE | 15 +++++
packages/ruby/server/README.md | 40 ++++++++++++
packages/ruby/server/lib/server.rb | 47 +++++++++++++
packages/ruby/server/lib/server/dir.rb | 34 ++++++++++
packages/ruby/server/lib/server/puma.rb | 8 +++
packages/ruby/server/server.rb.gemspec | 18 +++++
packages/ruby/server/test/fakeweb/index.html | 8 +++
packages/ruby/server/test/server_dir_test.rb | 23 +++++++
packages/ruby/server/test/setup.rb | 3 +
tasks.lib/pf.rb | 21 ------
tasks/config/build.rake | 69 --------------------
tasks/config/install.rake | 10 ---
15 files changed, 209 insertions(+), 104 deletions(-)
create mode 100644 packages/ruby/server/.gitignore
create mode 100644 packages/ruby/server/Gemfile
create mode 100644 packages/ruby/server/LICENSE
create mode 100644 packages/ruby/server/README.md
create mode 100644 packages/ruby/server/lib/server.rb
create mode 100644 packages/ruby/server/lib/server/dir.rb
create mode 100644 packages/ruby/server/lib/server/puma.rb
create mode 100644 packages/ruby/server/server.rb.gemspec
create mode 100644 packages/ruby/server/test/fakeweb/index.html
create mode 100644 packages/ruby/server/test/server_dir_test.rb
create mode 100644 packages/ruby/server/test/setup.rb
delete mode 100644 tasks.lib/pf.rb
delete mode 100644 tasks/config/build.rake
delete mode 100644 tasks/config/install.rake
diff --git a/Rakefile.rb b/Rakefile.rb
index 038924a..48ee08f 100644
--- a/Rakefile.rb
+++ b/Rakefile.rb
@@ -2,9 +2,6 @@
require "ryo"
require "listen"
-
-load "tasks/config/build.rake"
-load "tasks/config/install.rake"
load "tasks/deploy.rake"
namespace :nanoc do
@@ -58,5 +55,5 @@ namespace :lint do
end
end
end
-task lint: ["linter:ruby", "linter:typescript"]
+task lint: ["lint:rubocop", "lint:eslint"]
task default: "deploy:local"
diff --git a/packages/ruby/server/.gitignore b/packages/ruby/server/.gitignore
new file mode 100644
index 0000000..8926010
--- /dev/null
+++ b/packages/ruby/server/.gitignore
@@ -0,0 +1,8 @@
+build/
+tmp/
+node_modules/
+.bundle/
+*.log
+*.sh
+*.core
+Gemfile.lock
diff --git a/packages/ruby/server/Gemfile b/packages/ruby/server/Gemfile
new file mode 100644
index 0000000..bb94df8
--- /dev/null
+++ b/packages/ruby/server/Gemfile
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+source "https://rubygems.org"
+gemspec
diff --git a/packages/ruby/server/LICENSE b/packages/ruby/server/LICENSE
new file mode 100644
index 0000000..548a1d0
--- /dev/null
+++ b/packages/ruby/server/LICENSE
@@ -0,0 +1,15 @@
+Copyright (C) 2023 by 0x1eef <0x1eef@protonmail.com>
+
+Permission to use, copy, modify, and/or distribute this
+software for any purpose with or without fee is hereby
+granted.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS
+ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+OF THIS SOFTWARE.
diff --git a/packages/ruby/server/README.md b/packages/ruby/server/README.md
new file mode 100644
index 0000000..1312301
--- /dev/null
+++ b/packages/ruby/server/README.md
@@ -0,0 +1,40 @@
+## About
+
+server.rb implements a static file web server
+by using the fast performing Ruby web server
+[Puma](https://github.com/puma/puma)
+and a small
+[Rack](https://github.com/rack/rack)
+application.
+
+## Examples
+
+### Server.for_dir
+
+The `Server.for_dir` method returns a Server instance
+that serves the contents of a directory. `Server#start` spawns
+a new thread to listen for requests, and afterwards returns
+the thread. `Thread#join` can block execution at that point,
+or execution can continue as normal by not calling `Thread#join`:
+
+```ruby
+require "server"
+
+##
+# Create a Server instance for the contents of a directory
+server = Server.for_dir("./build/website/")
+
+##
+# Start listening for connections
+thr = server.start
+
+##
+# Prevent the main thread from exiting
+thr.join
+```
+
+## License
+
+[BSD Zero Clause](https://choosealicense.com/licenses/0bsd/).
+
+See [LICENSE](./LICENSE).
diff --git a/packages/ruby/server/lib/server.rb b/packages/ruby/server/lib/server.rb
new file mode 100644
index 0000000..543d35d
--- /dev/null
+++ b/packages/ruby/server/lib/server.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+class Server
+ require "rack"
+ require_relative "server/puma"
+ require_relative "server/dir"
+
+ def self.app(path)
+ Rack::Builder.app do
+ run Server::Dir.new(path)
+ end
+ end
+
+ def self.for_dir(path, options = {})
+ new(app(path), options)
+ end
+
+ def initialize(app, options = {})
+ @app = app
+ @options = default_options.merge!(options)
+ @events = Puma::Events.new
+ @server = Puma::Server.new(@app, @events, @options)
+ end
+
+ def start
+ @server.binder.parse(@options[:binds])
+ @server.run
+ end
+
+ def stop
+ @server.stop
+ end
+
+ private
+
+ def default_options
+ {
+ tcp_host: "127.0.0.1",
+ tcp_port: 7777,
+ binds: ["tcp://127.0.0.1:7777"],
+ supported_http_methods: %w[GET HEAD],
+ min_threads: 1,
+ max_threads: 5,
+ workers: 1
+ }
+ end
+end
diff --git a/packages/ruby/server/lib/server/dir.rb b/packages/ruby/server/lib/server/dir.rb
new file mode 100644
index 0000000..8397a3d
--- /dev/null
+++ b/packages/ruby/server/lib/server/dir.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+class Server::Dir
+ def initialize(root)
+ @root = File.realpath(root)
+ end
+
+ def call(env)
+ req = Rack::Request.new(env)
+ headers = Rack::Headers.new(env)
+ h, body = read(local_path(req.path))
+ [200, headers.merge!(h), body]
+ rescue Errno::ENOENT
+ not_found
+ end
+
+ private
+
+ attr_reader :root
+
+ def read(path)
+ body = File.binread(path)
+ [{"Content-Length" => body.bytesize}, body.each_line]
+ end
+
+ def local_path(req_path)
+ lpath = File.join root, File.expand_path(req_path)
+ File.directory?(lpath) ? File.join(lpath, "index.html") : lpath
+ end
+
+ def not_found
+ [404, {"Content-Type" => "text/plain"}, ["The requested URL was not found"]]
+ end
+end
diff --git a/packages/ruby/server/lib/server/puma.rb b/packages/ruby/server/lib/server/puma.rb
new file mode 100644
index 0000000..f84b988
--- /dev/null
+++ b/packages/ruby/server/lib/server/puma.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+require "puma"
+require "puma/events"
+
+class Puma::Server
+ public :binder
+end
diff --git a/packages/ruby/server/server.rb.gemspec b/packages/ruby/server/server.rb.gemspec
new file mode 100644
index 0000000..6f3d259
--- /dev/null
+++ b/packages/ruby/server/server.rb.gemspec
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+Gem::Specification.new do |gem|
+ gem.name = "server.rb"
+ gem.authors = ["0x1eef"]
+ gem.email = ["0x1eef@protonmail.com"]
+ gem.homepage = "https://github.com/0x1eef/server.rb#readme"
+ gem.version = "0.1.0"
+ gem.licenses = ["0BSD"]
+ gem.files = Dir["lib/*", "lib/**/*.rb"]
+ gem.require_paths = ["lib"]
+ gem.summary = "A static file web server"
+ gem.description = gem.summary
+ gem.add_runtime_dependency "puma", "~> 6.3"
+ gem.add_runtime_dependency "rack", "~> 3.0"
+ gem.add_development_dependency "standard", "~> 1.24"
+ gem.add_development_dependency "rack-test", "~> 2.1"
+ gem.add_development_dependency "test-unit", "~> 3.5"
+end
diff --git a/packages/ruby/server/test/fakeweb/index.html b/packages/ruby/server/test/fakeweb/index.html
new file mode 100644
index 0000000..95d1b90
--- /dev/null
+++ b/packages/ruby/server/test/fakeweb/index.html
@@ -0,0 +1,8 @@
+
+