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.
Files changed (132) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +80 -80
  3. data/LICENSE.txt +21 -21
  4. data/README.md +137 -137
  5. data/exe/tina4ruby +5 -5
  6. data/lib/tina4/ai.rb +696 -696
  7. data/lib/tina4/api.rb +189 -189
  8. data/lib/tina4/auth.rb +305 -305
  9. data/lib/tina4/auto_crud.rb +244 -244
  10. data/lib/tina4/cache.rb +154 -154
  11. data/lib/tina4/cli.rb +1449 -1449
  12. data/lib/tina4/constants.rb +46 -46
  13. data/lib/tina4/container.rb +74 -74
  14. data/lib/tina4/cors.rb +74 -74
  15. data/lib/tina4/crud.rb +692 -692
  16. data/lib/tina4/database/sqlite3_adapter.rb +165 -165
  17. data/lib/tina4/database.rb +625 -625
  18. data/lib/tina4/database_result.rb +208 -208
  19. data/lib/tina4/debug.rb +8 -8
  20. data/lib/tina4/dev.rb +14 -14
  21. data/lib/tina4/dev_admin.rb +935 -935
  22. data/lib/tina4/dev_mailbox.rb +191 -191
  23. data/lib/tina4/drivers/firebird_driver.rb +124 -110
  24. data/lib/tina4/drivers/mongodb_driver.rb +561 -561
  25. data/lib/tina4/drivers/mssql_driver.rb +112 -112
  26. data/lib/tina4/drivers/mysql_driver.rb +90 -90
  27. data/lib/tina4/drivers/odbc_driver.rb +191 -191
  28. data/lib/tina4/drivers/postgres_driver.rb +116 -106
  29. data/lib/tina4/drivers/sqlite_driver.rb +122 -122
  30. data/lib/tina4/env.rb +95 -95
  31. data/lib/tina4/error_overlay.rb +252 -252
  32. data/lib/tina4/events.rb +109 -109
  33. data/lib/tina4/field_types.rb +154 -154
  34. data/lib/tina4/frond.rb +2025 -2025
  35. data/lib/tina4/gallery/auth/meta.json +1 -1
  36. data/lib/tina4/gallery/auth/src/routes/api/gallery_auth.rb +114 -114
  37. data/lib/tina4/gallery/database/meta.json +1 -1
  38. data/lib/tina4/gallery/database/src/routes/api/gallery_db.rb +43 -43
  39. data/lib/tina4/gallery/error-overlay/meta.json +1 -1
  40. data/lib/tina4/gallery/error-overlay/src/routes/api/gallery_crash.rb +17 -17
  41. data/lib/tina4/gallery/orm/meta.json +1 -1
  42. data/lib/tina4/gallery/orm/src/routes/api/gallery_products.rb +16 -16
  43. data/lib/tina4/gallery/queue/meta.json +1 -1
  44. data/lib/tina4/gallery/queue/src/routes/api/gallery_queue.rb +325 -325
  45. data/lib/tina4/gallery/rest-api/meta.json +1 -1
  46. data/lib/tina4/gallery/rest-api/src/routes/api/gallery_hello.rb +14 -14
  47. data/lib/tina4/gallery/templates/meta.json +1 -1
  48. data/lib/tina4/gallery/templates/src/routes/gallery_page.rb +12 -12
  49. data/lib/tina4/gallery/templates/src/templates/gallery_page.twig +257 -257
  50. data/lib/tina4/graphql.rb +966 -966
  51. data/lib/tina4/health.rb +39 -39
  52. data/lib/tina4/html_element.rb +170 -170
  53. data/lib/tina4/job.rb +80 -80
  54. data/lib/tina4/localization.rb +168 -168
  55. data/lib/tina4/log.rb +203 -203
  56. data/lib/tina4/mcp.rb +696 -696
  57. data/lib/tina4/messenger.rb +587 -587
  58. data/lib/tina4/metrics.rb +793 -793
  59. data/lib/tina4/middleware.rb +445 -445
  60. data/lib/tina4/migration.rb +451 -451
  61. data/lib/tina4/orm.rb +790 -790
  62. data/lib/tina4/public/css/tina4.css +2463 -2463
  63. data/lib/tina4/public/css/tina4.min.css +1 -1
  64. data/lib/tina4/public/images/logo.svg +5 -5
  65. data/lib/tina4/public/js/frond.min.js +2 -2
  66. data/lib/tina4/public/js/tina4-dev-admin.js +565 -565
  67. data/lib/tina4/public/js/tina4-dev-admin.min.js +480 -480
  68. data/lib/tina4/public/js/tina4.min.js +92 -92
  69. data/lib/tina4/public/js/tina4js.min.js +48 -48
  70. data/lib/tina4/public/swagger/index.html +90 -90
  71. data/lib/tina4/public/swagger/oauth2-redirect.html +63 -63
  72. data/lib/tina4/query_builder.rb +380 -380
  73. data/lib/tina4/queue.rb +366 -366
  74. data/lib/tina4/queue_backends/kafka_backend.rb +80 -80
  75. data/lib/tina4/queue_backends/lite_backend.rb +298 -298
  76. data/lib/tina4/queue_backends/mongo_backend.rb +126 -126
  77. data/lib/tina4/queue_backends/rabbitmq_backend.rb +73 -73
  78. data/lib/tina4/rack_app.rb +817 -817
  79. data/lib/tina4/rate_limiter.rb +130 -130
  80. data/lib/tina4/request.rb +268 -255
  81. data/lib/tina4/response.rb +346 -346
  82. data/lib/tina4/response_cache.rb +551 -551
  83. data/lib/tina4/router.rb +406 -406
  84. data/lib/tina4/scss/tina4css/_alerts.scss +34 -34
  85. data/lib/tina4/scss/tina4css/_badges.scss +22 -22
  86. data/lib/tina4/scss/tina4css/_buttons.scss +69 -69
  87. data/lib/tina4/scss/tina4css/_cards.scss +49 -49
  88. data/lib/tina4/scss/tina4css/_forms.scss +156 -156
  89. data/lib/tina4/scss/tina4css/_grid.scss +81 -81
  90. data/lib/tina4/scss/tina4css/_modals.scss +84 -84
  91. data/lib/tina4/scss/tina4css/_nav.scss +149 -149
  92. data/lib/tina4/scss/tina4css/_reset.scss +94 -94
  93. data/lib/tina4/scss/tina4css/_tables.scss +54 -54
  94. data/lib/tina4/scss/tina4css/_typography.scss +55 -55
  95. data/lib/tina4/scss/tina4css/_utilities.scss +197 -197
  96. data/lib/tina4/scss/tina4css/_variables.scss +117 -117
  97. data/lib/tina4/scss/tina4css/base.scss +1 -1
  98. data/lib/tina4/scss/tina4css/colors.scss +48 -48
  99. data/lib/tina4/scss/tina4css/tina4.scss +17 -17
  100. data/lib/tina4/scss_compiler.rb +178 -178
  101. data/lib/tina4/seeder.rb +567 -567
  102. data/lib/tina4/service_runner.rb +303 -303
  103. data/lib/tina4/session.rb +297 -297
  104. data/lib/tina4/session_handlers/database_handler.rb +72 -72
  105. data/lib/tina4/session_handlers/file_handler.rb +67 -67
  106. data/lib/tina4/session_handlers/mongo_handler.rb +49 -49
  107. data/lib/tina4/session_handlers/redis_handler.rb +43 -43
  108. data/lib/tina4/session_handlers/valkey_handler.rb +43 -43
  109. data/lib/tina4/shutdown.rb +84 -84
  110. data/lib/tina4/sql_translation.rb +158 -158
  111. data/lib/tina4/swagger.rb +124 -124
  112. data/lib/tina4/template.rb +894 -894
  113. data/lib/tina4/templates/base.twig +26 -26
  114. data/lib/tina4/templates/errors/302.twig +14 -14
  115. data/lib/tina4/templates/errors/401.twig +9 -9
  116. data/lib/tina4/templates/errors/403.twig +29 -29
  117. data/lib/tina4/templates/errors/404.twig +29 -29
  118. data/lib/tina4/templates/errors/500.twig +38 -38
  119. data/lib/tina4/templates/errors/502.twig +9 -9
  120. data/lib/tina4/templates/errors/503.twig +12 -12
  121. data/lib/tina4/templates/errors/base.twig +37 -37
  122. data/lib/tina4/test_client.rb +159 -159
  123. data/lib/tina4/testing.rb +340 -340
  124. data/lib/tina4/validator.rb +174 -174
  125. data/lib/tina4/version.rb +1 -1
  126. data/lib/tina4/webserver.rb +312 -312
  127. data/lib/tina4/websocket.rb +343 -343
  128. data/lib/tina4/websocket_backplane.rb +190 -190
  129. data/lib/tina4/wsdl.rb +564 -564
  130. data/lib/tina4.rb +458 -458
  131. data/lib/tina4ruby.rb +4 -4
  132. metadata +3 -3
