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/localization.rb
CHANGED
|
@@ -1,168 +1,168 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require "json"
|
|
3
|
-
|
|
4
|
-
module Tina4
|
|
5
|
-
module Localization
|
|
6
|
-
LOCALE_DIRS = %w[locales translations i18n src/locales].freeze
|
|
7
|
-
|
|
8
|
-
class << self
|
|
9
|
-
def translations
|
|
10
|
-
@translations ||= {}
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
# Flat alias map: { locale => { leaf_key => value } }
|
|
14
|
-
# First-wins on conflict — later duplicates are ignored.
|
|
15
|
-
def flat_aliases
|
|
16
|
-
@flat_aliases ||= {}
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def current_locale
|
|
20
|
-
@current_locale || ENV["TINA4_LOCALE"] || "en"
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def current_locale=(locale)
|
|
24
|
-
@current_locale = locale.to_s
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def load(root_dir = Dir.pwd)
|
|
28
|
-
locale_dir_override = ENV["TINA4_LOCALE_DIR"]
|
|
29
|
-
search_dirs = locale_dir_override && !locale_dir_override.empty? ? [locale_dir_override] : LOCALE_DIRS
|
|
30
|
-
|
|
31
|
-
search_dirs.each do |dir|
|
|
32
|
-
locale_dir = File.expand_path(dir, root_dir)
|
|
33
|
-
next unless Dir.exist?(locale_dir)
|
|
34
|
-
|
|
35
|
-
Dir.glob(File.join(locale_dir, "*.json")).each do |file|
|
|
36
|
-
locale = File.basename(file, ".json")
|
|
37
|
-
data = JSON.parse(File.read(file))
|
|
38
|
-
translations[locale] ||= {}
|
|
39
|
-
translations[locale].merge!(data)
|
|
40
|
-
# Build leaf-key aliases from the loaded data
|
|
41
|
-
build_leaf_aliases(locale, data)
|
|
42
|
-
Tina4::Log.debug("Loaded locale: #{locale} from #{file}")
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# Also support YAML
|
|
46
|
-
Dir.glob(File.join(locale_dir, "*.{yml,yaml}")).each do |file|
|
|
47
|
-
begin
|
|
48
|
-
require "yaml"
|
|
49
|
-
locale = File.basename(file, File.extname(file))
|
|
50
|
-
data = YAML.safe_load(File.read(file))
|
|
51
|
-
if data.is_a?(Hash)
|
|
52
|
-
translations[locale] ||= {}
|
|
53
|
-
translations[locale].merge!(data)
|
|
54
|
-
build_leaf_aliases(locale, data)
|
|
55
|
-
end
|
|
56
|
-
rescue LoadError
|
|
57
|
-
Tina4::Log.warning("YAML support requires the 'yaml' gem")
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def t(key, locale: nil, default: nil, **interpolations)
|
|
64
|
-
lang = locale || current_locale
|
|
65
|
-
value = lookup(lang, key)
|
|
66
|
-
|
|
67
|
-
if value.nil? && lang != "en"
|
|
68
|
-
value = lookup("en", key)
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
value = default || key if value.nil?
|
|
72
|
-
|
|
73
|
-
# Interpolation: "Hello %{name}" => "Hello World"
|
|
74
|
-
interpolations.each do |k, v|
|
|
75
|
-
value = value.gsub("%{#{k}}", v.to_s)
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
value
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def set_locale(locale)
|
|
82
|
-
self.current_locale = locale.to_s
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def get_locale
|
|
86
|
-
current_locale
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def translate(key, params: nil, locale: nil)
|
|
90
|
-
t(key, locale: locale, **(params || {}))
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def load_translations(locale)
|
|
94
|
-
load(Dir.pwd) if translations.empty?
|
|
95
|
-
translations[locale.to_s] || {}
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def add_translation(locale, key, value)
|
|
99
|
-
add(locale, key, value)
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def add(locale, key, value)
|
|
103
|
-
translations[locale.to_s] ||= {}
|
|
104
|
-
keys = key.to_s.split(".")
|
|
105
|
-
hash = translations[locale.to_s]
|
|
106
|
-
keys[0..-2].each do |k|
|
|
107
|
-
hash[k] ||= {}
|
|
108
|
-
hash = hash[k]
|
|
109
|
-
end
|
|
110
|
-
hash[keys.last] = value
|
|
111
|
-
|
|
112
|
-
# Register leaf-key alias (first-wins)
|
|
113
|
-
leaf = keys.last
|
|
114
|
-
if value.is_a?(String)
|
|
115
|
-
flat_aliases[locale.to_s] ||= {}
|
|
116
|
-
flat_aliases[locale.to_s][leaf] ||= value
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
def available_locales
|
|
121
|
-
translations.keys
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
private
|
|
125
|
-
|
|
126
|
-
# Recursively walk a nested hash and register leaf-key aliases.
|
|
127
|
-
# First-wins: if a leaf key already exists, it is NOT overwritten.
|
|
128
|
-
def build_leaf_aliases(locale, hash, prefix = nil)
|
|
129
|
-
flat_aliases[locale.to_s] ||= {}
|
|
130
|
-
hash.each do |key, value|
|
|
131
|
-
full_key = prefix ? "#{prefix}.#{key}" : key.to_s
|
|
132
|
-
if value.is_a?(Hash)
|
|
133
|
-
build_leaf_aliases(locale, value, full_key)
|
|
134
|
-
else
|
|
135
|
-
# Store the leaf key as an alias (first-wins)
|
|
136
|
-
flat_aliases[locale.to_s][key.to_s] ||= value
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
def lookup(locale, key)
|
|
142
|
-
keys = key.to_s.split(".")
|
|
143
|
-
result = translations[locale]
|
|
144
|
-
return nil unless result
|
|
145
|
-
|
|
146
|
-
# Try dot-path traversal first
|
|
147
|
-
dot_result = result
|
|
148
|
-
keys.each do |k|
|
|
149
|
-
if dot_result.is_a?(Hash)
|
|
150
|
-
dot_result = dot_result[k] || dot_result[k.to_sym]
|
|
151
|
-
else
|
|
152
|
-
dot_result = nil
|
|
153
|
-
break
|
|
154
|
-
end
|
|
155
|
-
end
|
|
156
|
-
return dot_result if dot_result.is_a?(String)
|
|
157
|
-
|
|
158
|
-
# Fall back to leaf-key alias (only for simple keys without dots)
|
|
159
|
-
if flat_aliases[locale]
|
|
160
|
-
alias_val = flat_aliases[locale][key.to_s]
|
|
161
|
-
return alias_val if alias_val.is_a?(String)
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
nil
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
end
|
|
168
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "json"
|
|
3
|
+
|
|
4
|
+
module Tina4
|
|
5
|
+
module Localization
|
|
6
|
+
LOCALE_DIRS = %w[locales translations i18n src/locales].freeze
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def translations
|
|
10
|
+
@translations ||= {}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Flat alias map: { locale => { leaf_key => value } }
|
|
14
|
+
# First-wins on conflict — later duplicates are ignored.
|
|
15
|
+
def flat_aliases
|
|
16
|
+
@flat_aliases ||= {}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def current_locale
|
|
20
|
+
@current_locale || ENV["TINA4_LOCALE"] || "en"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def current_locale=(locale)
|
|
24
|
+
@current_locale = locale.to_s
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def load(root_dir = Dir.pwd)
|
|
28
|
+
locale_dir_override = ENV["TINA4_LOCALE_DIR"]
|
|
29
|
+
search_dirs = locale_dir_override && !locale_dir_override.empty? ? [locale_dir_override] : LOCALE_DIRS
|
|
30
|
+
|
|
31
|
+
search_dirs.each do |dir|
|
|
32
|
+
locale_dir = File.expand_path(dir, root_dir)
|
|
33
|
+
next unless Dir.exist?(locale_dir)
|
|
34
|
+
|
|
35
|
+
Dir.glob(File.join(locale_dir, "*.json")).each do |file|
|
|
36
|
+
locale = File.basename(file, ".json")
|
|
37
|
+
data = JSON.parse(File.read(file))
|
|
38
|
+
translations[locale] ||= {}
|
|
39
|
+
translations[locale].merge!(data)
|
|
40
|
+
# Build leaf-key aliases from the loaded data
|
|
41
|
+
build_leaf_aliases(locale, data)
|
|
42
|
+
Tina4::Log.debug("Loaded locale: #{locale} from #{file}")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Also support YAML
|
|
46
|
+
Dir.glob(File.join(locale_dir, "*.{yml,yaml}")).each do |file|
|
|
47
|
+
begin
|
|
48
|
+
require "yaml"
|
|
49
|
+
locale = File.basename(file, File.extname(file))
|
|
50
|
+
data = YAML.safe_load(File.read(file))
|
|
51
|
+
if data.is_a?(Hash)
|
|
52
|
+
translations[locale] ||= {}
|
|
53
|
+
translations[locale].merge!(data)
|
|
54
|
+
build_leaf_aliases(locale, data)
|
|
55
|
+
end
|
|
56
|
+
rescue LoadError
|
|
57
|
+
Tina4::Log.warning("YAML support requires the 'yaml' gem")
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def t(key, locale: nil, default: nil, **interpolations)
|
|
64
|
+
lang = locale || current_locale
|
|
65
|
+
value = lookup(lang, key)
|
|
66
|
+
|
|
67
|
+
if value.nil? && lang != "en"
|
|
68
|
+
value = lookup("en", key)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
value = default || key if value.nil?
|
|
72
|
+
|
|
73
|
+
# Interpolation: "Hello %{name}" => "Hello World"
|
|
74
|
+
interpolations.each do |k, v|
|
|
75
|
+
value = value.gsub("%{#{k}}", v.to_s)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
value
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def set_locale(locale)
|
|
82
|
+
self.current_locale = locale.to_s
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def get_locale
|
|
86
|
+
current_locale
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def translate(key, params: nil, locale: nil)
|
|
90
|
+
t(key, locale: locale, **(params || {}))
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def load_translations(locale)
|
|
94
|
+
load(Dir.pwd) if translations.empty?
|
|
95
|
+
translations[locale.to_s] || {}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def add_translation(locale, key, value)
|
|
99
|
+
add(locale, key, value)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def add(locale, key, value)
|
|
103
|
+
translations[locale.to_s] ||= {}
|
|
104
|
+
keys = key.to_s.split(".")
|
|
105
|
+
hash = translations[locale.to_s]
|
|
106
|
+
keys[0..-2].each do |k|
|
|
107
|
+
hash[k] ||= {}
|
|
108
|
+
hash = hash[k]
|
|
109
|
+
end
|
|
110
|
+
hash[keys.last] = value
|
|
111
|
+
|
|
112
|
+
# Register leaf-key alias (first-wins)
|
|
113
|
+
leaf = keys.last
|
|
114
|
+
if value.is_a?(String)
|
|
115
|
+
flat_aliases[locale.to_s] ||= {}
|
|
116
|
+
flat_aliases[locale.to_s][leaf] ||= value
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def available_locales
|
|
121
|
+
translations.keys
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
private
|
|
125
|
+
|
|
126
|
+
# Recursively walk a nested hash and register leaf-key aliases.
|
|
127
|
+
# First-wins: if a leaf key already exists, it is NOT overwritten.
|
|
128
|
+
def build_leaf_aliases(locale, hash, prefix = nil)
|
|
129
|
+
flat_aliases[locale.to_s] ||= {}
|
|
130
|
+
hash.each do |key, value|
|
|
131
|
+
full_key = prefix ? "#{prefix}.#{key}" : key.to_s
|
|
132
|
+
if value.is_a?(Hash)
|
|
133
|
+
build_leaf_aliases(locale, value, full_key)
|
|
134
|
+
else
|
|
135
|
+
# Store the leaf key as an alias (first-wins)
|
|
136
|
+
flat_aliases[locale.to_s][key.to_s] ||= value
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def lookup(locale, key)
|
|
142
|
+
keys = key.to_s.split(".")
|
|
143
|
+
result = translations[locale]
|
|
144
|
+
return nil unless result
|
|
145
|
+
|
|
146
|
+
# Try dot-path traversal first
|
|
147
|
+
dot_result = result
|
|
148
|
+
keys.each do |k|
|
|
149
|
+
if dot_result.is_a?(Hash)
|
|
150
|
+
dot_result = dot_result[k] || dot_result[k.to_sym]
|
|
151
|
+
else
|
|
152
|
+
dot_result = nil
|
|
153
|
+
break
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
return dot_result if dot_result.is_a?(String)
|
|
157
|
+
|
|
158
|
+
# Fall back to leaf-key alias (only for simple keys without dots)
|
|
159
|
+
if flat_aliases[locale]
|
|
160
|
+
alias_val = flat_aliases[locale][key.to_s]
|
|
161
|
+
return alias_val if alias_val.is_a?(String)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
nil
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|