tina4ruby 3.11.13 → 3.11.15
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/CHANGELOG.md +80 -80
- data/LICENSE.txt +21 -21
- data/README.md +137 -137
- data/exe/tina4ruby +5 -5
- data/lib/tina4/ai.rb +696 -696
- data/lib/tina4/api.rb +189 -189
- data/lib/tina4/auth.rb +305 -305
- data/lib/tina4/auto_crud.rb +244 -244
- data/lib/tina4/cache.rb +154 -154
- data/lib/tina4/cli.rb +1449 -1449
- data/lib/tina4/constants.rb +46 -46
- data/lib/tina4/container.rb +74 -74
- data/lib/tina4/cors.rb +74 -74
- data/lib/tina4/crud.rb +692 -692
- data/lib/tina4/database/sqlite3_adapter.rb +165 -165
- data/lib/tina4/database.rb +625 -625
- data/lib/tina4/database_result.rb +208 -208
- data/lib/tina4/debug.rb +8 -8
- data/lib/tina4/dev.rb +14 -14
- data/lib/tina4/dev_admin.rb +935 -935
- data/lib/tina4/dev_mailbox.rb +191 -191
- data/lib/tina4/drivers/firebird_driver.rb +124 -110
- data/lib/tina4/drivers/mongodb_driver.rb +561 -561
- data/lib/tina4/drivers/mssql_driver.rb +112 -112
- data/lib/tina4/drivers/mysql_driver.rb +90 -90
- data/lib/tina4/drivers/odbc_driver.rb +191 -191
- data/lib/tina4/drivers/postgres_driver.rb +116 -106
- data/lib/tina4/drivers/sqlite_driver.rb +122 -122
- data/lib/tina4/env.rb +95 -95
- data/lib/tina4/error_overlay.rb +252 -252
- data/lib/tina4/events.rb +109 -109
- data/lib/tina4/field_types.rb +154 -154
- data/lib/tina4/frond.rb +2025 -2025
- data/lib/tina4/gallery/auth/meta.json +1 -1
- data/lib/tina4/gallery/auth/src/routes/api/gallery_auth.rb +114 -114
- data/lib/tina4/gallery/database/meta.json +1 -1
- data/lib/tina4/gallery/database/src/routes/api/gallery_db.rb +43 -43
- data/lib/tina4/gallery/error-overlay/meta.json +1 -1
- data/lib/tina4/gallery/error-overlay/src/routes/api/gallery_crash.rb +17 -17
- data/lib/tina4/gallery/orm/meta.json +1 -1
- data/lib/tina4/gallery/orm/src/routes/api/gallery_products.rb +16 -16
- data/lib/tina4/gallery/queue/meta.json +1 -1
- data/lib/tina4/gallery/queue/src/routes/api/gallery_queue.rb +325 -325
- data/lib/tina4/gallery/rest-api/meta.json +1 -1
- data/lib/tina4/gallery/rest-api/src/routes/api/gallery_hello.rb +14 -14
- data/lib/tina4/gallery/templates/meta.json +1 -1
- data/lib/tina4/gallery/templates/src/routes/gallery_page.rb +12 -12
- data/lib/tina4/gallery/templates/src/templates/gallery_page.twig +257 -257
- data/lib/tina4/graphql.rb +966 -966
- data/lib/tina4/health.rb +39 -39
- data/lib/tina4/html_element.rb +170 -170
- data/lib/tina4/job.rb +80 -80
- data/lib/tina4/localization.rb +168 -168
- data/lib/tina4/log.rb +203 -203
- data/lib/tina4/mcp.rb +696 -696
- data/lib/tina4/messenger.rb +587 -587
- data/lib/tina4/metrics.rb +793 -793
- data/lib/tina4/middleware.rb +445 -445
- data/lib/tina4/migration.rb +451 -451
- data/lib/tina4/orm.rb +790 -790
- data/lib/tina4/public/css/tina4.css +2463 -2463
- data/lib/tina4/public/css/tina4.min.css +1 -1
- data/lib/tina4/public/images/logo.svg +5 -5
- data/lib/tina4/public/js/frond.min.js +2 -2
- data/lib/tina4/public/js/tina4-dev-admin.js +565 -565
- data/lib/tina4/public/js/tina4-dev-admin.min.js +480 -480
- data/lib/tina4/public/js/tina4.min.js +92 -92
- data/lib/tina4/public/js/tina4js.min.js +48 -48
- data/lib/tina4/public/swagger/index.html +90 -90
- data/lib/tina4/public/swagger/oauth2-redirect.html +63 -63
- data/lib/tina4/query_builder.rb +380 -380
- data/lib/tina4/queue.rb +366 -366
- data/lib/tina4/queue_backends/kafka_backend.rb +80 -80
- data/lib/tina4/queue_backends/lite_backend.rb +298 -298
- data/lib/tina4/queue_backends/mongo_backend.rb +126 -126
- data/lib/tina4/queue_backends/rabbitmq_backend.rb +73 -73
- data/lib/tina4/rack_app.rb +817 -817
- data/lib/tina4/rate_limiter.rb +130 -130
- data/lib/tina4/request.rb +268 -255
- data/lib/tina4/response.rb +346 -346
- data/lib/tina4/response_cache.rb +551 -551
- data/lib/tina4/router.rb +406 -406
- data/lib/tina4/scss/tina4css/_alerts.scss +34 -34
- data/lib/tina4/scss/tina4css/_badges.scss +22 -22
- data/lib/tina4/scss/tina4css/_buttons.scss +69 -69
- data/lib/tina4/scss/tina4css/_cards.scss +49 -49
- data/lib/tina4/scss/tina4css/_forms.scss +156 -156
- data/lib/tina4/scss/tina4css/_grid.scss +81 -81
- data/lib/tina4/scss/tina4css/_modals.scss +84 -84
- data/lib/tina4/scss/tina4css/_nav.scss +149 -149
- data/lib/tina4/scss/tina4css/_reset.scss +94 -94
- data/lib/tina4/scss/tina4css/_tables.scss +54 -54
- data/lib/tina4/scss/tina4css/_typography.scss +55 -55
- data/lib/tina4/scss/tina4css/_utilities.scss +197 -197
- data/lib/tina4/scss/tina4css/_variables.scss +117 -117
- data/lib/tina4/scss/tina4css/base.scss +1 -1
- data/lib/tina4/scss/tina4css/colors.scss +48 -48
- data/lib/tina4/scss/tina4css/tina4.scss +17 -17
- data/lib/tina4/scss_compiler.rb +178 -178
- data/lib/tina4/seeder.rb +567 -567
- data/lib/tina4/service_runner.rb +303 -303
- data/lib/tina4/session.rb +297 -297
- data/lib/tina4/session_handlers/database_handler.rb +72 -72
- data/lib/tina4/session_handlers/file_handler.rb +67 -67
- data/lib/tina4/session_handlers/mongo_handler.rb +49 -49
- data/lib/tina4/session_handlers/redis_handler.rb +43 -43
- data/lib/tina4/session_handlers/valkey_handler.rb +43 -43
- data/lib/tina4/shutdown.rb +84 -84
- data/lib/tina4/sql_translation.rb +158 -158
- data/lib/tina4/swagger.rb +124 -124
- data/lib/tina4/template.rb +894 -894
- data/lib/tina4/templates/base.twig +26 -26
- data/lib/tina4/templates/errors/302.twig +14 -14
- data/lib/tina4/templates/errors/401.twig +9 -9
- data/lib/tina4/templates/errors/403.twig +29 -29
- data/lib/tina4/templates/errors/404.twig +29 -29
- data/lib/tina4/templates/errors/500.twig +38 -38
- data/lib/tina4/templates/errors/502.twig +9 -9
- data/lib/tina4/templates/errors/503.twig +12 -12
- data/lib/tina4/templates/errors/base.twig +37 -37
- data/lib/tina4/test_client.rb +159 -159
- data/lib/tina4/testing.rb +340 -340
- data/lib/tina4/validator.rb +174 -174
- data/lib/tina4/version.rb +1 -1
- data/lib/tina4/webserver.rb +312 -312
- data/lib/tina4/websocket.rb +343 -343
- data/lib/tina4/websocket_backplane.rb +190 -190
- data/lib/tina4/wsdl.rb +564 -564
- data/lib/tina4.rb +458 -458
- data/lib/tina4ruby.rb +4 -4
- metadata +3 -3
data/lib/tina4/validator.rb
CHANGED
|
@@ -1,174 +1,174 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Tina4
|
|
4
|
-
# Request body validator with chainable rules.
|
|
5
|
-
#
|
|
6
|
-
# Usage:
|
|
7
|
-
# validator = Tina4::Validator.new(request.body)
|
|
8
|
-
# validator.required("name", "email")
|
|
9
|
-
# .email("email")
|
|
10
|
-
# .min_length("name", 2)
|
|
11
|
-
# .max_length("name", 100)
|
|
12
|
-
# .integer("age")
|
|
13
|
-
# .min("age", 0)
|
|
14
|
-
# .max("age", 150)
|
|
15
|
-
# .in_list("role", ["admin", "user", "guest"])
|
|
16
|
-
# .regex("phone", /^\+?[\d\s\-]+$/)
|
|
17
|
-
#
|
|
18
|
-
# unless validator.is_valid?
|
|
19
|
-
# return response.error("VALIDATION_FAILED", validator.errors.first[:message], 400)
|
|
20
|
-
# end
|
|
21
|
-
#
|
|
22
|
-
class Validator
|
|
23
|
-
attr_reader :validation_errors
|
|
24
|
-
|
|
25
|
-
EMAIL_REGEX = /\A[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}\z/
|
|
26
|
-
|
|
27
|
-
def initialize(data = {})
|
|
28
|
-
@data = data.is_a?(Hash) ? data : {}
|
|
29
|
-
# Normalise keys to strings for consistent lookup
|
|
30
|
-
@data = @data.transform_keys(&:to_s) unless @data.empty?
|
|
31
|
-
@validation_errors = []
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# Check that one or more fields are present and non-empty.
|
|
35
|
-
def required(*fields)
|
|
36
|
-
fields.each do |field|
|
|
37
|
-
key = field.to_s
|
|
38
|
-
value = @data[key]
|
|
39
|
-
if value.nil? || (value.is_a?(String) && value.strip.empty?)
|
|
40
|
-
@validation_errors << { field: key, message: "#{key} is required" }
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
self
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# Check that a field contains a valid email address.
|
|
47
|
-
def email(field)
|
|
48
|
-
key = field.to_s
|
|
49
|
-
value = @data[key]
|
|
50
|
-
return self if value.nil?
|
|
51
|
-
|
|
52
|
-
unless value.is_a?(String) && value.match?(EMAIL_REGEX)
|
|
53
|
-
@validation_errors << { field: key, message: "#{key} must be a valid email address" }
|
|
54
|
-
end
|
|
55
|
-
self
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
# Check that a string field has at least +length+ characters.
|
|
59
|
-
def min_length(field, length)
|
|
60
|
-
key = field.to_s
|
|
61
|
-
value = @data[key]
|
|
62
|
-
return self if value.nil?
|
|
63
|
-
|
|
64
|
-
unless value.is_a?(String) && value.length >= length
|
|
65
|
-
@validation_errors << { field: key, message: "#{key} must be at least #{length} characters" }
|
|
66
|
-
end
|
|
67
|
-
self
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# Check that a string field has at most +length+ characters.
|
|
71
|
-
def max_length(field, length)
|
|
72
|
-
key = field.to_s
|
|
73
|
-
value = @data[key]
|
|
74
|
-
return self if value.nil?
|
|
75
|
-
|
|
76
|
-
unless value.is_a?(String) && value.length <= length
|
|
77
|
-
@validation_errors << { field: key, message: "#{key} must be at most #{length} characters" }
|
|
78
|
-
end
|
|
79
|
-
self
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# Check that a field is an integer (or can be parsed as one).
|
|
83
|
-
def integer(field)
|
|
84
|
-
key = field.to_s
|
|
85
|
-
value = @data[key]
|
|
86
|
-
return self if value.nil?
|
|
87
|
-
|
|
88
|
-
if value.is_a?(Integer)
|
|
89
|
-
return self
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
begin
|
|
93
|
-
Integer(value)
|
|
94
|
-
rescue ArgumentError, TypeError
|
|
95
|
-
@validation_errors << { field: key, message: "#{key} must be an integer" }
|
|
96
|
-
end
|
|
97
|
-
self
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
# Check that a numeric field is >= +minimum+.
|
|
101
|
-
def min(field, minimum)
|
|
102
|
-
key = field.to_s
|
|
103
|
-
value = @data[key]
|
|
104
|
-
return self if value.nil?
|
|
105
|
-
|
|
106
|
-
begin
|
|
107
|
-
num = Float(value)
|
|
108
|
-
rescue ArgumentError, TypeError
|
|
109
|
-
return self
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
if num < minimum
|
|
113
|
-
@validation_errors << { field: key, message: "#{key} must be at least #{minimum}" }
|
|
114
|
-
end
|
|
115
|
-
self
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
# Check that a numeric field is <= +maximum+.
|
|
119
|
-
def max(field, maximum)
|
|
120
|
-
key = field.to_s
|
|
121
|
-
value = @data[key]
|
|
122
|
-
return self if value.nil?
|
|
123
|
-
|
|
124
|
-
begin
|
|
125
|
-
num = Float(value)
|
|
126
|
-
rescue ArgumentError, TypeError
|
|
127
|
-
return self
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
if num > maximum
|
|
131
|
-
@validation_errors << { field: key, message: "#{key} must be at most #{maximum}" }
|
|
132
|
-
end
|
|
133
|
-
self
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
# Check that a field's value is one of the allowed values.
|
|
137
|
-
def in_list(field, allowed)
|
|
138
|
-
key = field.to_s
|
|
139
|
-
value = @data[key]
|
|
140
|
-
return self if value.nil?
|
|
141
|
-
|
|
142
|
-
unless allowed.include?(value)
|
|
143
|
-
@validation_errors << { field: key, message: "#{key} must be one of #{allowed}" }
|
|
144
|
-
end
|
|
145
|
-
self
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
# Check that a field matches a regular expression.
|
|
149
|
-
def regex(field, pattern)
|
|
150
|
-
key = field.to_s
|
|
151
|
-
value = @data[key]
|
|
152
|
-
return self if value.nil?
|
|
153
|
-
|
|
154
|
-
regexp = pattern.is_a?(Regexp) ? pattern : Regexp.new(pattern)
|
|
155
|
-
unless value.is_a?(String) && value.match?(regexp)
|
|
156
|
-
@validation_errors << { field: key, message: "#{key} does not match the required format" }
|
|
157
|
-
end
|
|
158
|
-
self
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
# Return the list of validation errors (empty if valid).
|
|
162
|
-
def errors
|
|
163
|
-
@validation_errors.dup
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
# Return true if no validation errors have been recorded.
|
|
167
|
-
def is_valid?
|
|
168
|
-
@validation_errors.empty?
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
# Alias for is_valid? (Ruby convention)
|
|
172
|
-
alias_method :valid?, :is_valid?
|
|
173
|
-
end
|
|
174
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Tina4
|
|
4
|
+
# Request body validator with chainable rules.
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# validator = Tina4::Validator.new(request.body)
|
|
8
|
+
# validator.required("name", "email")
|
|
9
|
+
# .email("email")
|
|
10
|
+
# .min_length("name", 2)
|
|
11
|
+
# .max_length("name", 100)
|
|
12
|
+
# .integer("age")
|
|
13
|
+
# .min("age", 0)
|
|
14
|
+
# .max("age", 150)
|
|
15
|
+
# .in_list("role", ["admin", "user", "guest"])
|
|
16
|
+
# .regex("phone", /^\+?[\d\s\-]+$/)
|
|
17
|
+
#
|
|
18
|
+
# unless validator.is_valid?
|
|
19
|
+
# return response.error("VALIDATION_FAILED", validator.errors.first[:message], 400)
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
class Validator
|
|
23
|
+
attr_reader :validation_errors
|
|
24
|
+
|
|
25
|
+
EMAIL_REGEX = /\A[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}\z/
|
|
26
|
+
|
|
27
|
+
def initialize(data = {})
|
|
28
|
+
@data = data.is_a?(Hash) ? data : {}
|
|
29
|
+
# Normalise keys to strings for consistent lookup
|
|
30
|
+
@data = @data.transform_keys(&:to_s) unless @data.empty?
|
|
31
|
+
@validation_errors = []
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Check that one or more fields are present and non-empty.
|
|
35
|
+
def required(*fields)
|
|
36
|
+
fields.each do |field|
|
|
37
|
+
key = field.to_s
|
|
38
|
+
value = @data[key]
|
|
39
|
+
if value.nil? || (value.is_a?(String) && value.strip.empty?)
|
|
40
|
+
@validation_errors << { field: key, message: "#{key} is required" }
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
self
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Check that a field contains a valid email address.
|
|
47
|
+
def email(field)
|
|
48
|
+
key = field.to_s
|
|
49
|
+
value = @data[key]
|
|
50
|
+
return self if value.nil?
|
|
51
|
+
|
|
52
|
+
unless value.is_a?(String) && value.match?(EMAIL_REGEX)
|
|
53
|
+
@validation_errors << { field: key, message: "#{key} must be a valid email address" }
|
|
54
|
+
end
|
|
55
|
+
self
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Check that a string field has at least +length+ characters.
|
|
59
|
+
def min_length(field, length)
|
|
60
|
+
key = field.to_s
|
|
61
|
+
value = @data[key]
|
|
62
|
+
return self if value.nil?
|
|
63
|
+
|
|
64
|
+
unless value.is_a?(String) && value.length >= length
|
|
65
|
+
@validation_errors << { field: key, message: "#{key} must be at least #{length} characters" }
|
|
66
|
+
end
|
|
67
|
+
self
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Check that a string field has at most +length+ characters.
|
|
71
|
+
def max_length(field, length)
|
|
72
|
+
key = field.to_s
|
|
73
|
+
value = @data[key]
|
|
74
|
+
return self if value.nil?
|
|
75
|
+
|
|
76
|
+
unless value.is_a?(String) && value.length <= length
|
|
77
|
+
@validation_errors << { field: key, message: "#{key} must be at most #{length} characters" }
|
|
78
|
+
end
|
|
79
|
+
self
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Check that a field is an integer (or can be parsed as one).
|
|
83
|
+
def integer(field)
|
|
84
|
+
key = field.to_s
|
|
85
|
+
value = @data[key]
|
|
86
|
+
return self if value.nil?
|
|
87
|
+
|
|
88
|
+
if value.is_a?(Integer)
|
|
89
|
+
return self
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
begin
|
|
93
|
+
Integer(value)
|
|
94
|
+
rescue ArgumentError, TypeError
|
|
95
|
+
@validation_errors << { field: key, message: "#{key} must be an integer" }
|
|
96
|
+
end
|
|
97
|
+
self
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Check that a numeric field is >= +minimum+.
|
|
101
|
+
def min(field, minimum)
|
|
102
|
+
key = field.to_s
|
|
103
|
+
value = @data[key]
|
|
104
|
+
return self if value.nil?
|
|
105
|
+
|
|
106
|
+
begin
|
|
107
|
+
num = Float(value)
|
|
108
|
+
rescue ArgumentError, TypeError
|
|
109
|
+
return self
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
if num < minimum
|
|
113
|
+
@validation_errors << { field: key, message: "#{key} must be at least #{minimum}" }
|
|
114
|
+
end
|
|
115
|
+
self
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Check that a numeric field is <= +maximum+.
|
|
119
|
+
def max(field, maximum)
|
|
120
|
+
key = field.to_s
|
|
121
|
+
value = @data[key]
|
|
122
|
+
return self if value.nil?
|
|
123
|
+
|
|
124
|
+
begin
|
|
125
|
+
num = Float(value)
|
|
126
|
+
rescue ArgumentError, TypeError
|
|
127
|
+
return self
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
if num > maximum
|
|
131
|
+
@validation_errors << { field: key, message: "#{key} must be at most #{maximum}" }
|
|
132
|
+
end
|
|
133
|
+
self
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Check that a field's value is one of the allowed values.
|
|
137
|
+
def in_list(field, allowed)
|
|
138
|
+
key = field.to_s
|
|
139
|
+
value = @data[key]
|
|
140
|
+
return self if value.nil?
|
|
141
|
+
|
|
142
|
+
unless allowed.include?(value)
|
|
143
|
+
@validation_errors << { field: key, message: "#{key} must be one of #{allowed}" }
|
|
144
|
+
end
|
|
145
|
+
self
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Check that a field matches a regular expression.
|
|
149
|
+
def regex(field, pattern)
|
|
150
|
+
key = field.to_s
|
|
151
|
+
value = @data[key]
|
|
152
|
+
return self if value.nil?
|
|
153
|
+
|
|
154
|
+
regexp = pattern.is_a?(Regexp) ? pattern : Regexp.new(pattern)
|
|
155
|
+
unless value.is_a?(String) && value.match?(regexp)
|
|
156
|
+
@validation_errors << { field: key, message: "#{key} does not match the required format" }
|
|
157
|
+
end
|
|
158
|
+
self
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Return the list of validation errors (empty if valid).
|
|
162
|
+
def errors
|
|
163
|
+
@validation_errors.dup
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Return true if no validation errors have been recorded.
|
|
167
|
+
def is_valid?
|
|
168
|
+
@validation_errors.empty?
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Alias for is_valid? (Ruby convention)
|
|
172
|
+
alias_method :valid?, :is_valid?
|
|
173
|
+
end
|
|
174
|
+
end
|
data/lib/tina4/version.rb
CHANGED