stipa 0.2.3 → 0.2.5
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 +4 -4
- data/lib/stipa/generators/api.rb +56 -7
- data/lib/stipa/generators/base.rb +36 -10
- data/lib/stipa/generators/vue.rb +100 -46
- data/lib/stipa/model/uuid.rb +5 -2
- data/lib/stipa/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c13ca91809cf6a4b578b9336b9588f4f544055dab765021a54c822f55281291d
|
|
4
|
+
data.tar.gz: ac20150e5e8528532aebe21cf8f1b6354101cf6a92deeb1fe760cd813ef28841
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 84e7a0fd600288d87b0ae3c69510028d39d89f4ce2da788624682b39a608b10b2c43fb071cfbbc59f95199c35376cb9acbf32e13d1cf12651fb40d2c39a0cbe0
|
|
7
|
+
data.tar.gz: 2097c4e81b7fc64049a4bb073c7f0ddb704c2e0916d2f0254214cfffbe904ad16b10b40ddaa1ecc0d16ed5a02b9cf8576b6fc6da511e96894c2ef2cc87b02b74
|
data/lib/stipa/generators/api.rb
CHANGED
|
@@ -35,6 +35,7 @@ module Stipa
|
|
|
35
35
|
'controllers/application_controller.rb' => t_application_controller,
|
|
36
36
|
'controllers/health_controller.rb' => t_health_controller,
|
|
37
37
|
'models/application_model.rb' => t_application_model,
|
|
38
|
+
'models/post.rb' => t_sample_model,
|
|
38
39
|
'db/migrate/001_create_posts.rb' => t_migration_create_posts,
|
|
39
40
|
}
|
|
40
41
|
end
|
|
@@ -92,20 +93,68 @@ module Stipa
|
|
|
92
93
|
|
|
93
94
|
private
|
|
94
95
|
|
|
96
|
+
# ── Success helpers ──────────────────────────────
|
|
97
|
+
|
|
95
98
|
def json(data, status: 200)
|
|
96
99
|
res.status = status
|
|
97
100
|
res.json(data)
|
|
98
101
|
end
|
|
99
102
|
|
|
100
|
-
def
|
|
101
|
-
res.status =
|
|
102
|
-
res.json(
|
|
103
|
-
|
|
103
|
+
def created!(data = nil)
|
|
104
|
+
res.status = 201
|
|
105
|
+
data ? res.json(data) : res.tap { _1.body = '' }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def no_content!
|
|
109
|
+
res.status = 204
|
|
110
|
+
res.body = ''
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# ── Redirection ──────────────────────────────────
|
|
114
|
+
|
|
115
|
+
def redirect_to(path, status: 302)
|
|
116
|
+
res.status = status
|
|
117
|
+
res['Location'] = path
|
|
118
|
+
res.body = ''
|
|
104
119
|
end
|
|
105
120
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
121
|
+
# ── Error helpers ────────────────────────────────
|
|
122
|
+
|
|
123
|
+
def bad_request!(message = 'Bad Request')
|
|
124
|
+
error(400, message)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def unauthorized!(message = 'Unauthorized')
|
|
128
|
+
error(401, message)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def forbidden!(message = 'Forbidden')
|
|
132
|
+
error(403, message)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def not_found!(message = 'Not Found')
|
|
136
|
+
error(404, message)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def conflict!(message = 'Conflict')
|
|
140
|
+
error(409, message)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def unprocessable_entity!(message = 'Unprocessable Entity')
|
|
144
|
+
error(422, message)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def too_many_requests!(message = 'Too Many Requests')
|
|
148
|
+
error(429, message)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def internal_server_error!(message = 'Internal Server Error')
|
|
152
|
+
error(500, message)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def error(status, message)
|
|
156
|
+
res.status = status
|
|
157
|
+
res.json(error: { message: message })
|
|
109
158
|
throw :halt
|
|
110
159
|
end
|
|
111
160
|
|
|
@@ -5,7 +5,7 @@ require 'pathname'
|
|
|
5
5
|
module Stipa
|
|
6
6
|
module Generators
|
|
7
7
|
class Base
|
|
8
|
-
GEM_JS
|
|
8
|
+
GEM_JS = File.expand_path('../../js', __dir__)
|
|
9
9
|
GEM_MEDIA = File.expand_path('../../../media', __dir__)
|
|
10
10
|
GEM_ROOT = File.expand_path('../..', GEM_JS)
|
|
11
11
|
|
|
@@ -51,7 +51,7 @@ module Stipa
|
|
|
51
51
|
system('git', 'commit', '-q', '-m', 'Initial commit')
|
|
52
52
|
end
|
|
53
53
|
say ' git initialized repository with initial commit'
|
|
54
|
-
rescue => e
|
|
54
|
+
rescue StandardError => e
|
|
55
55
|
say " warn git init failed: #{e.message}"
|
|
56
56
|
end
|
|
57
57
|
|
|
@@ -150,7 +150,7 @@ module Stipa
|
|
|
150
150
|
%w[get post put patch delete].each do |verb|
|
|
151
151
|
define_method(verb) do |pattern, to:|
|
|
152
152
|
klass, action = resolve(to)
|
|
153
|
-
@app.public_send(verb, pattern) { |req, res| klass.new(req, res).public_send(action) }
|
|
153
|
+
@app.public_send(verb, pattern) { |req, res| catch(:halt) { klass.new(req, res).public_send(action) } }
|
|
154
154
|
end
|
|
155
155
|
end
|
|
156
156
|
end
|
|
@@ -188,19 +188,45 @@ module Stipa
|
|
|
188
188
|
<<~RUBY
|
|
189
189
|
# frozen_string_literal: true
|
|
190
190
|
|
|
191
|
+
Sequel::Model.require_valid_table = false
|
|
192
|
+
|
|
191
193
|
require 'stipa/model'
|
|
192
194
|
|
|
193
|
-
#
|
|
194
|
-
#
|
|
195
|
+
# Base model class providing timestamps, UUID, soft delete,
|
|
196
|
+
# serialization, dirty tracking, and validation helpers.
|
|
195
197
|
#
|
|
196
198
|
# Usage:
|
|
197
|
-
# class
|
|
198
|
-
# include ApplicationModel
|
|
199
|
+
# class Car < ApplicationModel
|
|
199
200
|
# end
|
|
200
201
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
202
|
+
class ApplicationModel < Sequel::Model
|
|
203
|
+
include Stipa::Model
|
|
204
|
+
|
|
205
|
+
# Ensure every subclass maps to its own table (e.g. Car → cars),
|
|
206
|
+
# not to the parent's abstract +application_models+ table.
|
|
207
|
+
def self.inherited(subclass)
|
|
208
|
+
super
|
|
209
|
+
subclass.set_dataset(subclass.implicit_table_name) unless subclass.name.to_s.empty?
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
Sequel::Model.require_valid_table = true
|
|
214
|
+
RUBY
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def t_sample_model
|
|
218
|
+
<<~RUBY
|
|
219
|
+
# frozen_string_literal: true
|
|
220
|
+
|
|
221
|
+
class Post < ApplicationModel
|
|
222
|
+
def validate
|
|
223
|
+
super
|
|
224
|
+
errors.add(:title, 'is required') if title.to_s.strip.empty?
|
|
225
|
+
errors.add(:slug, 'is required') if slug.to_s.strip.empty?
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def to_param
|
|
229
|
+
slug
|
|
204
230
|
end
|
|
205
231
|
end
|
|
206
232
|
RUBY
|
data/lib/stipa/generators/vue.rb
CHANGED
|
@@ -21,7 +21,7 @@ module Stipa
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def post_generate
|
|
24
|
-
copy_asset 'stipa-vue.js',
|
|
24
|
+
copy_asset 'stipa-vue.js', from: GEM_JS
|
|
25
25
|
copy_asset 'logo.png', from: GEM_MEDIA
|
|
26
26
|
copy_asset 'favicon.ico', from: GEM_MEDIA
|
|
27
27
|
end
|
|
@@ -54,30 +54,31 @@ module Stipa
|
|
|
54
54
|
|
|
55
55
|
def files
|
|
56
56
|
{
|
|
57
|
-
'.gitignore'
|
|
58
|
-
'Gemfile'
|
|
59
|
-
'Rakefile'
|
|
60
|
-
'package.json'
|
|
61
|
-
'rollup.config.js'
|
|
62
|
-
'tsconfig.json'
|
|
63
|
-
'server.rb'
|
|
64
|
-
'app/config/database.rb'
|
|
65
|
-
'app/config/routes.rb'
|
|
57
|
+
'.gitignore' => t_gitignore,
|
|
58
|
+
'Gemfile' => t_gemfile,
|
|
59
|
+
'Rakefile' => t_rakefile(config_path: 'app/config'),
|
|
60
|
+
'package.json' => t_package_json,
|
|
61
|
+
'rollup.config.js' => t_rollup_config,
|
|
62
|
+
'tsconfig.json' => t_tsconfig,
|
|
63
|
+
'server.rb' => t_server,
|
|
64
|
+
'app/config/database.rb' => t_database_config,
|
|
65
|
+
'app/config/routes.rb' => t_routes(
|
|
66
66
|
extra_requires: ['../controllers/home_controller', '../controllers/health_controller'],
|
|
67
|
-
extra_routes:
|
|
68
|
-
method_override: true
|
|
67
|
+
extra_routes: ["get '/', to: 'home#index'", "get '/api/health', to: 'health#show'"],
|
|
68
|
+
method_override: true
|
|
69
69
|
),
|
|
70
|
-
'app/controllers/application_controller.rb'
|
|
71
|
-
'app/controllers/home_controller.rb'
|
|
72
|
-
'app/controllers/health_controller.rb'
|
|
73
|
-
'app/models/application_model.rb'
|
|
74
|
-
'
|
|
75
|
-
'
|
|
76
|
-
'app/views/
|
|
77
|
-
'
|
|
78
|
-
'app
|
|
79
|
-
'app/
|
|
80
|
-
'app/
|
|
70
|
+
'app/controllers/application_controller.rb' => t_application_controller,
|
|
71
|
+
'app/controllers/home_controller.rb' => t_home_controller,
|
|
72
|
+
'app/controllers/health_controller.rb' => t_health_controller,
|
|
73
|
+
'app/models/application_model.rb' => t_application_model,
|
|
74
|
+
'app/models/post.rb' => t_sample_model,
|
|
75
|
+
'db/migrate/001_create_posts.rb' => t_migration_create_posts,
|
|
76
|
+
'app/views/layouts/application.html.erb' => t_layout,
|
|
77
|
+
'app/views/home/index.html.erb' => t_home_index,
|
|
78
|
+
'public/app.css' => t_app_css,
|
|
79
|
+
'app/components/RequestCard.vue' => t_request_card_vue,
|
|
80
|
+
'app/main.ts' => t_main_ts,
|
|
81
|
+
'app/shims-vue.d.ts' => t_shims_vue
|
|
81
82
|
}
|
|
82
83
|
end
|
|
83
84
|
|
|
@@ -101,22 +102,22 @@ module Stipa
|
|
|
101
102
|
private: true,
|
|
102
103
|
type: 'module',
|
|
103
104
|
scripts: {
|
|
104
|
-
'copy:vue'
|
|
105
|
-
build:
|
|
106
|
-
watch:
|
|
107
|
-
dev:
|
|
108
|
-
typecheck:
|
|
105
|
+
'copy:vue' => 'cp node_modules/vue/dist/vue.esm-browser.prod.js public/vendor/vue.esm-browser.prod.js',
|
|
106
|
+
build: 'npm run copy:vue && rollup -c',
|
|
107
|
+
watch: 'rollup -c --watch',
|
|
108
|
+
dev: 'npm run copy:vue && concurrently "STIPA_RELOAD=1 bundle exec ruby server.rb" "rollup -c --watch"',
|
|
109
|
+
typecheck: 'vue-tsc --noEmit'
|
|
109
110
|
},
|
|
110
111
|
devDependencies: {
|
|
111
|
-
'concurrently'
|
|
112
|
-
'rollup'
|
|
113
|
-
'rollup-plugin-vue'
|
|
112
|
+
'concurrently' => '^8.0.0',
|
|
113
|
+
'rollup' => '^4.0.0',
|
|
114
|
+
'rollup-plugin-vue' => '^6.0.0',
|
|
114
115
|
'@rollup/plugin-typescript' => '^11.0.0',
|
|
115
|
-
'@vue/compiler-sfc'
|
|
116
|
-
'typescript'
|
|
117
|
-
'vue'
|
|
118
|
-
'vue-tsc'
|
|
119
|
-
}
|
|
116
|
+
'@vue/compiler-sfc' => '^3.4.0',
|
|
117
|
+
'typescript' => '^5.0.0',
|
|
118
|
+
'vue' => '^3.4.0',
|
|
119
|
+
'vue-tsc' => '^2.0.0'
|
|
120
|
+
}
|
|
120
121
|
) + "\n"
|
|
121
122
|
end
|
|
122
123
|
|
|
@@ -142,14 +143,14 @@ module Stipa
|
|
|
142
143
|
def t_tsconfig
|
|
143
144
|
JSON.pretty_generate(
|
|
144
145
|
compilerOptions: {
|
|
145
|
-
target:
|
|
146
|
-
module:
|
|
146
|
+
target: 'ESNext',
|
|
147
|
+
module: 'ESNext',
|
|
147
148
|
moduleResolution: 'bundler',
|
|
148
|
-
strict:
|
|
149
|
-
skipLibCheck:
|
|
150
|
-
allowJs:
|
|
149
|
+
strict: true,
|
|
150
|
+
skipLibCheck: true,
|
|
151
|
+
allowJs: true
|
|
151
152
|
},
|
|
152
|
-
include: ['app/**/*']
|
|
153
|
+
include: ['app/**/*']
|
|
153
154
|
) + "\n"
|
|
154
155
|
end
|
|
155
156
|
|
|
@@ -209,15 +210,68 @@ module Stipa
|
|
|
209
210
|
res.render(template, locals: locals, layout: layout)
|
|
210
211
|
end
|
|
211
212
|
|
|
213
|
+
# ── Success helpers ──────────────────────────────
|
|
214
|
+
|
|
215
|
+
def json(data, status: 200)
|
|
216
|
+
res.status = status
|
|
217
|
+
res.json(data)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def created!(data = nil)
|
|
221
|
+
res.status = 201
|
|
222
|
+
data ? res.json(data) : res.tap { _1.body = '' }
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def no_content!
|
|
226
|
+
res.status = 204
|
|
227
|
+
res.body = ''
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# ── Redirection ──────────────────────────────────
|
|
231
|
+
|
|
212
232
|
def redirect_to(path, status: 302)
|
|
213
233
|
res.status = status
|
|
214
234
|
res['Location'] = path
|
|
215
235
|
res.body = ''
|
|
216
236
|
end
|
|
217
237
|
|
|
238
|
+
# ── Error helpers ────────────────────────────────
|
|
239
|
+
|
|
240
|
+
def bad_request!(message = 'Bad Request')
|
|
241
|
+
error(400, message)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def unauthorized!(message = 'Unauthorized')
|
|
245
|
+
error(401, message)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def forbidden!(message = 'Forbidden')
|
|
249
|
+
error(403, message)
|
|
250
|
+
end
|
|
251
|
+
|
|
218
252
|
def not_found!(message = 'Not Found')
|
|
219
|
-
|
|
220
|
-
|
|
253
|
+
error(404, message)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def conflict!(message = 'Conflict')
|
|
257
|
+
error(409, message)
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def unprocessable_entity!(message = 'Unprocessable Entity')
|
|
261
|
+
error(422, message)
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def too_many_requests!(message = 'Too Many Requests')
|
|
265
|
+
error(429, message)
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def internal_server_error!(message = 'Internal Server Error')
|
|
269
|
+
error(500, message)
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def error(status, message)
|
|
273
|
+
res.status = status
|
|
274
|
+
res.json(error: { message: message })
|
|
221
275
|
throw :halt
|
|
222
276
|
end
|
|
223
277
|
|
|
@@ -237,6 +291,8 @@ module Stipa
|
|
|
237
291
|
k, v = pair.split('=', 2)
|
|
238
292
|
p[k.to_sym] = URI.decode_www_form_component(v.to_s)
|
|
239
293
|
end
|
|
294
|
+
elsif req['content-type']&.include?('application/json')
|
|
295
|
+
JSON.parse(req.body).each { |k, v| p[k.to_sym] = v }
|
|
240
296
|
end
|
|
241
297
|
|
|
242
298
|
p
|
|
@@ -423,8 +479,6 @@ module Stipa
|
|
|
423
479
|
CSS
|
|
424
480
|
end
|
|
425
481
|
|
|
426
|
-
|
|
427
|
-
|
|
428
482
|
def t_shims_vue
|
|
429
483
|
<<~TS
|
|
430
484
|
declare module '*.vue' {
|
data/lib/stipa/model/uuid.rb
CHANGED
data/lib/stipa/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: stipa
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jānis Harbs
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-07-
|
|
11
|
+
date: 2026-07-30 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: minitest
|