luna 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2e347831d31d4b1db1333c5bfb7ff3399fa58a95c5f776dcd68b2e8e8bd65987
4
+ data.tar.gz: 345be967db13b5f0368e2e576be3603fb6817c0dd6fbc9dce34ead65148ead17
5
+ SHA512:
6
+ metadata.gz: c159538036c29eb07e69bedcc79fa882e6dd3117acf79db1a1971034586230a63335f0856ad6b1cdc082675d938c5c631f3c1770cbfae5b3fa87c1ef93df7471
7
+ data.tar.gz: 0444b5af72dbb9cf8845e3aecbcdc5d1eba3b97af656b23ca828362155cd8b683a300ffd10619cbda79c109b10988346770b4d3a8a393da814ea4cb078831952
checksums.yaml.gz.sig ADDED
Binary file
data/agents.md ADDED
@@ -0,0 +1,205 @@
1
+ # Agent
2
+
3
+ ## Context
4
+
5
+ This section provides links to documentation from installed packages. It is automatically generated and may be updated by running `bake agent:context:install`.
6
+
7
+ **Important:** Before performing any code, documentation, or analysis tasks, always read and apply the full content of any relevant documentation referenced in the following sections. These context files contain authoritative standards and best practices for documentation, code style, and project-specific workflows. **Do not proceed with any actions until you have read and incorporated the guidance from relevant context files.**
8
+
9
+ **Setup Instructions:** If the referenced files are not present or if dependencies have been updated, run `bake agent:context:install` to install the latest context files.
10
+
11
+ ### agent-context
12
+
13
+ Install and manage context files from Ruby gems.
14
+
15
+ #### [Getting Started](.context/agent-context/getting-started.md)
16
+
17
+ This guide explains how to use `agent-context`, a tool for discovering and installing contextual information from Ruby gems to help AI agents.
18
+
19
+ ### async
20
+
21
+ A concurrency framework for Ruby.
22
+
23
+ #### [Getting Started](.context/async/getting-started.md)
24
+
25
+ This guide shows how to add async to your project and run code asynchronously.
26
+
27
+ #### [Scheduler](.context/async/scheduler.md)
28
+
29
+ This guide gives an overview of how the scheduler is implemented.
30
+
31
+ #### [Tasks](.context/async/tasks.md)
32
+
33
+ This guide explains how asynchronous tasks work and how to use them.
34
+
35
+ #### [Best Practices](.context/async/best-practices.md)
36
+
37
+ This guide gives an overview of best practices for using Async.
38
+
39
+ #### [Debugging](.context/async/debugging.md)
40
+
41
+ This guide explains how to debug issues with programs that use Async.
42
+
43
+ #### [Thread safety](.context/async/thread-safety.md)
44
+
45
+ This guide explains thread safety in Ruby, focusing on fibers and threads, common pitfalls, and best practices to avoid problems like data corruption, race conditions, and deadlocks.
46
+
47
+ ### async-http
48
+
49
+ A HTTP client and server library.
50
+
51
+ #### [Getting Started](.context/async-http/getting-started.md)
52
+
53
+ This guide explains how to get started with `Async::HTTP`.
54
+
55
+ #### [Testing](.context/async-http/testing.md)
56
+
57
+ This guide explains how to use `Async::HTTP` clients and servers in your tests.
58
+
59
+ ### bake
60
+
61
+ A replacement for rake with a simpler syntax.
62
+
63
+ #### [Getting Started](.context/bake/getting-started.md)
64
+
65
+ This guide gives a general overview of `bake` and how to use it.
66
+
67
+ #### [Command Line Interface](.context/bake/command-line-interface.md)
68
+
69
+ The `bake` command is broken up into two main functions: `list` and `call`.
70
+
71
+ #### [Project Integration](.context/bake/project-integration.md)
72
+
73
+ This guide explains how to add `bake` to a Ruby project.
74
+
75
+ #### [Gem Integration](.context/bake/gem-integration.md)
76
+
77
+ This guide explains how to add `bake` to a Ruby gem and export standardised tasks for use by other gems and projects.
78
+
79
+ #### [Input and Output](.context/bake/input-and-output.md)
80
+
81
+ `bake` has built in tasks for reading input and writing output in different formats. While this can be useful for general processing, there are some limitations, notably that rich object representations like `json` and `yaml` often don't support stream processing.
82
+
83
+ ### covered
84
+
85
+ A modern approach to code coverage.
86
+
87
+ #### [Getting Started](.context/covered/getting-started.md)
88
+
89
+ This guide explains how to get started with `covered` and integrate it with your test suite.
90
+
91
+ #### [Configuration](.context/covered/configuration.md)
92
+
93
+ This guide will help you to configure covered for your project's specific requirements.
94
+
95
+ ### io-event
96
+
97
+ An event loop.
98
+
99
+ #### [Getting Started](.context/io-event/getting-started.md)
100
+
101
+ This guide explains how to use `io-event` for non-blocking IO.
102
+
103
+ ### metrics
104
+
105
+ Application metrics and instrumentation.
106
+
107
+ #### [Getting Started](.context/metrics/getting-started.md)
108
+
109
+ This guide explains how to use `metrics` for capturing run-time metrics.
110
+
111
+ #### [Capture](.context/metrics/capture.md)
112
+
113
+ This guide explains how to use `metrics` for exporting metric definitions from your application.
114
+
115
+ #### [Testing](.context/metrics/testing.md)
116
+
117
+ This guide explains how to write assertions in your test suite to validate `metrics` are being emitted correctly.
118
+
119
+ ### protocol-http
120
+
121
+ Provides abstractions to handle HTTP protocols.
122
+
123
+ #### [Getting Started](.context/protocol-http/getting-started.md)
124
+
125
+ This guide explains how to use `protocol-http` for building abstract HTTP interfaces.
126
+
127
+ #### [Message Body](.context/protocol-http/message-body.md)
128
+
129
+ This guide explains how to work with HTTP request and response message bodies using `Protocol::HTTP::Body` classes.
130
+
131
+ #### [Headers](.context/protocol-http/headers.md)
132
+
133
+ This guide explains how to work with HTTP headers using `protocol-http`.
134
+
135
+ #### [Middleware](.context/protocol-http/middleware.md)
136
+
137
+ This guide explains how to build and use HTTP middleware with `Protocol::HTTP::Middleware`.
138
+
139
+ #### [Streaming](.context/protocol-http/streaming.md)
140
+
141
+ This guide gives an overview of how to implement streaming requests and responses.
142
+
143
+ #### [Design Overview](.context/protocol-http/design-overview.md)
144
+
145
+ This guide explains the high level design of `protocol-http` in the context of wider design patterns that can be used to implement HTTP clients and servers.
146
+
147
+ ### protocol-http1
148
+
149
+ A low level implementation of the HTTP/1 protocol.
150
+
151
+ #### [Getting Started](.context/protocol-http1/getting-started.md)
152
+
153
+ This guide explains how to get started with `protocol-http1`, a low-level implementation of the HTTP/1 protocol for building HTTP clients and servers.
154
+
155
+ ### protocol-http2
156
+
157
+ A low level implementation of the HTTP/2 protocol.
158
+
159
+ #### [Getting Started](.context/protocol-http2/getting-started.md)
160
+
161
+ This guide explains how to use the `protocol-http2` gem to implement a basic HTTP/2 client.
162
+
163
+ ### samovar
164
+
165
+ Samovar is a flexible option parser excellent support for sub-commands and help documentation.
166
+
167
+ #### [Getting Started](.context/samovar/getting-started.md)
168
+
169
+ This guide explains how to use `samovar` to build command-line tools and applications.
170
+
171
+ ### sus
172
+
173
+ A fast and scalable test runner.
174
+
175
+ #### [Getting Started](.context/sus/getting-started.md)
176
+
177
+ This guide explains how to use the `sus` gem to write tests for your Ruby projects.
178
+
179
+ ### sus-fixtures-async-http
180
+
181
+ Test fixtures for running in Async::HTTP.
182
+
183
+ #### [Getting Started](.context/sus-fixtures-async-http/getting-started.md)
184
+
185
+ This guide explains how to use the `sus-fixtures-async-http` gem to test HTTP clients and servers.
186
+
187
+ ### traces
188
+
189
+ Application instrumentation and tracing.
190
+
191
+ #### [Getting Started](.context/traces/getting-started.md)
192
+
193
+ This guide explains how to use `traces` for tracing code execution.
194
+
195
+ #### [Context Propagation](.context/traces/context-propagation.md)
196
+
197
+ This guide explains how to propagate trace context between different execution contexts within your application using `Traces.current_context` and `Traces.with_context`.
198
+
199
+ #### [Testing](.context/traces/testing.md)
200
+
201
+ This guide explains how to test traces in your code.
202
+
203
+ #### [Capture](.context/traces/capture.md)
204
+
205
+ This guide explains how to use `traces` for exporting traces from your application. This can be used to document all possible traces.
data/bin/luna ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Released under the MIT License.
5
+ # Copyright, 2025.
6
+
7
+ require_relative "../lib/luna"
8
+ require_relative "../lib/luna/command"
9
+
10
+ exit Luna::Command.call(*ARGV)
@@ -0,0 +1,7 @@
1
+ # Luna Example
2
+
3
+ This markdown file should be rendered to HTML when `--markdown` is enabled.
4
+
5
+ - Item 1
6
+ - Item 2
7
+ - Item 3
@@ -0,0 +1,6 @@
1
+ <!doctype html>
2
+ <meta charset="utf-8">
3
+ <title>Luna Example</title>
4
+ <h1>Hello from Luna</h1>
5
+ <p>This is a static HTML file.</p>
6
+ <p>Try <a href="README.md">README.md</a> for Markdown rendering.</p>
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2025, by Samuel Williams.
5
+
6
+ require "samovar"
7
+ require "async"
8
+ require "async/http/endpoint"
9
+
10
+ require_relative "../server"
11
+
12
+ module Luna
13
+ module Command
14
+ class Serve < Samovar::Command
15
+ self.description = "Serve static files from a directory."
16
+
17
+ options do
18
+ option "-b/--bind <address>", "Bind address (e.g. http://localhost:3000)", default: "http://localhost:3000"
19
+ option "-p/--port <number>", "Override port", type: Integer
20
+ option "-h/--hostname <hostname>", "Override hostname"
21
+ option "-t/--timeout <seconds>", "I/O timeout", type: Float, default: nil
22
+
23
+ option "-r/--root <path>", "Root directory", default: nil
24
+ option "-i/--index <filename>", "Directory index", default: "index.html"
25
+ option "--[no]-directory-listing", "Enable directory listing", default: true
26
+ option "--[no]-markdown", "Enable Markdown rendering", default: true
27
+ option "-v/--[no]-verbose", "Verbose logging", default: false
28
+ end
29
+
30
+ # Optional positional argument for root directory
31
+ one :path, "Root directory to serve", default: nil
32
+
33
+ def root_directory
34
+ # Use positional argument if provided, otherwise --root option, otherwise current directory
35
+ if @path
36
+ @path
37
+ elsif @options[:root]
38
+ @options[:root]
39
+ else
40
+ Dir.pwd
41
+ end
42
+ end
43
+
44
+ def endpoint_options
45
+ @options.slice(:hostname, :port, :timeout)
46
+ end
47
+
48
+ def endpoint
49
+ Async::HTTP::Endpoint.parse(@options[:bind], **endpoint_options)
50
+ end
51
+
52
+ def middleware
53
+ Luna::Server.middleware(
54
+ root: File.expand_path(root_directory),
55
+ verbose: @options[:verbose],
56
+ markdown: @options[:markdown],
57
+ index: @options[:index],
58
+ directory_listing: @options[:directory_listing]
59
+ )
60
+ end
61
+
62
+ def call
63
+ Console.logger.info(self) do |buffer|
64
+ buffer.puts "Luna v#{Luna::VERSION} serving..."
65
+ buffer.puts "- Root: #{File.expand_path(root_directory)}"
66
+ buffer.puts "- Bind: #{endpoint}"
67
+ buffer.puts "- Markdown: #{@options[:markdown]} | Index: #{@options[:index]} | Directory Listing: #{@options[:directory_listing]}"
68
+ end
69
+
70
+ Async do |task|
71
+ server = Luna::Server.new(middleware, endpoint, protocol: endpoint.protocol)
72
+
73
+ server.run
74
+
75
+ task.children.each(&:wait)
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2025, by Samuel Williams.
5
+
6
+ require_relative "command/serve"
7
+
8
+ module Luna
9
+ module Command
10
+ def self.call(*arguments)
11
+ Serve.call(*arguments)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2025, by Samuel Williams.
5
+
6
+ require "protocol/http/middleware"
7
+
8
+ begin
9
+ require "markly"
10
+ rescue LoadError
11
+ # The gemspec declares dependency, but handle gracefully if not present at runtime.
12
+ end
13
+
14
+ module Luna
15
+ module Middleware
16
+ # Render Markdown files to HTML on-the-fly.
17
+ # Intercepts requests that target .md/.markdown files.
18
+ class Markdown < Protocol::HTTP::Middleware
19
+ def initialize(app, root: Dir.pwd, index_candidates: ["index.md", "README.md"])
20
+ super(app)
21
+ @root = File.expand_path(root)
22
+ @index_candidates = index_candidates
23
+ end
24
+
25
+ attr :root
26
+ attr :index_candidates
27
+
28
+ def safe_join(path)
29
+ full = File.expand_path(File.join(@root, path))
30
+ return nil unless full.start_with?(@root)
31
+ full
32
+ end
33
+
34
+ def call(request)
35
+ return super unless request.method == "GET" || request.method == "HEAD"
36
+ path = request.path.split("?", 2).first
37
+
38
+ fs_path = safe_join(path)
39
+ return super unless fs_path
40
+
41
+ if File.directory?(fs_path)
42
+ candidate = @index_candidates.map {|n| File.join(fs_path, n)}.find {|p| File.file?(p)}
43
+ return render_file(candidate, head: request.method == "HEAD") if candidate
44
+ elsif File.file?(fs_path) && markdown_file?(fs_path)
45
+ return render_file(fs_path, head: request.method == "HEAD")
46
+ end
47
+
48
+ super
49
+ end
50
+
51
+ private
52
+
53
+ def markdown_file?(path)
54
+ [".md", ".markdown"].include?(File.extname(path).downcase)
55
+ end
56
+
57
+ def render_html(markdown)
58
+ if defined?(::Markly)
59
+ ::Markly.render_html(markdown)
60
+ else
61
+ # Fallback minimal rendering if markly isn't available:
62
+ escape = ->(s){s.to_s.gsub("&","&amp;").gsub("<","&lt;").gsub(">","&gt;")}
63
+ "<pre>" + escape.call(markdown) + "</pre>"
64
+ end
65
+ end
66
+
67
+ def render_file(path, head: false)
68
+ content = File.read(path)
69
+ html = render_html(content)
70
+ headers = [
71
+ ["content-type", "text/html; charset=utf-8"]
72
+ ]
73
+ Protocol::HTTP::Response[200, headers, head ? nil : [html]]
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2025, by Samuel Williams.
5
+
6
+ require "protocol/http/middleware"
7
+ require "protocol/http/body/file"
8
+ require "time"
9
+
10
+ module Luna
11
+ module Middleware
12
+ # Serve static files from a root directory, with optional index and directory listing.
13
+ class Static < Protocol::HTTP::Middleware
14
+ MIME_TYPES = {
15
+ ".html" => "text/html; charset=utf-8",
16
+ ".css" => "text/css",
17
+ ".js" => "application/javascript",
18
+ ".json" => "application/json",
19
+ ".xml" => "application/xml",
20
+ ".txt" => "text/plain; charset=utf-8",
21
+ ".md" => "text/markdown; charset=utf-8",
22
+ ".png" => "image/png",
23
+ ".jpg" => "image/jpeg",
24
+ ".jpeg" => "image/jpeg",
25
+ ".gif" => "image/gif",
26
+ ".svg" => "image/svg+xml",
27
+ ".ico" => "image/x-icon",
28
+ ".pdf" => "application/pdf",
29
+ ".zip" => "application/zip",
30
+ }.freeze
31
+
32
+ def initialize(app, root: Dir.pwd, index: "index.html", directory_listing: true)
33
+ super(app)
34
+ @root = File.expand_path(root)
35
+ @index = index
36
+ @directory_listing = directory_listing
37
+ end
38
+
39
+ attr :root
40
+ attr :index
41
+ attr :directory_listing
42
+
43
+ def mime_type_for_extension(ext)
44
+ MIME_TYPES[ext.downcase] || "application/octet-stream"
45
+ end
46
+
47
+ def safe_join(path)
48
+ full = File.expand_path(File.join(@root, path))
49
+ return nil unless full.start_with?(@root)
50
+ full
51
+ end
52
+
53
+ def call(request)
54
+ return super unless request.method == "GET" || request.method == "HEAD"
55
+ # Remove query and decode percent-encoding carefully:
56
+ request_path = request.path.split("?", 2).first
57
+
58
+ file_path = safe_join(request_path)
59
+ return super unless file_path
60
+
61
+ if File.exist?(file_path)
62
+ if File.directory?(file_path)
63
+ index_path = File.join(file_path, @index)
64
+ if File.file?(index_path)
65
+ return serve_file(index_path, head: request.method == "HEAD")
66
+ elsif @directory_listing
67
+ return serve_directory_listing(file_path, request_path)
68
+ else
69
+ return super
70
+ end
71
+ elsif File.file?(file_path)
72
+ return serve_file(file_path, head: request.method == "HEAD")
73
+ end
74
+ end
75
+
76
+ super
77
+ end
78
+
79
+ private
80
+
81
+ def serve_file(path, head: false)
82
+ stat = File.stat(path)
83
+ headers = [
84
+ ["content-type", mime_type_for_extension(File.extname(path))],
85
+ ["last-modified", stat.mtime.httpdate],
86
+ ]
87
+
88
+ body = head ? nil : Protocol::HTTP::Body::File.open(path)
89
+
90
+ Protocol::HTTP::Response[200, headers, body]
91
+ end
92
+
93
+ def serve_directory_listing(dir_path, request_path)
94
+ entries = Dir.entries(dir_path).sort - ["."]
95
+ entries.reject! {|e| e == ".."} if request_path == "/"
96
+ links = entries.map do |e|
97
+ name = e
98
+ target = File.join(request_path, e)
99
+ target += "/" if File.directory?(File.join(dir_path, e)) && !target.end_with?("/")
100
+ "<li><a href=\"#{escape_html(target)}\">#{escape_html(name)}</a></li>"
101
+ end.join("\n")
102
+ html = <<~HTML
103
+ <!doctype html>
104
+ <meta charset="utf-8">
105
+ <title>Index of #{escape_html(request_path)}</title>
106
+ <h1>Index of #{escape_html(request_path)}</h1>
107
+ <ul>#{links}</ul>
108
+ HTML
109
+
110
+ headers = [
111
+ ["content-type", "text/html; charset=utf-8"]
112
+ ]
113
+
114
+ Protocol::HTTP::Response[200, headers, [html]]
115
+ end
116
+
117
+ def escape_html(s)
118
+ s.to_s.gsub("&", "&amp;").gsub("<", "&lt;").gsub(">", "&gt;")
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2025, by Samuel Williams.
5
+
6
+ require "console"
7
+ require "async/http/statistics"
8
+
9
+ module Luna
10
+ module Middleware
11
+ # Simple HTTP logging middleware adapted from Falcon.
12
+ class Verbose < Protocol::HTTP::Middleware
13
+ def initialize(app, logger = Console)
14
+ super(app)
15
+ @logger = logger
16
+ end
17
+
18
+ def annotate(request)
19
+ task = Async::Task.current
20
+ address = request.remote_address
21
+ @logger.info(request, "-> #{request.method} #{request.path}", headers: request.headers.to_h, address: address.inspect)
22
+ task.annotate("#{request.method} #{request.path} from #{address.inspect}")
23
+ end
24
+
25
+ def call(request)
26
+ annotate(request)
27
+ statistics = Async::HTTP::Statistics.start
28
+ response = super
29
+ statistics.wrap(response) do |body, error|
30
+ @logger.info(request, "<- #{request.method} #{request.path}", headers: response.headers.to_h, status: response.status, body: body.inspect, error: error)
31
+ end
32
+ response
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2025, by Samuel Williams.
5
+
6
+ require "async/http/server"
7
+ require "protocol/http/middleware/builder"
8
+ require "protocol/http/content_encoding"
9
+
10
+ require_relative "middleware/verbose"
11
+ require_relative "middleware/static"
12
+ require_relative "middleware/markdown"
13
+
14
+ module Luna
15
+ # HTTP server hosting a middleware stack for static content.
16
+ class Server < Async::HTTP::Server
17
+ # Build a middleware for serving static files with optional markdown and logging.
18
+ # @param root [String] Root directory for content.
19
+ # @param verbose [Boolean] Enable verbose logging.
20
+ # @param markdown [Boolean] Enable markdown rendering.
21
+ # @param index [String] Index file for directories.
22
+ # @param directory_listing [Boolean] Enable basic directory listing when no index exists.
23
+ def self.middleware(root: Dir.pwd, verbose: false, markdown: true, index: "index.html", directory_listing: true)
24
+ ::Protocol::HTTP::Middleware.build do
25
+ use Luna::Middleware::Verbose if verbose
26
+ use ::Protocol::HTTP::ContentEncoding
27
+ use Luna::Middleware::Markdown, root: root if markdown
28
+ run Luna::Middleware::Static.new(
29
+ ->(request){Protocol::HTTP::Response[404, {"content-type"=>"text/plain"}, ["Not Found"]]},
30
+ root: root,
31
+ index: index,
32
+ directory_listing: directory_listing
33
+ )
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2025, by Samuel Williams.
5
+
6
+ module Luna
7
+ VERSION = "0.0.1"
8
+ end
data/lib/luna.rb ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2025, by Samuel Williams.
5
+
6
+ require_relative "luna/version"
7
+
8
+ module Luna
9
+ end
data/license.md ADDED
@@ -0,0 +1,21 @@
1
+ # MIT License
2
+
3
+ Copyright, 2025, by Samuel Williams.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/readme.md ADDED
@@ -0,0 +1,52 @@
1
+ # Luna
2
+
3
+ A tiny, asynchronous static file server with optional Markdown rendering, built on Protocol::HTTP and Async.
4
+
5
+ - Static files via `Luna::Middleware::Static`
6
+ - Optional Markdown-to-HTML via `Luna::Middleware::Markdown` (using `markly`)
7
+ - Simple verbose logging via `Luna::Middleware::Verbose`
8
+
9
+ [![Development Status](https://github.com/socketry/luna/workflows/Test/badge.svg)](https://github.com/socketry/luna/actions?workflow=Test)
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ gem "luna"
16
+
17
+ And then execute:
18
+
19
+ bundle install
20
+
21
+ Or install it yourself as:
22
+
23
+ gem install luna
24
+
25
+ ## Usage
26
+
27
+ Serve the current directory:
28
+
29
+ luna --bind http://localhost:3000
30
+
31
+ Options:
32
+
33
+ - `--root PATH` (default: current directory)
34
+ - `--index FILENAME` (default: `index.html`)
35
+ - `--[no]-directory-listing` (default: `true`)
36
+ - `--[no]-markdown` (default: `true`)
37
+ - `--[no]-verbose`
38
+ - `--bind URL` (default: `http://localhost:3000`)
39
+
40
+ ## Programmatic Usage
41
+
42
+ require "async"
43
+ require "async/http/endpoint"
44
+ require "luna/server"
45
+
46
+ endpoint = Async::HTTP::Endpoint.parse("http://localhost:3000")
47
+ app = Luna::Server.middleware(root: "/path/to/root", markdown: true)
48
+
49
+ Async do |task|
50
+ server = Luna::Server.new(app, endpoint, protocol: endpoint.protocol)
51
+ server.run
52
+ end
data.tar.gz.sig ADDED
Binary file
metadata ADDED
@@ -0,0 +1,186 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: luna
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Samuel Williams
8
+ bindir: bin
9
+ cert_chain:
10
+ - |
11
+ -----BEGIN CERTIFICATE-----
12
+ MIIE2DCCA0CgAwIBAgIBATANBgkqhkiG9w0BAQsFADBhMRgwFgYDVQQDDA9zYW11
13
+ ZWwud2lsbGlhbXMxHTAbBgoJkiaJk/IsZAEZFg1vcmlvbnRyYW5zZmVyMRIwEAYK
14
+ CZImiZPyLGQBGRYCY28xEjAQBgoJkiaJk/IsZAEZFgJuejAeFw0yMjA4MDYwNDUz
15
+ MjRaFw0zMjA4MDMwNDUzMjRaMGExGDAWBgNVBAMMD3NhbXVlbC53aWxsaWFtczEd
16
+ MBsGCgmSJomT8ixkARkWDW9yaW9udHJhbnNmZXIxEjAQBgoJkiaJk/IsZAEZFgJj
17
+ bzESMBAGCgmSJomT8ixkARkWAm56MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB
18
+ igKCAYEAomvSopQXQ24+9DBB6I6jxRI2auu3VVb4nOjmmHq7XWM4u3HL+pni63X2
19
+ 9qZdoq9xt7H+RPbwL28LDpDNflYQXoOhoVhQ37Pjn9YDjl8/4/9xa9+NUpl9XDIW
20
+ sGkaOY0eqsQm1pEWkHJr3zn/fxoKPZPfaJOglovdxf7dgsHz67Xgd/ka+Wo1YqoE
21
+ e5AUKRwUuvaUaumAKgPH+4E4oiLXI4T1Ff5Q7xxv6yXvHuYtlMHhYfgNn8iiW8WN
22
+ XibYXPNP7NtieSQqwR/xM6IRSoyXKuS+ZNGDPUUGk8RoiV/xvVN4LrVm9upSc0ss
23
+ RZ6qwOQmXCo/lLcDUxJAgG95cPw//sI00tZan75VgsGzSWAOdjQpFM0l4dxvKwHn
24
+ tUeT3ZsAgt0JnGqNm2Bkz81kG4A2hSyFZTFA8vZGhp+hz+8Q573tAR89y9YJBdYM
25
+ zp0FM4zwMNEUwgfRzv1tEVVUEXmoFCyhzonUUw4nE4CFu/sE3ffhjKcXcY//qiSW
26
+ xm4erY3XAgMBAAGjgZowgZcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O
27
+ BBYEFO9t7XWuFf2SKLmuijgqR4sGDlRsMC4GA1UdEQQnMCWBI3NhbXVlbC53aWxs
28
+ aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MC4GA1UdEgQnMCWBI3NhbXVlbC53aWxs
29
+ aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MA0GCSqGSIb3DQEBCwUAA4IBgQB5sxkE
30
+ cBsSYwK6fYpM+hA5B5yZY2+L0Z+27jF1pWGgbhPH8/FjjBLVn+VFok3CDpRqwXCl
31
+ xCO40JEkKdznNy2avOMra6PFiQyOE74kCtv7P+Fdc+FhgqI5lMon6tt9rNeXmnW/
32
+ c1NaMRdxy999hmRGzUSFjozcCwxpy/LwabxtdXwXgSay4mQ32EDjqR1TixS1+smp
33
+ 8C/NCWgpIfzpHGJsjvmH2wAfKtTTqB9CVKLCWEnCHyCaRVuKkrKjqhYCdmMBqCws
34
+ JkxfQWC+jBVeG9ZtPhQgZpfhvh+6hMhraUYRQ6XGyvBqEUe+yo6DKIT3MtGE2+CP
35
+ eX9i9ZWBydWb8/rvmwmX2kkcBbX0hZS1rcR593hGc61JR6lvkGYQ2MYskBveyaxt
36
+ Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
37
+ voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
38
+ -----END CERTIFICATE-----
39
+ date: 1980-01-02 00:00:00.000000000 Z
40
+ dependencies:
41
+ - !ruby/object:Gem::Dependency
42
+ name: async
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: async-http
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.75'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.75'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: console
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '1.30'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '1.30'
97
+ - !ruby/object:Gem::Dependency
98
+ name: markly
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0.6'
104
+ - - "<"
105
+ - !ruby/object:Gem::Version
106
+ version: '2.0'
107
+ type: :runtime
108
+ prerelease: false
109
+ version_requirements: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0.6'
114
+ - - "<"
115
+ - !ruby/object:Gem::Version
116
+ version: '2.0'
117
+ - !ruby/object:Gem::Dependency
118
+ name: protocol-http
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '0.31'
124
+ type: :runtime
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '0.31'
131
+ - !ruby/object:Gem::Dependency
132
+ name: samovar
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '2.3'
138
+ type: :runtime
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: '2.3'
145
+ executables:
146
+ - luna
147
+ extensions: []
148
+ extra_rdoc_files: []
149
+ files:
150
+ - agents.md
151
+ - bin/luna
152
+ - examples/static/README.md
153
+ - examples/static/index.html
154
+ - lib/luna.rb
155
+ - lib/luna/command.rb
156
+ - lib/luna/command/serve.rb
157
+ - lib/luna/middleware/markdown.rb
158
+ - lib/luna/middleware/static.rb
159
+ - lib/luna/middleware/verbose.rb
160
+ - lib/luna/server.rb
161
+ - lib/luna/version.rb
162
+ - license.md
163
+ - readme.md
164
+ homepage: https://github.com/socketry/luna
165
+ licenses:
166
+ - MIT
167
+ metadata:
168
+ source_code_uri: https://github.com/socketry/luna.git
169
+ rdoc_options: []
170
+ require_paths:
171
+ - lib
172
+ required_ruby_version: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - ">="
175
+ - !ruby/object:Gem::Version
176
+ version: '3.2'
177
+ required_rubygems_version: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - ">="
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ requirements: []
183
+ rubygems_version: 4.0.10
184
+ specification_version: 4
185
+ summary: A tiny, asynchronous static file server with optional Markdown rendering.
186
+ test_files: []
metadata.gz.sig ADDED
Binary file