gr_api_manager 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/README.md +345 -0
- data/lib/gr_api_manager.rb +149 -0
- metadata +75 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f5f6ec1afb3c1aed64ba13f1d41198f656b52918b061bb0e9fffbb28145ec681
|
|
4
|
+
data.tar.gz: eb578c753e0a5f662dcdc5198855e3f063c6241d3b41f531e3eaaebfb2c66dd4
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 840d926e759021f66b36e4148a41dc46a6e26bc74aad62aeab1cfef669620dcb3f344febba2bfe3ed287568844c141b92e3004e193d90d2c9d3d95faa117aa7d
|
|
7
|
+
data.tar.gz: 3e65530682e1ae7bded34147ef2df0f75e345bcaf702ed46f54d59fb746ed373d2e1d59344be796723f26af3c8cb26db54cdb42cee2a940c70b9cdd4b983aad7
|
data/README.md
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
# GR API Manager
|
|
2
|
+
|
|
3
|
+
[](https://rubygems.org/gems/gr-api-manager)
|
|
4
|
+
|
|
5
|
+
A minimal, opinionated Ruby wrapper around Sinatra that eliminates boilerplate from REST API development. Authentication, parameter validation, type casting, CORS, and error handling are handled at the framework level — you write only the business logic.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Available on RubyGems!** Install globally and use it in any project.
|
|
12
|
+
- Route-level Bearer Token authentication (opt-in/opt-out per endpoint).
|
|
13
|
+
- Declarative required parameter validation with automatic `400 Bad Request` responses.
|
|
14
|
+
- Smart type casting for query string values (`"10"` -> `Integer`, `"true"` -> `TrueClass`, `"9.5"` -> `Float`).
|
|
15
|
+
- Global JSON error responses for `404` and `500`.
|
|
16
|
+
- Broad CORS and `OPTIONS` preflight handling out of the box (Frontend ready).
|
|
17
|
+
- API versioning via configurable route prefix (e.g. `/api/v1`).
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
Add this line to your application's Gemfile:
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
gem 'gr-api-manager'
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
And then execute:
|
|
30
|
+
```bash
|
|
31
|
+
bundle install
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or install it yourself directly via RubyGems:
|
|
35
|
+
```bash
|
|
36
|
+
gem install gr-api-manager
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
```ruby
|
|
44
|
+
require 'gr_api_manager'
|
|
45
|
+
|
|
46
|
+
api = GRApiManager::Server.new(
|
|
47
|
+
port: 4567,
|
|
48
|
+
bearer_token: "your_secret_token",
|
|
49
|
+
prefix: "/api/v1"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Public endpoint
|
|
53
|
+
api.get('/health', auth: false) do
|
|
54
|
+
{ status: 'online', time: Time.now.to_s }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
api.run!
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
curl http://localhost:4567/api/v1/health
|
|
62
|
+
# => {"status":"online","time":"..."}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Configuration
|
|
68
|
+
|
|
69
|
+
All parameters are optional. If omitted, the manager falls back to environment variables loaded from a `.env` file at the project root (via `dotenv`). If neither is provided, defaults are used.
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
GRApiManager::Server.new(
|
|
73
|
+
port: 4567, # Default: ENV['PORT'] || 4000
|
|
74
|
+
bearer_token: "secret", # Default: ENV['API_TOKEN']
|
|
75
|
+
permitted_hosts: ["example.com"], # Host allowlist — empty means allow all
|
|
76
|
+
prefix: "/api/v1" # Route prefix applied to all endpoints
|
|
77
|
+
)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Using a .env file (Recommended)
|
|
81
|
+
|
|
82
|
+
Create a `.env` file at the root of your project:
|
|
83
|
+
|
|
84
|
+
```env
|
|
85
|
+
PORT=4567
|
|
86
|
+
API_TOKEN=your_secret_token
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Then initialize the manager with no arguments and it will pick everything up automatically:
|
|
90
|
+
|
|
91
|
+
```ruby
|
|
92
|
+
require 'gr_api_manager'
|
|
93
|
+
|
|
94
|
+
api = GRApiManager::Server.new
|
|
95
|
+
# Reads PORT and API_TOKEN from .env
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
This is the recommended approach for production — keep secrets out of source code and out of version control. Add `.env` to your `.gitignore`.
|
|
99
|
+
|
|
100
|
+
```text
|
|
101
|
+
# .gitignore
|
|
102
|
+
.env
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Priority order
|
|
106
|
+
|
|
107
|
+
When a value is provided both in code and in `.env`, the explicit argument always wins:
|
|
108
|
+
|
|
109
|
+
```text
|
|
110
|
+
new(bearer_token: "hardcoded") > ENV['API_TOKEN'] > nil (auth disabled)
|
|
111
|
+
new(port: 4567) > ENV['PORT'] > 4000
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Defining Routes
|
|
117
|
+
|
|
118
|
+
The manager exposes `get`, `post`, `put`, and `delete` methods. Each route receives a merged `params` hash containing both URL/query parameters (type-cast) and the parsed JSON body.
|
|
119
|
+
|
|
120
|
+
```ruby
|
|
121
|
+
api.get('/users', auth: true, requires: [:role, :age]) do |params|
|
|
122
|
+
# params is a merged, type-cast hash of all inputs
|
|
123
|
+
{
|
|
124
|
+
requested_role: params[:role],
|
|
125
|
+
is_adult: params[:age] >= 18 # age is safely cast to Integer
|
|
126
|
+
}
|
|
127
|
+
end
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Options
|
|
131
|
+
|
|
132
|
+
| Option | Type | Default | Description |
|
|
133
|
+
|------------|---------|---------|--------------------------------------------------|
|
|
134
|
+
| `auth` | Boolean | `true` | Require Bearer Token for this route |
|
|
135
|
+
| `requires` | Array | `[]` | List of required parameter keys (symbols/strings)|
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Authentication
|
|
140
|
+
|
|
141
|
+
All routes require a valid Bearer Token by default. Pass the token in the `Authorization` header:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
curl -H "Authorization: Bearer your_secret_token" http://localhost:4567/api/v1/users
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
To make a route public, set `auth: false`:
|
|
148
|
+
|
|
149
|
+
```ruby
|
|
150
|
+
api.get('/health', auth: false) do
|
|
151
|
+
{ status: 'online' }
|
|
152
|
+
end
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Automatic Error Responses:**
|
|
156
|
+
|
|
157
|
+
```json
|
|
158
|
+
// Missing header (401)
|
|
159
|
+
{ "error": "Token required. Format: 'Bearer <token>'" }
|
|
160
|
+
|
|
161
|
+
// Wrong token (403)
|
|
162
|
+
{ "error": "Invalid token" }
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Parameter Validation
|
|
168
|
+
|
|
169
|
+
Declare required parameters at the route level. If any are missing or blank, the framework responds with `400 Bad Request` automatically — no conditional logic needed in your handler.
|
|
170
|
+
|
|
171
|
+
```ruby
|
|
172
|
+
api.post('/users', requires: [:name, :email]) do |params|
|
|
173
|
+
status 201
|
|
174
|
+
{ message: "User created", user: params }
|
|
175
|
+
end
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
curl -X POST http://localhost:4567/api/v1/users \
|
|
180
|
+
-H "Authorization: Bearer secret" \
|
|
181
|
+
-H "Content-Type: application/json" \
|
|
182
|
+
-d '{"name": "Gabo"}'
|
|
183
|
+
|
|
184
|
+
# => 400 { "error": "Missing required parameters", "required": ["email"] }
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Smart Type Casting
|
|
190
|
+
|
|
191
|
+
Query string parameters are automatically cast to native Ruby types before reaching your handler.
|
|
192
|
+
|
|
193
|
+
| Input string | Ruby type | Value |
|
|
194
|
+
|--------------|-----------|----------|
|
|
195
|
+
| `"42"` | Integer | `42` |
|
|
196
|
+
| `"3.14"` | Float | `3.14` |
|
|
197
|
+
| `"true"` | TrueClass | `true` |
|
|
198
|
+
| `"false"` | FalseClass| `false` |
|
|
199
|
+
| `"hello"` | String | `"hello"`|
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
curl "http://localhost:4567/api/v1/test/types?age=18&active=true&score=9.5" \
|
|
203
|
+
-H "Authorization: Bearer secret"
|
|
204
|
+
|
|
205
|
+
# => { "age": 18, "active": true, "score": 9.5 }
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Error Handling
|
|
211
|
+
|
|
212
|
+
Global handlers return consistent JSON for unmatched routes and unhandled exceptions.
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
// 404
|
|
216
|
+
{ "error": "Endpoint not found", "path": "/api/v1/missing" }
|
|
217
|
+
|
|
218
|
+
// 500
|
|
219
|
+
{ "error": "Internal Server Error", "details": "..." }
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
You can also set status codes and short-circuit responses manually inside any handler:
|
|
223
|
+
|
|
224
|
+
```ruby
|
|
225
|
+
api.get('/users/:id') do |params|
|
|
226
|
+
if params[:id] == 0
|
|
227
|
+
status 404
|
|
228
|
+
next { error: "Invalid user ID" }
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
{ id: params[:id], name: "User_#{params[:id]}" }
|
|
232
|
+
end
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Request Logging
|
|
238
|
+
|
|
239
|
+
Every request is logged to stdout with a timestamp and color-coded status:
|
|
240
|
+
|
|
241
|
+
```text
|
|
242
|
+
[14:32:01] GET /api/v1/health - 200
|
|
243
|
+
[14:32:05] POST /api/v1/users - 400
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Green for 2xx, red for everything else.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Running the Server
|
|
251
|
+
|
|
252
|
+
```ruby
|
|
253
|
+
api.run!
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
```text
|
|
257
|
+
=============================================
|
|
258
|
+
GR API MANAGER STARTED
|
|
259
|
+
Port : 4567
|
|
260
|
+
Auth : Enabled
|
|
261
|
+
Prefix : /api/v1
|
|
262
|
+
=============================================
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Full Usage Example
|
|
268
|
+
|
|
269
|
+
Below is a complete working API covering the most common patterns.
|
|
270
|
+
|
|
271
|
+
```ruby
|
|
272
|
+
require 'gr_api_manager'
|
|
273
|
+
|
|
274
|
+
api = GRApiManager::Server.new(
|
|
275
|
+
port: 4567,
|
|
276
|
+
bearer_token: "secret123",
|
|
277
|
+
prefix: "/api/v1"
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
# 1. Public health check — no token required
|
|
281
|
+
api.get('/health', auth: false) do
|
|
282
|
+
{ status: 'online', time: Time.now.strftime("%Y-%m-%d %H:%M:%S") }
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
# 2. List users — token required (default)
|
|
286
|
+
api.get('/users') do |params|
|
|
287
|
+
users = [
|
|
288
|
+
{ id: 1, name: "Alice", role: "admin" },
|
|
289
|
+
{ id: 2, name: "Bob", role: "viewer" }
|
|
290
|
+
]
|
|
291
|
+
{ users: users, total: users.length }
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# 3. Get single user by ID — :id is cast to Integer automatically
|
|
295
|
+
api.get('/users/:id') do |params|
|
|
296
|
+
if params[:id] == 0
|
|
297
|
+
status 404
|
|
298
|
+
next { error: "User not found" }
|
|
299
|
+
end
|
|
300
|
+
{ id: params[:id], name: "User_#{params[:id]}" }
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# 4. Create user — validates required fields before reaching the block
|
|
304
|
+
api.post('/users', requires: [:name, :role]) do |params|
|
|
305
|
+
status 201
|
|
306
|
+
{ message: "User created", user: { name: params[:name], role: params[:role] } }
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# 5. Update user
|
|
310
|
+
api.put('/users/:id', requires: [:name]) do |params|
|
|
311
|
+
{ message: "User #{params[:id]} updated", name: params[:name] }
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# 6. Delete user
|
|
315
|
+
api.delete('/users/:id') do |params|
|
|
316
|
+
{ message: "User #{params[:id]} deleted" }
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
api.run!
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Minimal setup using only .env
|
|
323
|
+
|
|
324
|
+
```env
|
|
325
|
+
# .env
|
|
326
|
+
PORT=4567
|
|
327
|
+
API_TOKEN=secret123
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
```ruby
|
|
331
|
+
# api_server.rb
|
|
332
|
+
require 'gr_api_manager'
|
|
333
|
+
|
|
334
|
+
api = GRApiManager::Server.new # reads everything from .env
|
|
335
|
+
|
|
336
|
+
api.get('/ping', auth: false) { { pong: true } }
|
|
337
|
+
|
|
338
|
+
api.run!
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## License
|
|
344
|
+
|
|
345
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
require 'sinatra/base'
|
|
2
|
+
require 'json'
|
|
3
|
+
require 'dotenv/load'
|
|
4
|
+
|
|
5
|
+
module GRApiManager
|
|
6
|
+
class Server
|
|
7
|
+
attr_reader :app_class
|
|
8
|
+
|
|
9
|
+
# Initializes the server configuration.
|
|
10
|
+
def initialize(port: nil, bearer_token: nil, permitted_hosts: [], prefix: '')
|
|
11
|
+
@port = port || ENV['PORT'] || 4000
|
|
12
|
+
@token = bearer_token || ENV['API_TOKEN']
|
|
13
|
+
@permitted_hosts = permitted_hosts.empty? ? [] : permitted_hosts
|
|
14
|
+
@prefix = prefix
|
|
15
|
+
|
|
16
|
+
@app_class = Class.new(Sinatra::Base) do
|
|
17
|
+
|
|
18
|
+
# Logs HTTP requests with status-based color coding.
|
|
19
|
+
def log_request(method, path, params, status_code)
|
|
20
|
+
color = status_code.between?(200, 299) ? "\e[32m" : "\e[31m"
|
|
21
|
+
puts "[#{Time.now.strftime('%H:%M:%S')}] #{color}#{method} #{path} - #{status_code}\e[0m"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Casts string URL parameters to native Ruby types (Integer, Float, Boolean).
|
|
25
|
+
def smart_parse(hash)
|
|
26
|
+
hash.transform_values do |val|
|
|
27
|
+
case val
|
|
28
|
+
when 'true' then true
|
|
29
|
+
when 'false' then false
|
|
30
|
+
when /^[0-9]+$/ then val.to_i
|
|
31
|
+
when /^[0-9]+\.[0-9]+$/ then val.to_f
|
|
32
|
+
else val
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
configure_app
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
# Sets up Sinatra environment, CORS policies, and global error handlers.
|
|
44
|
+
def configure_app
|
|
45
|
+
app = @app_class
|
|
46
|
+
app.set :port, @port
|
|
47
|
+
app.set :bind, '0.0.0.0'
|
|
48
|
+
app.set :token, @token
|
|
49
|
+
app.set :show_exceptions, false
|
|
50
|
+
app.set :host_authorization, { permitted_hosts: @permitted_hosts }
|
|
51
|
+
|
|
52
|
+
# Enable broad CORS and handle preflight requests.
|
|
53
|
+
app.before do
|
|
54
|
+
headers 'Access-Control-Allow-Origin' => '*',
|
|
55
|
+
'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, OPTIONS',
|
|
56
|
+
'Access-Control-Allow-Headers' => 'Content-Type, Authorization'
|
|
57
|
+
content_type :json
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
app.options '*' do
|
|
61
|
+
halt 200
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# JSON formatted 404 response.
|
|
65
|
+
app.not_found do
|
|
66
|
+
status 404
|
|
67
|
+
{ error: "Endpoint not found", path: request.path_info }.to_json
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# JSON formatted 500 response.
|
|
71
|
+
app.error do
|
|
72
|
+
e = env['sinatra.error']
|
|
73
|
+
status 500
|
|
74
|
+
{ error: "Internal Server Error", details: e.message }.to_json
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
public
|
|
79
|
+
|
|
80
|
+
# Dynamically generate routing methods (get, post, put, delete).
|
|
81
|
+
%w[get post put delete].each do |verb|
|
|
82
|
+
define_method(verb) do |path, options = {}, &block|
|
|
83
|
+
register_route(verb, path, options, &block)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Core routing logic: auth validation, param parsing, and block execution.
|
|
88
|
+
def register_route(verb, path, options = {}, &block)
|
|
89
|
+
verb = verb.to_s.upcase
|
|
90
|
+
require_auth = options.fetch(:auth, true)
|
|
91
|
+
required_params = options.fetch(:requires, [])
|
|
92
|
+
|
|
93
|
+
# Construct the full path with the optional prefix.
|
|
94
|
+
full_path = File.join('/', @prefix.to_s, path.to_s).gsub(%r{/+}, '/')
|
|
95
|
+
|
|
96
|
+
handler = proc do
|
|
97
|
+
|
|
98
|
+
# 1. Authentication check
|
|
99
|
+
if require_auth
|
|
100
|
+
auth_header = request.env["HTTP_AUTHORIZATION"]
|
|
101
|
+
halt 401, { error: "Token required. Format: 'Bearer <token>'" }.to_json if auth_header.nil?
|
|
102
|
+
halt 403, { error: "Invalid token" }.to_json if auth_header.split(" ").last != settings.token
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# 2. Body parsing (for POST/PUT requests)
|
|
106
|
+
parsed_body = {}
|
|
107
|
+
if ['POST', 'PUT'].include?(verb)
|
|
108
|
+
body_data = request.body.read.to_s
|
|
109
|
+
unless body_data.empty?
|
|
110
|
+
begin
|
|
111
|
+
parsed_body = JSON.parse(body_data, symbolize_names: true)
|
|
112
|
+
rescue JSON::ParserError
|
|
113
|
+
halt 400, { error: "Invalid JSON body" }.to_json
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# 3. Merge query parameters with parsed JSON body
|
|
119
|
+
all_params = smart_parse(params).merge(parsed_body)
|
|
120
|
+
|
|
121
|
+
# 4. Declarative parameter validation
|
|
122
|
+
missing = required_params.select { |p| all_params[p.to_sym].nil? || all_params[p.to_sym].to_s.strip.empty? }
|
|
123
|
+
if missing.any?
|
|
124
|
+
status 400
|
|
125
|
+
log_request(verb, full_path, all_params, 400)
|
|
126
|
+
next { error: "Missing required parameters", required: missing }.to_json
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# 5. Execute user-defined block
|
|
130
|
+
result = instance_exec(all_params, &block)
|
|
131
|
+
log_request(verb, full_path, all_params, response.status)
|
|
132
|
+
result.to_json
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
@app_class.send(verb.downcase, full_path, &handler)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Starts the Sinatra server with a custom GR banner.
|
|
139
|
+
def run!
|
|
140
|
+
puts "============================================="
|
|
141
|
+
puts " GR API MANAGER STARTED"
|
|
142
|
+
puts " Port : #{@port}"
|
|
143
|
+
puts " Auth : #{@token ? 'Enabled' : 'Public'}"
|
|
144
|
+
puts " Prefix : #{@prefix.empty? ? '/' : @prefix}"
|
|
145
|
+
puts "============================================="
|
|
146
|
+
@app_class.run!
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: gr_api_manager
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Razo
|
|
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: sinatra
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '3.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '3.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: dotenv
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '2.8'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '2.8'
|
|
40
|
+
description: Eliminates boilerplate from REST API development. Handles Auth, CORS,
|
|
41
|
+
parameter validation, and type casting.
|
|
42
|
+
email:
|
|
43
|
+
- garabatoangelopolis@gmail.com
|
|
44
|
+
executables: []
|
|
45
|
+
extensions: []
|
|
46
|
+
extra_rdoc_files: []
|
|
47
|
+
files:
|
|
48
|
+
- README.md
|
|
49
|
+
- lib/gr_api_manager.rb
|
|
50
|
+
homepage: https://github.com/Gabo-Razo/sinatra-api-manager
|
|
51
|
+
licenses:
|
|
52
|
+
- MIT
|
|
53
|
+
metadata:
|
|
54
|
+
allowed_push_host: https://rubygems.org
|
|
55
|
+
source_code_uri: https://github.com/Gabo-Razo/sinatra-api-manager
|
|
56
|
+
changelog_uri: https://github.com/Gabo-Razo/sinatra-api-manager/blob/main/README.md
|
|
57
|
+
post_install_message: Thanks for installing GR API Manager! Ready to eliminate boilerplate?
|
|
58
|
+
rdoc_options: []
|
|
59
|
+
require_paths:
|
|
60
|
+
- lib
|
|
61
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
62
|
+
requirements:
|
|
63
|
+
- - ">="
|
|
64
|
+
- !ruby/object:Gem::Version
|
|
65
|
+
version: '3.0'
|
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
67
|
+
requirements:
|
|
68
|
+
- - ">="
|
|
69
|
+
- !ruby/object:Gem::Version
|
|
70
|
+
version: '0'
|
|
71
|
+
requirements: []
|
|
72
|
+
rubygems_version: 3.6.7
|
|
73
|
+
specification_version: 4
|
|
74
|
+
summary: A minimal, opinionated Ruby wrapper around Sinatra.
|
|
75
|
+
test_files: []
|