tina4ruby 3.11.15 → 3.11.17

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 (134) 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 +1291 -935
  22. data/lib/tina4/dev_mailbox.rb +191 -191
  23. data/lib/tina4/drivers/firebird_driver.rb +124 -124
  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 -116
  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 +2087 -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 +871 -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/plan.rb +471 -0
  63. data/lib/tina4/project_index.rb +366 -0
  64. data/lib/tina4/public/css/tina4.css +2463 -2463
  65. data/lib/tina4/public/css/tina4.min.css +1 -1
  66. data/lib/tina4/public/images/logo.svg +5 -5
  67. data/lib/tina4/public/js/frond.min.js +2 -2
  68. data/lib/tina4/public/js/tina4-dev-admin.js +1264 -565
  69. data/lib/tina4/public/js/tina4-dev-admin.min.js +1264 -480
  70. data/lib/tina4/public/js/tina4.min.js +92 -92
  71. data/lib/tina4/public/js/tina4js.min.js +48 -48
  72. data/lib/tina4/public/swagger/index.html +90 -90
  73. data/lib/tina4/public/swagger/oauth2-redirect.html +63 -63
  74. data/lib/tina4/query_builder.rb +380 -380
  75. data/lib/tina4/queue.rb +366 -366
  76. data/lib/tina4/queue_backends/kafka_backend.rb +80 -80
  77. data/lib/tina4/queue_backends/lite_backend.rb +298 -298
  78. data/lib/tina4/queue_backends/mongo_backend.rb +126 -126
  79. data/lib/tina4/queue_backends/rabbitmq_backend.rb +73 -73
  80. data/lib/tina4/rack_app.rb +817 -817
  81. data/lib/tina4/rate_limiter.rb +130 -130
  82. data/lib/tina4/request.rb +268 -268
  83. data/lib/tina4/response.rb +346 -346
  84. data/lib/tina4/response_cache.rb +551 -551
  85. data/lib/tina4/router.rb +406 -406
  86. data/lib/tina4/scss/tina4css/_alerts.scss +34 -34
  87. data/lib/tina4/scss/tina4css/_badges.scss +22 -22
  88. data/lib/tina4/scss/tina4css/_buttons.scss +69 -69
  89. data/lib/tina4/scss/tina4css/_cards.scss +49 -49
  90. data/lib/tina4/scss/tina4css/_forms.scss +156 -156
  91. data/lib/tina4/scss/tina4css/_grid.scss +81 -81
  92. data/lib/tina4/scss/tina4css/_modals.scss +84 -84
  93. data/lib/tina4/scss/tina4css/_nav.scss +149 -149
  94. data/lib/tina4/scss/tina4css/_reset.scss +94 -94
  95. data/lib/tina4/scss/tina4css/_tables.scss +54 -54
  96. data/lib/tina4/scss/tina4css/_typography.scss +55 -55
  97. data/lib/tina4/scss/tina4css/_utilities.scss +197 -197
  98. data/lib/tina4/scss/tina4css/_variables.scss +117 -117
  99. data/lib/tina4/scss/tina4css/base.scss +1 -1
  100. data/lib/tina4/scss/tina4css/colors.scss +48 -48
  101. data/lib/tina4/scss/tina4css/tina4.scss +17 -17
  102. data/lib/tina4/scss_compiler.rb +178 -178
  103. data/lib/tina4/seeder.rb +567 -567
  104. data/lib/tina4/service_runner.rb +303 -303
  105. data/lib/tina4/session.rb +297 -297
  106. data/lib/tina4/session_handlers/database_handler.rb +72 -72
  107. data/lib/tina4/session_handlers/file_handler.rb +67 -67
  108. data/lib/tina4/session_handlers/mongo_handler.rb +49 -49
  109. data/lib/tina4/session_handlers/redis_handler.rb +43 -43
  110. data/lib/tina4/session_handlers/valkey_handler.rb +43 -43
  111. data/lib/tina4/shutdown.rb +84 -84
  112. data/lib/tina4/sql_translation.rb +158 -158
  113. data/lib/tina4/swagger.rb +124 -124
  114. data/lib/tina4/template.rb +894 -894
  115. data/lib/tina4/templates/base.twig +26 -26
  116. data/lib/tina4/templates/errors/302.twig +14 -14
  117. data/lib/tina4/templates/errors/401.twig +9 -9
  118. data/lib/tina4/templates/errors/403.twig +29 -29
  119. data/lib/tina4/templates/errors/404.twig +29 -29
  120. data/lib/tina4/templates/errors/500.twig +38 -38
  121. data/lib/tina4/templates/errors/502.twig +9 -9
  122. data/lib/tina4/templates/errors/503.twig +12 -12
  123. data/lib/tina4/templates/errors/base.twig +37 -37
  124. data/lib/tina4/test_client.rb +159 -159
  125. data/lib/tina4/testing.rb +340 -340
  126. data/lib/tina4/validator.rb +174 -174
  127. data/lib/tina4/version.rb +1 -1
  128. data/lib/tina4/webserver.rb +312 -312
  129. data/lib/tina4/websocket.rb +343 -343
  130. data/lib/tina4/websocket_backplane.rb +190 -190
  131. data/lib/tina4/wsdl.rb +564 -564
  132. data/lib/tina4.rb +460 -458
  133. data/lib/tina4ruby.rb +4 -4
  134. metadata +5 -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"