steppe 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/.rspec +3 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +5 -0
- data/CLAUDE.md +88 -0
- data/LICENSE.txt +21 -0
- data/README.md +883 -0
- data/Rakefile +23 -0
- data/docs/README.md +3 -0
- data/docs/styles.css +527 -0
- data/examples/hanami.ru +29 -0
- data/examples/service.rb +323 -0
- data/examples/sinatra.rb +38 -0
- data/lib/docs_builder.rb +253 -0
- data/lib/steppe/auth/basic.rb +130 -0
- data/lib/steppe/auth/bearer.rb +130 -0
- data/lib/steppe/auth.rb +46 -0
- data/lib/steppe/content_type.rb +80 -0
- data/lib/steppe/endpoint.rb +742 -0
- data/lib/steppe/openapi_visitor.rb +155 -0
- data/lib/steppe/request.rb +22 -0
- data/lib/steppe/responder.rb +165 -0
- data/lib/steppe/responder_registry.rb +79 -0
- data/lib/steppe/result.rb +68 -0
- data/lib/steppe/serializer.rb +180 -0
- data/lib/steppe/service.rb +232 -0
- data/lib/steppe/status_map.rb +82 -0
- data/lib/steppe/utils.rb +19 -0
- data/lib/steppe/version.rb +5 -0
- data/lib/steppe.rb +44 -0
- data/sig/steppe.rbs +4 -0
- metadata +143 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'steppe/auth'
|
|
4
|
+
|
|
5
|
+
module Steppe
|
|
6
|
+
class Service
|
|
7
|
+
VERBS = %i[get post put patch delete].freeze
|
|
8
|
+
|
|
9
|
+
class Server < Types::Data
|
|
10
|
+
attribute :url, Types::Forms::URI::HTTP
|
|
11
|
+
attribute? :description, String
|
|
12
|
+
|
|
13
|
+
def node_name = :server
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class Tag < Types::Data
|
|
17
|
+
attribute :name, String
|
|
18
|
+
attribute :description, Types::String.nullable
|
|
19
|
+
attribute :external_docs, Types::Forms::URI::HTTP.nullable
|
|
20
|
+
def node_name = :tag
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
attr_reader :node_name, :servers, :tags, :security_schemes, :registered_security_schemes
|
|
24
|
+
attr_accessor :title, :description, :version
|
|
25
|
+
|
|
26
|
+
def initialize(&)
|
|
27
|
+
@lookup = {}
|
|
28
|
+
@title = ''
|
|
29
|
+
@description = ''
|
|
30
|
+
@version = '0.0.1'
|
|
31
|
+
@node_name = :service
|
|
32
|
+
@servers = []
|
|
33
|
+
@tags = []
|
|
34
|
+
@security_schemes = {}
|
|
35
|
+
@registered_security_schemes = {}
|
|
36
|
+
yield self if block_given?
|
|
37
|
+
freeze
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def [](name) = @lookup[name]
|
|
41
|
+
def endpoints = @lookup.values
|
|
42
|
+
|
|
43
|
+
def server(args = {})
|
|
44
|
+
@servers << Server.parse(args)
|
|
45
|
+
self
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def tag(name, description: nil, external_docs: nil)
|
|
49
|
+
@tags << Tag.parse(name:, description:, external_docs:)
|
|
50
|
+
self
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Register a Bearer token authentication security scheme.
|
|
54
|
+
# This is a convenience method that creates a Bearer auth scheme and registers it.
|
|
55
|
+
#
|
|
56
|
+
# @see https://swagger.io/docs/specification/v3_0/authentication/
|
|
57
|
+
# @see Auth::Bearer
|
|
58
|
+
#
|
|
59
|
+
# @param name [String] The security scheme name (used to reference in endpoints)
|
|
60
|
+
# @param store [Hash, Auth::TokenStoreInterface] Token store mapping tokens to scopes.
|
|
61
|
+
# Can be a Hash (converted to HashTokenStore) or a custom store implementing the TokenStoreInterface.
|
|
62
|
+
# @param format [String] Bearer token format hint for documentation (e.g., 'JWT', 'opaque')
|
|
63
|
+
# @return [self] Returns self for method chaining
|
|
64
|
+
#
|
|
65
|
+
# @example Basic usage with hash store
|
|
66
|
+
# service.bearer_auth 'api_key', store: {
|
|
67
|
+
# 'token123' => ['read:users', 'write:users'],
|
|
68
|
+
# 'token456' => ['read:posts']
|
|
69
|
+
# }
|
|
70
|
+
#
|
|
71
|
+
# @example With JWT format hint
|
|
72
|
+
# service.bearer_auth 'jwt_auth', store: my_token_store, format: 'JWT'
|
|
73
|
+
def bearer_auth(name, store: {}, format: 'string')
|
|
74
|
+
security_scheme Auth::Bearer.new(name, store:, format:)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Register a Basic HTTP authentication security scheme.
|
|
78
|
+
# This is a convenience method that creates a Basic auth scheme and registers it.
|
|
79
|
+
#
|
|
80
|
+
# @see https://swagger.io/docs/specification/v3_0/authentication/
|
|
81
|
+
# @see Auth::Basic
|
|
82
|
+
#
|
|
83
|
+
# @param name [String] The security scheme name (used to reference in endpoints)
|
|
84
|
+
# @param store [Hash, Auth::Basic::CredentialsStoreInterface] Credentials store mapping usernames to passwords.
|
|
85
|
+
# Can be a Hash (converted to SimpleUserPasswordStore) or a custom store implementing the CredentialsStoreInterface.
|
|
86
|
+
# @return [self]
|
|
87
|
+
#
|
|
88
|
+
# @example Basic usage with hash store
|
|
89
|
+
# service.basic_auth 'BasicAuth', store: {
|
|
90
|
+
# 'admin' => 'secret123',
|
|
91
|
+
# 'user' => 'password456'
|
|
92
|
+
# }
|
|
93
|
+
#
|
|
94
|
+
# @example With custom credentials store
|
|
95
|
+
# class DatabaseCredentialsStore
|
|
96
|
+
# def lookup(username)
|
|
97
|
+
# user = User.find_by(username: username)
|
|
98
|
+
# user&.password_digest
|
|
99
|
+
# end
|
|
100
|
+
# end
|
|
101
|
+
#
|
|
102
|
+
# service.basic_auth 'BasicAuth', store: DatabaseCredentialsStore.new
|
|
103
|
+
#
|
|
104
|
+
# @example Using in an endpoint
|
|
105
|
+
# service.basic_auth 'BasicAuth', store: { 'admin' => 'secret' }
|
|
106
|
+
# service.get :protected, '/protected' do |e|
|
|
107
|
+
# e.security 'BasicAuth'
|
|
108
|
+
# # ... endpoint definition
|
|
109
|
+
# end
|
|
110
|
+
def basic_auth(name, store: {})
|
|
111
|
+
security_scheme Auth::Basic.new(name, store:)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Register a security scheme for use in endpoints.
|
|
115
|
+
# Security schemes define authentication methods that can be applied to endpoints.
|
|
116
|
+
#
|
|
117
|
+
# @see https://swagger.io/docs/specification/v3_0/authentication/
|
|
118
|
+
# @see Auth::SecuritySchemeInterface
|
|
119
|
+
#
|
|
120
|
+
# @param scheme [Auth::SecuritySchemeInterface] A security scheme object implementing the SecuritySchemeInterface
|
|
121
|
+
# @return [self] Returns self for method chaining
|
|
122
|
+
#
|
|
123
|
+
# @example Register a custom security scheme
|
|
124
|
+
# bearer = Steppe::Auth::Bearer.new('my_auth', store: token_store)
|
|
125
|
+
# service.security_scheme(bearer)
|
|
126
|
+
#
|
|
127
|
+
# @example Register and use in an endpoint
|
|
128
|
+
# service.bearer_auth 'api_key', store: { 'token123' => ['read:users'] }
|
|
129
|
+
# service.get :users, '/users' do |e|
|
|
130
|
+
# e.security 'api_key', ['read:users']
|
|
131
|
+
# # ... endpoint definition
|
|
132
|
+
# end
|
|
133
|
+
def security_scheme(scheme)
|
|
134
|
+
scheme => Auth::SecuritySchemeInterface
|
|
135
|
+
@security_schemes[scheme.name] = scheme
|
|
136
|
+
self
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Apply a security requirement globally to endpoints defined after this call.
|
|
140
|
+
# This registers a security scheme with required scopes at the service level,
|
|
141
|
+
# making it apply to all endpoints defined after this method is called.
|
|
142
|
+
#
|
|
143
|
+
# IMPORTANT: Order matters! This method only applies security to endpoints
|
|
144
|
+
# defined AFTER it is called, not to endpoints defined before.
|
|
145
|
+
#
|
|
146
|
+
# @see https://swagger.io/docs/specification/v3_0/authentication/
|
|
147
|
+
# @see Endpoint#security
|
|
148
|
+
#
|
|
149
|
+
# @param scheme_name [String] The name of a registered security scheme
|
|
150
|
+
# @param scopes [Array<String>] Required scopes for this security requirement
|
|
151
|
+
# @return [self] Returns self for method chaining
|
|
152
|
+
# @raise [KeyError] If the security scheme has not been registered
|
|
153
|
+
#
|
|
154
|
+
# @example Apply Bearer auth globally to all endpoints defined after
|
|
155
|
+
# service.bearer_auth 'api_key', store: {
|
|
156
|
+
# 'token123' => ['read:users', 'write:users']
|
|
157
|
+
# }
|
|
158
|
+
# service.security 'api_key', ['read:users']
|
|
159
|
+
# # All endpoints defined below will require this security
|
|
160
|
+
#
|
|
161
|
+
# @example Order matters - security only applies to endpoints after the call
|
|
162
|
+
# service.bearer_auth 'api_key', store: tokens
|
|
163
|
+
# service.get :public_endpoint, '/public' { } # No security required
|
|
164
|
+
# service.security 'api_key', ['read:users']
|
|
165
|
+
# service.get :protected_endpoint, '/protected' { } # Security required
|
|
166
|
+
#
|
|
167
|
+
# @example Multiple security schemes
|
|
168
|
+
# service.bearer_auth 'api_key', store: tokens
|
|
169
|
+
# service.bearer_auth 'admin_key', store: admin_tokens
|
|
170
|
+
# service.security 'api_key', ['read:users']
|
|
171
|
+
# service.security 'admin_key', ['admin']
|
|
172
|
+
def security(scheme_name, scopes = [])
|
|
173
|
+
scheme = security_schemes.fetch(scheme_name)
|
|
174
|
+
@registered_security_schemes[scheme_name] = scopes
|
|
175
|
+
self
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# A custom serializer that generates the OpenAPI specification in JSON format.
|
|
179
|
+
class OpenAPISerializer
|
|
180
|
+
# @param service [Steppe::Service] The service instance to generate the OpenAPI spec from.
|
|
181
|
+
def initialize(service)
|
|
182
|
+
@service = service
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# @param conn [Steppe::Result]
|
|
186
|
+
# @return [String] JSON data
|
|
187
|
+
def render(conn)
|
|
188
|
+
spec = Steppe::OpenAPIVisitor.from_request(@service, conn.request)
|
|
189
|
+
JSON.dump(spec)
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Generates an endpoint that serves the OpenAPI specification in JSON format.
|
|
194
|
+
# @param path [String] The path where the OpenAPI spec will be available (default: '/')
|
|
195
|
+
def specs(path = '/')
|
|
196
|
+
get :__open_api, path do |e|
|
|
197
|
+
e.no_spec!
|
|
198
|
+
e.json 200..299, OpenAPISerializer.new(self)
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Registers all defined endpoints with the given router.
|
|
203
|
+
# The router is expected to respond to HTTP verb methods (e.g., get, post).
|
|
204
|
+
# ie. router.get '/users/:id', to: rack_endpoint
|
|
205
|
+
# @example
|
|
206
|
+
# app = MyService.route_with(Hanami::Router.new)
|
|
207
|
+
# run app
|
|
208
|
+
#
|
|
209
|
+
# @example
|
|
210
|
+
# app = Hanami::Router.new do
|
|
211
|
+
# scope '/api' do
|
|
212
|
+
# MyService.route_with(self)
|
|
213
|
+
# end
|
|
214
|
+
# end
|
|
215
|
+
#
|
|
216
|
+
# @param router [Object] A router instance that responds to HTTP verb methods (e.g., get, post).
|
|
217
|
+
# @return [Object] The router with registered endpoints.
|
|
218
|
+
def route_with(router)
|
|
219
|
+
endpoints.each do |endpoint|
|
|
220
|
+
router.public_send(endpoint.verb, endpoint.path.to_s, to: endpoint.to_rack)
|
|
221
|
+
end
|
|
222
|
+
router
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
VERBS.each do |verb|
|
|
226
|
+
define_method(verb) do |name, path, &block|
|
|
227
|
+
@lookup[name] = Endpoint.new(self, name, verb, path:, &block)
|
|
228
|
+
self
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Steppe
|
|
4
|
+
class StatusMap
|
|
5
|
+
def initialize
|
|
6
|
+
@responders = []
|
|
7
|
+
@index = nil
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def <<(responder)
|
|
11
|
+
@responders << responder
|
|
12
|
+
build_index
|
|
13
|
+
self
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def each(&block)
|
|
17
|
+
return enum_for(:each) unless block
|
|
18
|
+
|
|
19
|
+
@responders.each(&block)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def find(status)
|
|
23
|
+
lo = 0
|
|
24
|
+
hi = @index.size - 1
|
|
25
|
+
|
|
26
|
+
while lo <= hi
|
|
27
|
+
mid = (lo + hi) / 2
|
|
28
|
+
start, finish, responder = @index[mid]
|
|
29
|
+
|
|
30
|
+
if status < start
|
|
31
|
+
hi = mid - 1
|
|
32
|
+
elsif status > finish
|
|
33
|
+
lo = mid + 1
|
|
34
|
+
else
|
|
35
|
+
return responder
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
nil
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def build_index
|
|
45
|
+
# Collect all boundary points with priorities
|
|
46
|
+
points = []
|
|
47
|
+
@responders.each_with_index do |responder, index|
|
|
48
|
+
range = responder.statuses
|
|
49
|
+
# Add index as priority - earlier additions have higher priority
|
|
50
|
+
points << [range.begin, :start, index, responder]
|
|
51
|
+
points << [range.end + 1, :end, index, responder]
|
|
52
|
+
end
|
|
53
|
+
# Sort by position, then by type (:end before :start at same position),
|
|
54
|
+
# then by priority (lower index first)
|
|
55
|
+
points.sort_by! { |pos, type, priority, _| [pos, type == :start ? 1 : 0, priority] }
|
|
56
|
+
|
|
57
|
+
# Build non-overlapping segments
|
|
58
|
+
segments = []
|
|
59
|
+
active = {} # Map from responder to priority
|
|
60
|
+
prev_point = nil
|
|
61
|
+
|
|
62
|
+
points.each do |point, type, priority, responder|
|
|
63
|
+
# If we have active responders and moved to a new point, create segment
|
|
64
|
+
if !active.empty? && prev_point && prev_point < point
|
|
65
|
+
# Use the highest priority responder (min priority value = first added)
|
|
66
|
+
winner = active.min_by { |_, p| p }.first
|
|
67
|
+
segments << [prev_point, point - 1, winner]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
if type == :start
|
|
71
|
+
active[responder] = priority
|
|
72
|
+
else
|
|
73
|
+
active.delete(responder)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
prev_point = point
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
@index = segments
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
data/lib/steppe/utils.rb
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Steppe
|
|
4
|
+
module Utils
|
|
5
|
+
def self.deep_symbolize_keys(hash)
|
|
6
|
+
hash.each.with_object({}) do |(k, v), h|
|
|
7
|
+
value = case v
|
|
8
|
+
when Hash
|
|
9
|
+
deep_symbolize_keys(v)
|
|
10
|
+
when Array
|
|
11
|
+
v.map { |e| e.is_a?(Hash) ? deep_symbolize_keys(e) : e }
|
|
12
|
+
else
|
|
13
|
+
v
|
|
14
|
+
end
|
|
15
|
+
h[k.to_sym] = value
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
data/lib/steppe.rb
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'plumb'
|
|
4
|
+
require 'tempfile'
|
|
5
|
+
|
|
6
|
+
require_relative 'steppe/version'
|
|
7
|
+
require_relative 'steppe/content_type'
|
|
8
|
+
|
|
9
|
+
module Steppe
|
|
10
|
+
class Error < StandardError; end
|
|
11
|
+
|
|
12
|
+
Plumb.policy :desc, helper: true do |type, description|
|
|
13
|
+
type.metadata(description:)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
Plumb.policy :example, helper: true do |type, example|
|
|
17
|
+
type.metadata(example:)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
module Types
|
|
21
|
+
include Plumb::Types
|
|
22
|
+
|
|
23
|
+
class UploadedFile < Data
|
|
24
|
+
attribute :filename, String
|
|
25
|
+
attribute :type, String
|
|
26
|
+
attribute :name, String
|
|
27
|
+
attribute :tempfile, ::Tempfile
|
|
28
|
+
attribute :head, String
|
|
29
|
+
|
|
30
|
+
def self.node_name = :uploaded_file
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
module ContentTypes
|
|
35
|
+
JSON = ContentType.parse('application/json')
|
|
36
|
+
TEXT = ContentType.parse('text/plain')
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
require_relative 'steppe/request'
|
|
41
|
+
require_relative 'steppe/responder'
|
|
42
|
+
require_relative 'steppe/service'
|
|
43
|
+
require_relative 'steppe/endpoint'
|
|
44
|
+
require_relative 'steppe/openapi_visitor'
|
data/sig/steppe.rbs
ADDED
metadata
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: steppe
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Ismael Celis
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: mustermann
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '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'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: papercraft
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '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'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: mustermann-contrib
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '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'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: plumb
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: 0.0.15
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: 0.0.15
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: rack
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '3.0'
|
|
75
|
+
type: :runtime
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '3.0'
|
|
82
|
+
description: Composable, self-documenting REST APIs in Ruby
|
|
83
|
+
email:
|
|
84
|
+
- ismaelct@gmail.com
|
|
85
|
+
executables: []
|
|
86
|
+
extensions: []
|
|
87
|
+
extra_rdoc_files: []
|
|
88
|
+
files:
|
|
89
|
+
- ".rspec"
|
|
90
|
+
- ".ruby-version"
|
|
91
|
+
- CHANGELOG.md
|
|
92
|
+
- CLAUDE.md
|
|
93
|
+
- LICENSE.txt
|
|
94
|
+
- README.md
|
|
95
|
+
- Rakefile
|
|
96
|
+
- docs/README.md
|
|
97
|
+
- docs/styles.css
|
|
98
|
+
- examples/hanami.ru
|
|
99
|
+
- examples/service.rb
|
|
100
|
+
- examples/sinatra.rb
|
|
101
|
+
- lib/docs_builder.rb
|
|
102
|
+
- lib/steppe.rb
|
|
103
|
+
- lib/steppe/auth.rb
|
|
104
|
+
- lib/steppe/auth/basic.rb
|
|
105
|
+
- lib/steppe/auth/bearer.rb
|
|
106
|
+
- lib/steppe/content_type.rb
|
|
107
|
+
- lib/steppe/endpoint.rb
|
|
108
|
+
- lib/steppe/openapi_visitor.rb
|
|
109
|
+
- lib/steppe/request.rb
|
|
110
|
+
- lib/steppe/responder.rb
|
|
111
|
+
- lib/steppe/responder_registry.rb
|
|
112
|
+
- lib/steppe/result.rb
|
|
113
|
+
- lib/steppe/serializer.rb
|
|
114
|
+
- lib/steppe/service.rb
|
|
115
|
+
- lib/steppe/status_map.rb
|
|
116
|
+
- lib/steppe/utils.rb
|
|
117
|
+
- lib/steppe/version.rb
|
|
118
|
+
- sig/steppe.rbs
|
|
119
|
+
homepage: https://www.github.com/ismasan/steppe
|
|
120
|
+
licenses:
|
|
121
|
+
- MIT
|
|
122
|
+
metadata:
|
|
123
|
+
homepage_uri: https://www.github.com/ismasan/steppe
|
|
124
|
+
source_code_uri: https://www.github.com/ismasan/steppe
|
|
125
|
+
changelog_uri: https://www.github.com/ismasan/steppe
|
|
126
|
+
rdoc_options: []
|
|
127
|
+
require_paths:
|
|
128
|
+
- lib
|
|
129
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
130
|
+
requirements:
|
|
131
|
+
- - ">="
|
|
132
|
+
- !ruby/object:Gem::Version
|
|
133
|
+
version: 3.0.0
|
|
134
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - ">="
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: '0'
|
|
139
|
+
requirements: []
|
|
140
|
+
rubygems_version: 3.6.9
|
|
141
|
+
specification_version: 4
|
|
142
|
+
summary: Composable, self-documenting REST APIs in Ruby
|
|
143
|
+
test_files: []
|