gemstack 0.1.0
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 +7 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +29 -0
- data/lib/gemstack/application.rb +161 -0
- data/lib/gemstack/interlock.rb +46 -0
- data/lib/gemstack/reloader.rb +42 -0
- data/lib/gemstack/testing.rb +60 -0
- data/lib/gemstack.rb +64 -0
- metadata +178 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 39317af6119a78833f6f2223b54d72f629cf72ed2d1a1d87d6c1dd5b09b1d755
|
|
4
|
+
data.tar.gz: 77c7d2c8616cad5484be73a0b2b5e22638db88b6be21b3f4c1e33b6311f11d1e
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 2446525d9b09584be6a6d6bbf21a858b97a48ef177d7c8011c79c968e057bae21de4d181a965077c64a2bbcd3cd802c998bb4af4777e910b9226b19f1dcb491e
|
|
7
|
+
data.tar.gz: aa46e6be80e88973f82760d83f299e92346ce78ea176b6ea7ec65146257f59cb95dc722b92d96301fe9d6b16134453b858f64c254bde438429a6fb68da232c2e
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shoaib Malik
|
|
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,29 @@
|
|
|
1
|
+
# gemstack
|
|
2
|
+
|
|
3
|
+
GemStack: a fast, modular Ruby API framework for Next.js applications.
|
|
4
|
+
|
|
5
|
+
Part of [GemStack](https://github.com/gemstack-rb/gemstack), a modular Ruby API framework for Next.js
|
|
6
|
+
applications. All GemStack gems are developed together in that repository and released with the same
|
|
7
|
+
version.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
gem install gemstack
|
|
13
|
+
gemstack new myapp
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
This gem is the framework: it depends on every module an app needs (core, cache, schema, http, contract, dev, cli). Optional modules are added per app.
|
|
17
|
+
|
|
18
|
+
## Documentation
|
|
19
|
+
|
|
20
|
+
- [Guide](https://github.com/gemstack-rb/gemstack/blob/main/docs/getting-started.md)
|
|
21
|
+
- [All guides](https://github.com/gemstack-rb/gemstack/tree/main/docs) ·
|
|
22
|
+
[Architecture](https://github.com/gemstack-rb/gemstack/blob/main/ARCHITECTURE.md)
|
|
23
|
+
|
|
24
|
+
Source, issues and pull requests: [gemstack-rb/gemstack](https://github.com/gemstack-rb/gemstack)
|
|
25
|
+
(this gem lives in `gems/gemstack`).
|
|
26
|
+
|
|
27
|
+
## License
|
|
28
|
+
|
|
29
|
+
MIT — see [LICENSE.txt](LICENSE.txt).
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "zeitwerk"
|
|
4
|
+
|
|
5
|
+
module GemStack
|
|
6
|
+
# A GemStack application: configuration + autoloaded code + routes, served
|
|
7
|
+
# as a Rack app. There is one per process, available as GemStack.application.
|
|
8
|
+
#
|
|
9
|
+
# Boot sequence (idempotent):
|
|
10
|
+
# 1. load .env files (development/test) config.env_files
|
|
11
|
+
# 2. load config/environments/<env>.rb if present
|
|
12
|
+
# 3. run plugin hooks GemStack::Plugins
|
|
13
|
+
# 4. set up Zeitwerk for every directory in app/ (+ config.autoload_paths)
|
|
14
|
+
# 5. load config/routes.rb
|
|
15
|
+
# 6. compile the middleware stack config.http.middleware
|
|
16
|
+
# 7. eager load in production config.eager_load
|
|
17
|
+
class Application
|
|
18
|
+
attr_reader :config, :loader, :router
|
|
19
|
+
|
|
20
|
+
def initialize(config: GemStack.config)
|
|
21
|
+
@config = config
|
|
22
|
+
@booted = false
|
|
23
|
+
@boot_lock = Mutex.new
|
|
24
|
+
@shutdown_hooks = []
|
|
25
|
+
@reload_hooks = []
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def root = Pathname.new(config.root)
|
|
29
|
+
|
|
30
|
+
def boot!
|
|
31
|
+
@boot_lock.synchronize do
|
|
32
|
+
return self if @booted
|
|
33
|
+
|
|
34
|
+
load_env_files
|
|
35
|
+
load_environment_config
|
|
36
|
+
enable_jit
|
|
37
|
+
Plugins.run(self)
|
|
38
|
+
setup_loader
|
|
39
|
+
@router = build_router { load_routes }
|
|
40
|
+
install_reloader if config.reload_code
|
|
41
|
+
@http = HTTP::App.new(config: config.http, router: @router)
|
|
42
|
+
eager_load! if config.eager_load
|
|
43
|
+
@booted = true
|
|
44
|
+
end
|
|
45
|
+
self
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def booted? = @booted
|
|
49
|
+
|
|
50
|
+
def call(env)
|
|
51
|
+
boot! unless @booted
|
|
52
|
+
@http.call(env)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Replaces the route table. Called by config/routes.rb via GemStack.routes.
|
|
56
|
+
def draw_routes(&)
|
|
57
|
+
raise ArgumentError, "GemStack.routes needs a block" unless block_given?
|
|
58
|
+
|
|
59
|
+
@pending_routes << Proc.new(&) if @pending_routes # collected while (re)loading routes.rb
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def routes = @router&.routes || []
|
|
63
|
+
|
|
64
|
+
# Unloads and reloads app/ code and routes. Used by the development
|
|
65
|
+
# Reloader; safe to call from the console.
|
|
66
|
+
def reload!
|
|
67
|
+
unless config.reload_code
|
|
68
|
+
raise ConfigurationError, "code reloading is disabled; set config.reload_code = true (default in development)"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
@loader&.reload
|
|
72
|
+
@router = build_router { load_routes }
|
|
73
|
+
@http.router = @router
|
|
74
|
+
@reload_hooks.each(&:call)
|
|
75
|
+
GemStack.logger.debug("reloaded application code")
|
|
76
|
+
true
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def eager_load!
|
|
80
|
+
@loader&.eager_load
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Runs after code and routes reload (development), e.g. to re-read config/channels.rb.
|
|
84
|
+
def on_reload(&block)
|
|
85
|
+
@reload_hooks << block
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def on_shutdown(&block)
|
|
89
|
+
@shutdown_hooks << block
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def shutdown
|
|
93
|
+
@shutdown_hooks.reverse_each(&:call)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def autoload_dirs
|
|
97
|
+
app_dirs = Dir.glob(root.join("app/*").to_s)
|
|
98
|
+
extra = Array(config.autoload_paths).map { |path| File.expand_path(path, root) }
|
|
99
|
+
(app_dirs + extra).select { |path| File.directory?(path) }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
private
|
|
103
|
+
|
|
104
|
+
def load_env_files = GemStack.load_env_files!
|
|
105
|
+
|
|
106
|
+
# Enables config.jit unless a JIT is already running (e.g. `ruby --yjit`).
|
|
107
|
+
def enable_jit
|
|
108
|
+
jit = config.jit
|
|
109
|
+
return unless jit
|
|
110
|
+
|
|
111
|
+
engine = { yjit: :YJIT, zjit: :ZJIT }.fetch(jit.to_sym) do
|
|
112
|
+
raise ConfigurationError, "unknown config.jit #{jit.inspect} (use :yjit, :zjit or nil)"
|
|
113
|
+
end
|
|
114
|
+
return unless RubyVM.const_defined?(engine)
|
|
115
|
+
return if %i[YJIT ZJIT].any? { |name| RubyVM.const_defined?(name) && RubyVM.const_get(name).enabled? }
|
|
116
|
+
|
|
117
|
+
RubyVM.const_get(engine).enable
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def load_environment_config
|
|
121
|
+
file = root.join("config/environments/#{GemStack.env}.rb")
|
|
122
|
+
load file.to_s if file.file?
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def setup_loader
|
|
126
|
+
@loader = Zeitwerk::Loader.new
|
|
127
|
+
@loader.tag = "gemstack.app"
|
|
128
|
+
autoload_dirs.each { |dir| @loader.push_dir(dir) }
|
|
129
|
+
@loader.enable_reloading if config.reload_code
|
|
130
|
+
@loader.setup
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Inside the ErrorHandler, so errors raised while reloading (e.g. a
|
|
134
|
+
# SyntaxError) are rendered as JSON error responses.
|
|
135
|
+
def install_reloader
|
|
136
|
+
stack = config.http.middleware
|
|
137
|
+
return if stack.include?(Reloader)
|
|
138
|
+
|
|
139
|
+
if stack.include?(HTTP::Middleware::ErrorHandler)
|
|
140
|
+
stack.insert_after(HTTP::Middleware::ErrorHandler, Reloader, self)
|
|
141
|
+
else
|
|
142
|
+
stack.unshift(Reloader, self)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def load_routes
|
|
147
|
+
file = root.join("config/routes.rb")
|
|
148
|
+
load file.to_s if file.file?
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def build_router
|
|
152
|
+
@pending_routes = []
|
|
153
|
+
yield
|
|
154
|
+
router = HTTP::Router.new(prefix: config.http.api_path)
|
|
155
|
+
@pending_routes.each { |block| router.draw(&block) }
|
|
156
|
+
router
|
|
157
|
+
ensure
|
|
158
|
+
@pending_routes = nil
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GemStack
|
|
4
|
+
# A readers/writer lock. Requests hold a shared lock while application code
|
|
5
|
+
# runs; code reloading takes the exclusive lock, so a reload never happens
|
|
6
|
+
# while another thread is executing code that is about to be unloaded.
|
|
7
|
+
class Interlock
|
|
8
|
+
def initialize
|
|
9
|
+
@mutex = Mutex.new
|
|
10
|
+
@condition = ConditionVariable.new
|
|
11
|
+
@readers = 0
|
|
12
|
+
@writer = false
|
|
13
|
+
@waiting_writers = 0
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def acquire_shared
|
|
17
|
+
@mutex.synchronize do
|
|
18
|
+
# Waiting writers get priority so a busy server can't starve a reload.
|
|
19
|
+
@condition.wait(@mutex) while @writer || @waiting_writers.positive?
|
|
20
|
+
@readers += 1
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def release_shared
|
|
25
|
+
@mutex.synchronize do
|
|
26
|
+
@readers -= 1
|
|
27
|
+
@condition.broadcast if @readers.zero?
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def exclusive
|
|
32
|
+
@mutex.synchronize do
|
|
33
|
+
@waiting_writers += 1
|
|
34
|
+
@condition.wait(@mutex) while @writer || @readers.positive?
|
|
35
|
+
@waiting_writers -= 1
|
|
36
|
+
@writer = true
|
|
37
|
+
end
|
|
38
|
+
yield
|
|
39
|
+
ensure
|
|
40
|
+
@mutex.synchronize do
|
|
41
|
+
@writer = false
|
|
42
|
+
@condition.broadcast
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "gemstack/dev/file_watcher"
|
|
4
|
+
|
|
5
|
+
module GemStack
|
|
6
|
+
# Development middleware: reloads application code and routes before a
|
|
7
|
+
# request when files under app/ or config/routes.rb have changed
|
|
8
|
+
# (DECISIONS D-007). The shared lock is held until the response body is
|
|
9
|
+
# closed, so streamed responses finish on the code that produced them.
|
|
10
|
+
class Reloader
|
|
11
|
+
WATCHED = ["app/**/*", "config/routes.rb"].freeze
|
|
12
|
+
|
|
13
|
+
def initialize(app, application)
|
|
14
|
+
@app = app
|
|
15
|
+
@application = application
|
|
16
|
+
@watcher = Dev::FileWatcher.new(WATCHED, root: application.root)
|
|
17
|
+
@interlock = Interlock.new
|
|
18
|
+
@check = Mutex.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def call(env)
|
|
22
|
+
reload_if_changed
|
|
23
|
+
@interlock.acquire_shared
|
|
24
|
+
begin
|
|
25
|
+
status, headers, body = @app.call(env)
|
|
26
|
+
rescue Exception # rubocop:disable Lint/RescueException -- release the lock, then re-raise anything
|
|
27
|
+
@interlock.release_shared
|
|
28
|
+
raise
|
|
29
|
+
end
|
|
30
|
+
[status, headers, Rack::BodyProxy.new(body) { @interlock.release_shared }]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def reload_if_changed
|
|
36
|
+
changed = @check.synchronize { @watcher.changed? }
|
|
37
|
+
return unless changed
|
|
38
|
+
|
|
39
|
+
@interlock.exclusive { @application.reload! }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "minitest"
|
|
4
|
+
require "rack/test"
|
|
5
|
+
require "json"
|
|
6
|
+
|
|
7
|
+
module GemStack
|
|
8
|
+
# Test helpers for GemStack applications (Minitest + rack-test).
|
|
9
|
+
#
|
|
10
|
+
# class ProductsTest < GemStack::TestCase
|
|
11
|
+
# def test_index
|
|
12
|
+
# get_json "/api/products"
|
|
13
|
+
# assert_status 200
|
|
14
|
+
# assert_equal [], json_body
|
|
15
|
+
# end
|
|
16
|
+
# end
|
|
17
|
+
module Testing
|
|
18
|
+
module Helpers
|
|
19
|
+
include Rack::Test::Methods
|
|
20
|
+
|
|
21
|
+
def app = GemStack.application
|
|
22
|
+
|
|
23
|
+
# Parsed JSON body of the last response.
|
|
24
|
+
def json_body
|
|
25
|
+
JSON.parse(last_response.body)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def get_json(path, params = {}, headers = {})
|
|
29
|
+
get(path, params, json_headers(headers))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
%w[post put patch delete].each do |verb|
|
|
33
|
+
define_method(:"#{verb}_json") do |path, body = {}, headers = {}|
|
|
34
|
+
public_send(verb, path, JSON.generate(body),
|
|
35
|
+
json_headers(headers).merge("CONTENT_TYPE" => "application/json"))
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def assert_status(expected, message = nil)
|
|
40
|
+
expected = Rack::Utils::SYMBOL_TO_STATUS_CODE.fetch(expected) if expected.is_a?(Symbol)
|
|
41
|
+
message ||= "Expected status #{expected}, got #{last_response.status}: #{last_response.body[0, 500]}"
|
|
42
|
+
assert_equal expected, last_response.status, message
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# assert_error 422, "validation_failed"
|
|
46
|
+
def assert_error(status, code = nil)
|
|
47
|
+
assert_status status
|
|
48
|
+
assert_equal code, json_body.dig("error", "code") if code
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def json_headers(headers) = { "HTTP_ACCEPT" => "application/json" }.merge(headers)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
class TestCase < Minitest::Test
|
|
58
|
+
include Testing::Helpers
|
|
59
|
+
end
|
|
60
|
+
end
|
data/lib/gemstack.rb
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "gemstack/core"
|
|
4
|
+
require "gemstack/cache"
|
|
5
|
+
require "gemstack/http"
|
|
6
|
+
require "gemstack/dev"
|
|
7
|
+
require "gemstack/contract" # /api/docs in development; `gemstack contract`
|
|
8
|
+
require_relative "gemstack/interlock"
|
|
9
|
+
require_relative "gemstack/reloader"
|
|
10
|
+
require_relative "gemstack/application"
|
|
11
|
+
|
|
12
|
+
module GemStack
|
|
13
|
+
class Config
|
|
14
|
+
# Reload app/ code and routes between requests when files change.
|
|
15
|
+
setting :reload_code, default: -> { GemStack.env.development? }
|
|
16
|
+
# Load all application code at boot (faster first requests, copy-on-write
|
|
17
|
+
# friendly with Puma workers).
|
|
18
|
+
setting :eager_load, default: -> { !GemStack.env.local? }
|
|
19
|
+
# Extra directories to autoload, relative to the root (e.g. ["lib"]).
|
|
20
|
+
setting :autoload_paths, default: []
|
|
21
|
+
# Ruby JIT enabled at boot: :yjit (production default, measured fastest),
|
|
22
|
+
# :zjit (Ruby 4's newer JIT, opt-in), or nil. GEMSTACK_JIT=yjit|zjit|off overrides.
|
|
23
|
+
setting :jit, default: lambda {
|
|
24
|
+
case ENV.fetch("GEMSTACK_JIT", nil)
|
|
25
|
+
when "off", "none", "" then nil
|
|
26
|
+
when String then ENV["GEMSTACK_JIT"].to_sym
|
|
27
|
+
else GemStack.env.production? ? :yjit : nil
|
|
28
|
+
end
|
|
29
|
+
}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Short names for application code:
|
|
33
|
+
# class ApplicationController < GemStack::Controller
|
|
34
|
+
Controller = HTTP::Controller
|
|
35
|
+
Params = HTTP::Params
|
|
36
|
+
# GemStack::Schema, GemStack::Serializer and GemStack::Types come from gemstack-schema.
|
|
37
|
+
|
|
38
|
+
class << self
|
|
39
|
+
def application
|
|
40
|
+
@application ||= Application.new(config: config)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
attr_writer :application
|
|
44
|
+
|
|
45
|
+
def boot! = application.boot!
|
|
46
|
+
|
|
47
|
+
# config/routes.rb:
|
|
48
|
+
# GemStack.routes do
|
|
49
|
+
# resources :products
|
|
50
|
+
# end
|
|
51
|
+
def routes(&)
|
|
52
|
+
return application.routes unless block_given?
|
|
53
|
+
|
|
54
|
+
application.draw_routes(&)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
alias reset_core! reset!
|
|
58
|
+
|
|
59
|
+
def reset!
|
|
60
|
+
reset_core!
|
|
61
|
+
@application = nil
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: gemstack
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Shoaib Malik
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: gemstack-cache
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - '='
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 0.1.0
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - '='
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 0.1.0
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: gemstack-cli
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - '='
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 0.1.0
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - '='
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: 0.1.0
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: gemstack-contract
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - '='
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 0.1.0
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - '='
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: 0.1.0
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: gemstack-core
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - '='
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: 0.1.0
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - '='
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: 0.1.0
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: gemstack-dev
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - '='
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: 0.1.0
|
|
75
|
+
type: :runtime
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - '='
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: 0.1.0
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: gemstack-http
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - '='
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: 0.1.0
|
|
89
|
+
type: :runtime
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - '='
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: 0.1.0
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: gemstack-schema
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - '='
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: 0.1.0
|
|
103
|
+
type: :runtime
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - '='
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: 0.1.0
|
|
110
|
+
- !ruby/object:Gem::Dependency
|
|
111
|
+
name: puma
|
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - ">="
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: '6.4'
|
|
117
|
+
type: :runtime
|
|
118
|
+
prerelease: false
|
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - ">="
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '6.4'
|
|
124
|
+
- !ruby/object:Gem::Dependency
|
|
125
|
+
name: zeitwerk
|
|
126
|
+
requirement: !ruby/object:Gem::Requirement
|
|
127
|
+
requirements:
|
|
128
|
+
- - "~>"
|
|
129
|
+
- !ruby/object:Gem::Version
|
|
130
|
+
version: '2.6'
|
|
131
|
+
type: :runtime
|
|
132
|
+
prerelease: false
|
|
133
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - "~>"
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: '2.6'
|
|
138
|
+
email:
|
|
139
|
+
- gemstack26@gmail.com
|
|
140
|
+
executables: []
|
|
141
|
+
extensions: []
|
|
142
|
+
extra_rdoc_files: []
|
|
143
|
+
files:
|
|
144
|
+
- CHANGELOG.md
|
|
145
|
+
- LICENSE.txt
|
|
146
|
+
- README.md
|
|
147
|
+
- lib/gemstack.rb
|
|
148
|
+
- lib/gemstack/application.rb
|
|
149
|
+
- lib/gemstack/interlock.rb
|
|
150
|
+
- lib/gemstack/reloader.rb
|
|
151
|
+
- lib/gemstack/testing.rb
|
|
152
|
+
homepage: https://github.com/gemstack-rb/gemstack
|
|
153
|
+
licenses:
|
|
154
|
+
- MIT
|
|
155
|
+
metadata:
|
|
156
|
+
rubygems_mfa_required: 'true'
|
|
157
|
+
source_code_uri: https://github.com/gemstack-rb/gemstack/tree/main/gems/gemstack
|
|
158
|
+
changelog_uri: https://github.com/gemstack-rb/gemstack/blob/main/gems/gemstack/CHANGELOG.md
|
|
159
|
+
bug_tracker_uri: https://github.com/gemstack-rb/gemstack/issues
|
|
160
|
+
documentation_uri: https://github.com/gemstack-rb/gemstack/tree/main/docs
|
|
161
|
+
rdoc_options: []
|
|
162
|
+
require_paths:
|
|
163
|
+
- lib
|
|
164
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
165
|
+
requirements:
|
|
166
|
+
- - ">="
|
|
167
|
+
- !ruby/object:Gem::Version
|
|
168
|
+
version: '4.0'
|
|
169
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
170
|
+
requirements:
|
|
171
|
+
- - ">="
|
|
172
|
+
- !ruby/object:Gem::Version
|
|
173
|
+
version: '0'
|
|
174
|
+
requirements: []
|
|
175
|
+
rubygems_version: 4.0.20
|
|
176
|
+
specification_version: 4
|
|
177
|
+
summary: 'GemStack: a fast, modular Ruby API framework for Next.js applications'
|
|
178
|
+
test_files: []
|