@@ -1,208 +1,208 @@
1
- # frozen_string_literal: true
2
- require "json"
3
-
4
- module Tina4
5
- class DatabaseResult
6
- include Enumerable
7
-
8
- attr_reader :records, :columns, :count, :limit, :offset, :sql,
9
- :affected_rows, :last_id, :error
10
-
11
- def initialize(records = [], sql: "", columns: [], count: nil, limit: 10, offset: 0,
12
- affected_rows: 0, last_id: nil, error: nil, db: nil)
13
- @records = records || []
14
- @sql = sql
15
- @columns = columns.empty? && !@records.empty? ? @records.first.keys : columns
16
- @count = count || @records.length
17
- @limit = limit
18
- @offset = offset
19
- @affected_rows = affected_rows
20
- @last_id = last_id
21
- @error = error
22
- @db = db
23
- @column_info_cache = nil
24
- end
25
-
26
- def each(&block)
27
- @records.each(&block)
28
- end
29
-
30
- def first
31
- @records.first
32
- end
33
-
34
- def last
35
- @records.last
36
- end
37
-
38
- def empty?
39
- @records.empty?
40
- end
41
-
42
- def [](index)
43
- @records[index]
44
- end
45
-
46
- def length
47
- @count
48
- end
49
-
50
- def size
51
- @count
52
- end
53
-
54
- def success?
55
- @error.nil?
56
- end
57
-
58
- def to_array
59
- @records.map do |record|
60
- record.is_a?(Hash) ? record : record.to_h
61
- end
62
- end
63
-
64
- alias to_a to_array
65
-
66
- def to_json(*_args)
67
- JSON.generate(to_array)
68
- end
69
-
70
- def to_csv(separator: ",", headers: true)
71
- return "" if @records.empty?
72
- lines = []
73
- cols = @records.first.keys
74
- lines << cols.join(separator) if headers
75
- @records.each do |row|
76
- lines << cols.map { |c| escape_csv(row[c], separator) }.join(separator)
77
- end
78
- lines.join("\n")
79
- end
80
-
81
- def to_paginate(page: nil, per_page: nil)
82
- per_page ||= @limit > 0 ? @limit : 10
83
- page ||= @offset > 0 ? (@offset / per_page) + 1 : 1
84
- total = @count
85
- total_pages = [1, (total.to_f / per_page).ceil].max
86
- slice_offset = (page - 1) * per_page
87
- page_records = @records[slice_offset, per_page] || []
88
- {
89
- records: page_records,
90
- data: page_records,
91
- count: total,
92
- total: total,
93
- limit: per_page,
94
- offset: (page - 1) * per_page,
95
- page: page,
96
- per_page: per_page,
97
- totalPages: total_pages,
98
- total_pages: total_pages,
99
- has_next: page < total_pages,
100
- has_prev: page > 1
101
- }
102
- end
103
-
104
- def to_crud(table_name: "data", primary_key: "id", editable: true)
105
- Tina4::Crud.generate_table(@records, table_name: table_name,
106
- primary_key: primary_key, editable: editable)
107
- end
108
-
109
- # Return column metadata for the query's table.
110
- #
111
- # Lazy — only queries the database when explicitly called. Caches the
112
- # result so subsequent calls return immediately without re-querying.
113
- #
114
- # Returns an array of hashes with keys:
115
- # name, type, size, decimals, nullable, primary_key
116
- def column_info
117
- return @column_info_cache if @column_info_cache
118
-
119
- table = extract_table_from_sql
120
-
121
- if @db && table
122
- begin
123
- @column_info_cache = query_column_metadata(table)
124
- return @column_info_cache
125
- rescue StandardError
126
- # Fall through to fallback
127
- end
128
- end
129
-
130
- @column_info_cache = fallback_column_info
131
- @column_info_cache
132
- end
133
-
134
- private
135
-
136
- def extract_table_from_sql
137
- return nil if @sql.nil? || @sql.empty?
138
-
139
- if (m = @sql.match(/\bFROM\s+["']?(\w+)["']?/i))
140
- return m[1]
141
- end
142
- if (m = @sql.match(/\bINSERT\s+INTO\s+["']?(\w+)["']?/i))
143
- return m[1]
144
- end
145
- if (m = @sql.match(/\bUPDATE\s+["']?(\w+)["']?/i))
146
- return m[1]
147
- end
148
- nil
149
- end
150
-
151
- def query_column_metadata(table)
152
- # Use the database's columns method which delegates to the driver
153
- raw_cols = @db.columns(table)
154
- normalize_columns(raw_cols)
155
- rescue StandardError
156
- fallback_column_info
157
- end
158
-
159
- def normalize_columns(raw_cols)
160
- raw_cols.map do |col|
161
- col_type = (col[:type] || col["type"] || "UNKNOWN").to_s.upcase
162
- size, decimals = parse_type_size(col_type)
163
- {
164
- name: (col[:name] || col["name"]).to_s,
165
- type: col_type.sub(/\(.*\)/, ""),
166
- size: size,
167
- decimals: decimals,
168
- nullable: col.key?(:nullable) ? col[:nullable] : (col.key?("nullable") ? col["nullable"] : true),
169
- primary_key: col[:primary_key] || col["primary_key"] || col[:primary] || col["primary"] || false
170
- }
171
- end
172
- end
173
-
174
- def parse_type_size(type_str)
175
- if (m = type_str.match(/\((\d+)(?:\s*,\s*(\d+))?\)/))
176
- size = m[1].to_i
177
- decimals = m[2] ? m[2].to_i : nil
178
- [size, decimals]
179
- else
180
- [nil, nil]
181
- end
182
- end
183
-
184
- def fallback_column_info
185
- return [] if @records.empty?
186
- keys = @records.first.is_a?(Hash) ? @records.first.keys : []
187
- keys.map do |k|
188
- {
189
- name: k.to_s,
190
- type: "UNKNOWN",
191
- size: nil,
192
- decimals: nil,
193
- nullable: true,
194
- primary_key: false
195
- }
196
- end
197
- end
198
-
199
- def escape_csv(value, separator)
200
- str = value.to_s
201
- if str.include?(separator) || str.include?('"') || str.include?("\n")
202
- "\"#{str.gsub('"', '""')}\""
203
- else
204
- str
205
- end
206
- end
207
- end
208
- end
1
+ # frozen_string_literal: true
2
+ require "json"
3
+
4
+ module Tina4
5
+ class DatabaseResult
6
+ include Enumerable
7
+
8
+ attr_reader :records, :columns, :count, :limit, :offset, :sql,
9
+ :affected_rows, :last_id, :error
10
+
11
+ def initialize(records = [], sql: "", columns: [], count: nil, limit: 10, offset: 0,
12
+ affected_rows: 0, last_id: nil, error: nil, db: nil)
13
+ @records = records || []
14
+ @sql = sql
15
+ @columns = columns.empty? && !@records.empty? ? @records.first.keys : columns
16
+ @count = count || @records.length
17
+ @limit = limit
18
+ @offset = offset
19
+ @affected_rows = affected_rows
20
+ @last_id = last_id
21
+ @error = error
22
+ @db = db
23
+ @column_info_cache = nil
24
+ end
25
+
26
+ def each(&block)
27
+ @records.each(&block)
28
+ end
29
+
30
+ def first
31
+ @records.first
32
+ end
33
+
34
+ def last
35
+ @records.last
36
+ end
37
+
38
+ def empty?
39
+ @records.empty?
40
+ end
41
+
42
+ def [](index)
43
+ @records[index]
44
+ end
45
+
46
+ def length
47
+ @count
48
+ end
49
+
50
+ def size
51
+ @count
52
+ end
53
+
54
+ def success?
55
+ @error.nil?
56
+ end
57
+
58
+ def to_array
59
+ @records.map do |record|
60
+ record.is_a?(Hash) ? record : record.to_h
61
+ end
62
+ end
63
+
64
+ alias to_a to_array
65
+
66
+ def to_json(*_args)
67
+ JSON.generate(to_array)
68
+ end
69
+
70
+ def to_csv(separator: ",", headers: true)
71
+ return "" if @records.empty?
72
+ lines = []
73
+ cols = @records.first.keys
74
+ lines << cols.join(separator) if headers
75
+ @records.each do |row|
76
+ lines << cols.map { |c| escape_csv(row[c], separator) }.join(separator)
77
+ end
78
+ lines.join("\n")
79
+ end
80
+
81
+ def to_paginate(page: nil, per_page: nil)
82
+ per_page ||= @limit > 0 ? @limit : 10
83
+ page ||= @offset > 0 ? (@offset / per_page) + 1 : 1
84
+ total = @count
85
+ total_pages = [1, (total.to_f / per_page).ceil].max
86
+ slice_offset = (page - 1) * per_page
87
+ page_records = @records[slice_offset, per_page] || []
88
+ {
89
+ records: page_records,
90
+ data: page_records,
91
+ count: total,
92
+ total: total,
93
+ limit: per_page,
94
+ offset: (page - 1) * per_page,
95
+ page: page,
96
+ per_page: per_page,
97
+ totalPages: total_pages,
98
+ total_pages: total_pages,
99
+ has_next: page < total_pages,
100
+ has_prev: page > 1
101
+ }
102
+ end
103
+
104
+ def to_crud(table_name: "data", primary_key: "id", editable: true)
105
+ Tina4::Crud.generate_table(@records, table_name: table_name,
106
+ primary_key: primary_key, editable: editable)
107
+ end
108
+
109
+ # Return column metadata for the query's table.
110
+ #
111
+ # Lazy — only queries the database when explicitly called. Caches the
112
+ # result so subsequent calls return immediately without re-querying.
113
+ #
114
+ # Returns an array of hashes with keys:
115
+ # name, type, size, decimals, nullable, primary_key
116
+ def column_info
117
+ return @column_info_cache if @column_info_cache
118
+
119
+ table = extract_table_from_sql
120
+
121
+ if @db && table
122
+ begin
123
+ @column_info_cache = query_column_metadata(table)
124
+ return @column_info_cache
125
+ rescue StandardError
126
+ # Fall through to fallback
127
+ end
128
+ end
129
+
130
+ @column_info_cache = fallback_column_info
131
+ @column_info_cache
132
+ end
133
+
134
+ private
135
+
136
+ def extract_table_from_sql
137
+ return nil if @sql.nil? || @sql.empty?
138
+
139
+ if (m = @sql.match(/\bFROM\s+["']?(\w+)["']?/i))
140
+ return m[1]
141
+ end
142
+ if (m = @sql.match(/\bINSERT\s+INTO\s+["']?(\w+)["']?/i))
143
+ return m[1]
144
+ end
145
+ if (m = @sql.match(/\bUPDATE\s+["']?(\w+)["']?/i))
146
+ return m[1]
147
+ end
148
+ nil
149
+ end
150
+
151
+ def query_column_metadata(table)
152
+ # Use the database's columns method which delegates to the driver
153
+ raw_cols = @db.columns(table)
154
+ normalize_columns(raw_cols)
155
+ rescue StandardError
156
+ fallback_column_info
157
+ end
158
+
159
+ def normalize_columns(raw_cols)
160
+ raw_cols.map do |col|
161
+ col_type = (col[:type] || col["type"] || "UNKNOWN").to_s.upcase
162
+ size, decimals = parse_type_size(col_type)
163
+ {
164
+ name: (col[:name] || col["name"]).to_s,
165
+ type: col_type.sub(/\(.*\)/, ""),
166
+ size: size,
167
+ decimals: decimals,
168
+ nullable: col.key?(:nullable) ? col[:nullable] : (col.key?("nullable") ? col["nullable"] : true),
169
+ primary_key: col[:primary_key] || col["primary_key"] || col[:primary] || col["primary"] || false
170
+ }
171
+ end
172
+ end
173
+
174
+ def parse_type_size(type_str)
175
+ if (m = type_str.match(/\((\d+)(?:\s*,\s*(\d+))?\)/))
176
+ size = m[1].to_i
177
+ decimals = m[2] ? m[2].to_i : nil
178
+ [size, decimals]
179
+ else
180
+ [nil, nil]
181
+ end
182
+ end
183
+
184
+ def fallback_column_info
185
+ return [] if @records.empty?
186
+ keys = @records.first.is_a?(Hash) ? @records.first.keys : []
187
+ keys.map do |k|
188
+ {
189
+ name: k.to_s,
190
+ type: "UNKNOWN",
191
+ size: nil,
192
+ decimals: nil,
193
+ nullable: true,
194
+ primary_key: false
195
+ }
196
+ end
197
+ end
198
+
199
+ def escape_csv(value, separator)
200
+ str = value.to_s
201
+ if str.include?(separator) || str.include?('"') || str.include?("\n")
202
+ "\"#{str.gsub('"', '""')}\""
203
+ else
204
+ str
205
+ end
206
+ end
207
+ end
208
+ end
data/lib/tina4/debug.rb CHANGED
@@ -1,8 +1,8 @@
1
- # frozen_string_literal: true
2
-
3
- # Backward compatibility: Tina4::Debug is now Tina4::Log
4
- require_relative "log"
5
-
6
- module Tina4
7
- Debug = Log
8
- end
1
+ # frozen_string_literal: true
2
+
3
+ # Backward compatibility: Tina4::Debug is now Tina4::Log
4
+ require_relative "log"
5
+
6
+ module Tina4
7
+ Debug = Log
8
+ end
data/lib/tina4/dev.rb CHANGED
@@ -1,14 +1,14 @@
1
- # frozen_string_literal: true
2
-
3
- # Convenience require for all development/optional tools.
4
- # Usage: require "tina4/dev"
5
-
6
- require_relative "scss_compiler"
7
- require_relative "testing"
8
- require_relative "graphql"
9
- require_relative "websocket"
10
- require_relative "wsdl"
11
- require_relative "swagger"
12
- require_relative "seeder"
13
- require_relative "crud"
14
- require_relative "api"
1
+ # frozen_string_literal: true
2
+
3
+ # Convenience require for all development/optional tools.
4
+ # Usage: require "tina4/dev"
5
+
6
+ require_relative "scss_compiler"
7
+ require_relative "testing"
8
+ require_relative "graphql"
9
+ require_relative "websocket"
10
+ require_relative "wsdl"
11
+ require_relative "swagger"
12
+ require_relative "seeder"
13
+ require_relative "crud"
14
+ require_relative "api"