dbdoc_engine 0.1.0

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 (198) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +331 -0
  3. data/Rakefile +8 -0
  4. data/app/assets/builds/dbdoc_engine/application.css +5 -0
  5. data/app/assets/images/dbdoc_engine/arrowdown.svg +3 -0
  6. data/app/assets/images/dbdoc_engine/arrowhorizontal.svg +3 -0
  7. data/app/assets/images/dbdoc_engine/arrowleft.svg +3 -0
  8. data/app/assets/images/dbdoc_engine/changelog.svg +3 -0
  9. data/app/assets/images/dbdoc_engine/column_stats_dbdocs.svg +23 -0
  10. data/app/assets/images/dbdoc_engine/diagram.svg +3 -0
  11. data/app/assets/images/dbdoc_engine/double_arrow.svg +4 -0
  12. data/app/assets/images/dbdoc_engine/group_bu.svg +3 -0
  13. data/app/assets/images/dbdoc_engine/japan_circle.png +0 -0
  14. data/app/assets/images/dbdoc_engine/log_in_image.png +0 -0
  15. data/app/assets/images/dbdoc_engine/logo.svg +12 -0
  16. data/app/assets/images/dbdoc_engine/orange_changelog.svg +3 -0
  17. data/app/assets/images/dbdoc_engine/orange_fields.svg +23 -0
  18. data/app/assets/images/dbdoc_engine/orange_logo.svg +12 -0
  19. data/app/assets/images/dbdoc_engine/orange_table.svg +21 -0
  20. data/app/assets/images/dbdoc_engine/orange_updates.svg +43 -0
  21. data/app/assets/images/dbdoc_engine/orange_wiki.svg +3 -0
  22. data/app/assets/images/dbdoc_engine/search.svg +3 -0
  23. data/app/assets/images/dbdoc_engine/setting.svg +3 -0
  24. data/app/assets/images/dbdoc_engine/table_dbdocs.svg +21 -0
  25. data/app/assets/images/dbdoc_engine/uk_circle_transparent.png +0 -0
  26. data/app/assets/images/dbdoc_engine/update_stats_dbdocs.svg +43 -0
  27. data/app/assets/images/dbdoc_engine/wiki.svg +3 -0
  28. data/app/assets/stylesheets/dbdoc_engine/admin.css +176 -0
  29. data/app/assets/stylesheets/dbdoc_engine/admin_header.css +179 -0
  30. data/app/assets/stylesheets/dbdoc_engine/application.scss +1 -0
  31. data/app/assets/stylesheets/dbdoc_engine/changelog.css +173 -0
  32. data/app/assets/stylesheets/dbdoc_engine/dashboard.css +513 -0
  33. data/app/assets/stylesheets/dbdoc_engine/dbdoc_application.css +117 -0
  34. data/app/assets/stylesheets/dbdoc_engine/ecommerce.css +253 -0
  35. data/app/assets/stylesheets/dbdoc_engine/group_details.css +178 -0
  36. data/app/assets/stylesheets/dbdoc_engine/header.css +212 -0
  37. data/app/assets/stylesheets/dbdoc_engine/loading_spinner.css +127 -0
  38. data/app/assets/stylesheets/dbdoc_engine/login.css +213 -0
  39. data/app/assets/stylesheets/dbdoc_engine/schema_diagram.css +149 -0
  40. data/app/assets/stylesheets/dbdoc_engine/sidebar.css +296 -0
  41. data/app/assets/stylesheets/dbdoc_engine/table_details.css +417 -0
  42. data/app/controllers/dbdoc_engine/admin/base_controller.rb +23 -0
  43. data/app/controllers/dbdoc_engine/admin/dashboard_controller.rb +16 -0
  44. data/app/controllers/dbdoc_engine/admin/data_transfer_controller.rb +63 -0
  45. data/app/controllers/dbdoc_engine/admin/db_design_dynamic_tables_controller.rb +198 -0
  46. data/app/controllers/dbdoc_engine/admin/db_design_table_groups_controller.rb +107 -0
  47. data/app/controllers/dbdoc_engine/application_controller.rb +65 -0
  48. data/app/controllers/dbdoc_engine/concerns/internationalization.rb +57 -0
  49. data/app/controllers/dbdoc_engine/db_doc_sessions_controller.rb +33 -0
  50. data/app/controllers/dbdoc_engine/home_controller.rb +79 -0
  51. data/app/controllers/dbdoc_engine/schema_diagram_controller.rb +293 -0
  52. data/app/helper/dbdoc_engine/application_helper.rb +35 -0
  53. data/app/helpers/dbdoc_engine/application_helper.rb +4 -0
  54. data/app/helpers/dbdoc_engine/changelogs_helper.rb +27 -0
  55. data/app/helpers/dbdoc_engine/column_helper.rb +30 -0
  56. data/app/helpers/dbdoc_engine/db_design_dynamic_tables_helper.rb +15 -0
  57. data/app/helpers/dbdoc_engine/home_helper.rb +75 -0
  58. data/app/javascript/dbdoc_engine/application.js +12 -0
  59. data/app/javascript/dbdoc_engine/controllers/application.js +29 -0
  60. data/app/javascript/dbdoc_engine/controllers/auto_submit_controller.js +17 -0
  61. data/app/javascript/dbdoc_engine/controllers/chart_controller.js +58 -0
  62. data/app/javascript/dbdoc_engine/controllers/column-type_controller.js +149 -0
  63. data/app/javascript/dbdoc_engine/controllers/column_controller.js +362 -0
  64. data/app/javascript/dbdoc_engine/controllers/column_search_controller.js +42 -0
  65. data/app/javascript/dbdoc_engine/controllers/dbdoc_accordion_controller.js +42 -0
  66. data/app/javascript/dbdoc_engine/controllers/ecommerce_controller.js +73 -0
  67. data/app/javascript/dbdoc_engine/controllers/group_details_controller.js +88 -0
  68. data/app/javascript/dbdoc_engine/controllers/import_export_controller.js +200 -0
  69. data/app/javascript/dbdoc_engine/controllers/index.js +9 -0
  70. data/app/javascript/dbdoc_engine/controllers/language_controller.js +100 -0
  71. data/app/javascript/dbdoc_engine/controllers/loading_spinner_controller.js +48 -0
  72. data/app/javascript/dbdoc_engine/controllers/login_controller.js +75 -0
  73. data/app/javascript/dbdoc_engine/controllers/notification_controller.js +15 -0
  74. data/app/javascript/dbdoc_engine/controllers/schema_diagram_controller.js +1129 -0
  75. data/app/javascript/dbdoc_engine/controllers/select2_controller.js +67 -0
  76. data/app/javascript/dbdoc_engine/controllers/sidebar_controller.js +943 -0
  77. data/app/javascript/dbdoc_engine/controllers/table_details_controller.js +245 -0
  78. data/app/javascript/dbdoc_engine/controllers/table_group_validation_controller.js +148 -0
  79. data/app/javascript/dbdoc_engine/controllers/table_validation_controller.js +423 -0
  80. data/app/jobs/dbdoc_engine/application_job.rb +4 -0
  81. data/app/mailers/dbdoc_engine/application_mailer.rb +6 -0
  82. data/app/models/dbdoc_engine/application_record.rb +6 -0
  83. data/app/models/dbdoc_engine/concerns/soft_deletable.rb +30 -0
  84. data/app/models/dbdoc_engine/db_design_changelog.rb +44 -0
  85. data/app/models/dbdoc_engine/db_design_dynamic_column.rb +211 -0
  86. data/app/models/dbdoc_engine/db_design_dynamic_table.rb +124 -0
  87. data/app/models/dbdoc_engine/db_design_table_group.rb +88 -0
  88. data/app/models/dbdoc_engine/user.rb +21 -0
  89. data/app/queries/dbdoc_engine/admin_dashboard_queries.rb +71 -0
  90. data/app/queries/dbdoc_engine/db_design_changelog_queries.rb +68 -0
  91. data/app/queries/dbdoc_engine/db_design_dynamic_column_queries.rb +37 -0
  92. data/app/queries/dbdoc_engine/db_design_dynamic_table_commands.rb +106 -0
  93. data/app/queries/dbdoc_engine/db_design_dynamic_table_queries.rb +194 -0
  94. data/app/queries/dbdoc_engine/db_design_table_group_queries.rb +154 -0
  95. data/app/services/dbdoc_engine/db_design_dynamic_table_export_service.rb +38 -0
  96. data/app/services/dbdoc_engine/db_design_dynamic_table_handler_service.rb +49 -0
  97. data/app/services/dbdoc_engine/db_design_dynamic_tables_service.rb +21 -0
  98. data/app/services/dbdoc_engine/error_handler_service.rb +43 -0
  99. data/app/services/dbdoc_engine/schema_rb_import_service.rb +194 -0
  100. data/app/services/dbdoc_engine/schema_rb_parser_service.rb +339 -0
  101. data/app/services/dbdoc_engine/table_filter_service.rb +35 -0
  102. data/app/services/dbdoc_engine/table_groups_service.rb +199 -0
  103. data/app/services/dbdoc_engine/table_management_service.rb +192 -0
  104. data/app/views/dbdoc_engine/admin/dashboard/_action_badge.html.erb +11 -0
  105. data/app/views/dbdoc_engine/admin/dashboard/_changelog_rows.html.erb +22 -0
  106. data/app/views/dbdoc_engine/admin/dashboard/_changelog_table_headers.html.erb +8 -0
  107. data/app/views/dbdoc_engine/admin/dashboard/_filter_fields.html.erb +43 -0
  108. data/app/views/dbdoc_engine/admin/dashboard/index.html.erb +159 -0
  109. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_column_fields.html.erb +225 -0
  110. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_deleted_table_index.html.erb +110 -0
  111. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_foreign_key_fields.html.erb +51 -0
  112. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_form.html.erb +75 -0
  113. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_recent_activity.html.erb +39 -0
  114. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_table_columns.html.erb +127 -0
  115. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_table_index.html.erb +109 -0
  116. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_table_information.html.erb +99 -0
  117. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/deleted_tables.html.erb +95 -0
  118. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/edit.html.erb +23 -0
  119. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/export_all_to_excel.xlsx.axlsx +240 -0
  120. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/export_to_excel.xlsx.axlsx +135 -0
  121. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/index.html.erb +109 -0
  122. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/new.html.erb +25 -0
  123. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/show_table_info.html.erb +125 -0
  124. data/app/views/dbdoc_engine/admin/db_design_table_groups/_deleted_table_groups_list.html.erb +75 -0
  125. data/app/views/dbdoc_engine/admin/db_design_table_groups/_form.html.erb +88 -0
  126. data/app/views/dbdoc_engine/admin/db_design_table_groups/_table_groups_list.html.erb +82 -0
  127. data/app/views/dbdoc_engine/admin/db_design_table_groups/deleted_groups.html.erb +60 -0
  128. data/app/views/dbdoc_engine/admin/db_design_table_groups/edit.html.erb +25 -0
  129. data/app/views/dbdoc_engine/admin/db_design_table_groups/index.html.erb +85 -0
  130. data/app/views/dbdoc_engine/admin/db_design_table_groups/new.html.erb +26 -0
  131. data/app/views/dbdoc_engine/db_doc_sessions/new.html.erb +59 -0
  132. data/app/views/dbdoc_engine/home/changelog_details.html.erb +80 -0
  133. data/app/views/dbdoc_engine/home/changelogs.html.erb +20 -0
  134. data/app/views/dbdoc_engine/home/group_details.html.erb +94 -0
  135. data/app/views/dbdoc_engine/home/index.html.erb +11 -0
  136. data/app/views/dbdoc_engine/home/partials/_action_badge.html.erb +11 -0
  137. data/app/views/dbdoc_engine/home/partials/_breadcrumb_navigation.html.erb +30 -0
  138. data/app/views/dbdoc_engine/home/partials/_changelog_rows.html.erb +35 -0
  139. data/app/views/dbdoc_engine/home/partials/_changelog_table_headers.html.erb +16 -0
  140. data/app/views/dbdoc_engine/home/partials/_column_headers.html.erb +23 -0
  141. data/app/views/dbdoc_engine/home/partials/_column_row.html.erb +157 -0
  142. data/app/views/dbdoc_engine/home/partials/_filter_form.html.erb +47 -0
  143. data/app/views/dbdoc_engine/home/partials/_group_section.html.erb +84 -0
  144. data/app/views/dbdoc_engine/home/partials/_pagination.html.erb +5 -0
  145. data/app/views/dbdoc_engine/home/partials/_stats_container.html.erb +46 -0
  146. data/app/views/dbdoc_engine/home/partials/_table_groups.html.erb +7 -0
  147. data/app/views/dbdoc_engine/home/partials/_table_information_section.html.erb +50 -0
  148. data/app/views/dbdoc_engine/home/partials/_table_section.html.erb +48 -0
  149. data/app/views/dbdoc_engine/home/table_details.html.erb +9 -0
  150. data/app/views/dbdoc_engine/schema_diagram/index.html.erb +102 -0
  151. data/app/views/dbdoc_engine/shared/_admin_header.html.erb +78 -0
  152. data/app/views/dbdoc_engine/shared/_header.html.erb +94 -0
  153. data/app/views/dbdoc_engine/shared/_js_translations.html.erb +3 -0
  154. data/app/views/dbdoc_engine/shared/_language_button.html.erb +14 -0
  155. data/app/views/dbdoc_engine/shared/_sidebar.html.erb +128 -0
  156. data/app/views/kaminari/dbdoc_engine/_first_page.html.erb +3 -0
  157. data/app/views/kaminari/dbdoc_engine/_gap.html.erb +3 -0
  158. data/app/views/kaminari/dbdoc_engine/_last_page.html.erb +3 -0
  159. data/app/views/kaminari/dbdoc_engine/_next_page.html.erb +3 -0
  160. data/app/views/kaminari/dbdoc_engine/_page.html.erb +9 -0
  161. data/app/views/kaminari/dbdoc_engine/_paginator.html.erb +17 -0
  162. data/app/views/kaminari/dbdoc_engine/_prev_page.html.erb +3 -0
  163. data/app/views/layouts/dbdoc_engine/application.html.erb +107 -0
  164. data/app/views/layouts/dbdoc_engine/header.html.erb +108 -0
  165. data/config/importmap.rb +11 -0
  166. data/config/locales/en.yml +307 -0
  167. data/config/locales/ja.yml +306 -0
  168. data/config/routes.rb +73 -0
  169. data/db/migrate/rails7/20250227060610_create_db_design_table_groups.rb +15 -0
  170. data/db/migrate/rails7/20250227094626_create_db_design_dynamic_tables.rb +19 -0
  171. data/db/migrate/rails7/20250228022732_create_db_design_dynamic_columns.rb +34 -0
  172. data/db/migrate/rails7/20250401051453_create_db_design_changelogs.rb +26 -0
  173. data/db/migrate/rails7/20250411040822_create_users.rb +14 -0
  174. data/db/migrate/rails7/20250421080851_add_missing_indexes_to_dbdoc_tables.rb +23 -0
  175. data/db/migrate/rails8/20250227060610_create_db_design_table_groups.rb +15 -0
  176. data/db/migrate/rails8/20250227094626_create_db_design_dynamic_tables.rb +19 -0
  177. data/db/migrate/rails8/20250228022732_create_db_design_dynamic_columns.rb +34 -0
  178. data/db/migrate/rails8/20250401051453_create_db_design_changelogs.rb +26 -0
  179. data/db/migrate/rails8/20250411040822_create_users.rb +14 -0
  180. data/db/migrate/rails8/20250421080851_add_missing_indexes_to_dbdoc_tables.rb +23 -0
  181. data/db/seeds.rb +28 -0
  182. data/lib/dbdoc_engine/engine.rb +57 -0
  183. data/lib/dbdoc_engine/version.rb +3 -0
  184. data/lib/dbdoc_engine.rb +9 -0
  185. data/lib/generators/dbdoc_engine/install/install_generator.rb +245 -0
  186. data/lib/generators/dbdoc_engine/uninstall/uninstall_generator.rb +196 -0
  187. data/lib/tasks/dbdoc_engine_tasks.rake +44 -0
  188. data/public/dbdoc_engine_assets/images/camel_chess_head.png +0 -0
  189. data/public/dbdoc_engine_assets/images/dblogo.svg +4 -0
  190. data/public/dbdoc_engine_assets/images/japan_circle.png +0 -0
  191. data/public/dbdoc_engine_assets/images/king_chess_head.png +0 -0
  192. data/public/dbdoc_engine_assets/images/login-bg.svg +44 -0
  193. data/public/dbdoc_engine_assets/images/logo.png +0 -0
  194. data/public/dbdoc_engine_assets/images/logo.svg +12 -0
  195. data/public/dbdoc_engine_assets/images/queen_chess_head.png +0 -0
  196. data/public/dbdoc_engine_assets/images/soldier_chess_headd.png +0 -0
  197. data/public/dbdoc_engine_assets/images/uk_circle_transparent.png +0 -0
  198. metadata +415 -0
@@ -0,0 +1,211 @@
1
+ # Model for dynamically created columns
2
+ module DbdocEngine
3
+ class DbDesignDynamicColumn < ApplicationRecord
4
+ include DbdocEngine::Concerns::SoftDeletable
5
+ self.table_name = 'db_design_dynamic_columns'
6
+
7
+ # Constants for relationship types
8
+ RELATIONSHIP_ONE_TO_ONE = 'one_to_one'.freeze
9
+ RELATIONSHIP_ONE_TO_MANY = 'one_to_many'.freeze
10
+ RELATIONSHIP_MANY_TO_ONE = 'many_to_one'.freeze
11
+ RELATIONSHIP_MANY_TO_MANY = 'many_to_many'.freeze
12
+ RELATIONSHIP_TYPES = [RELATIONSHIP_ONE_TO_ONE, RELATIONSHIP_ONE_TO_MANY, RELATIONSHIP_MANY_TO_ONE, RELATIONSHIP_MANY_TO_MANY].freeze
13
+
14
+ # Association: Each column belongs to a dynamic table
15
+ belongs_to :db_design_dynamic_table
16
+
17
+ # === Validations ===
18
+
19
+ # Ensures the column name is present, unique within the table, and follows naming conventions
20
+ validates :column_name,
21
+ presence: true,
22
+ uniqueness: { scope: :db_design_dynamic_table_id },
23
+ format: { with: /\A[a-z_]+\z/ }
24
+
25
+ # Ensures the physical column name is present
26
+ validates :physical_column_name, presence: true
27
+
28
+ # Ensures a data type is selected
29
+ validates :data_type, presence: true
30
+
31
+ # Ensures the creator's name is recorded
32
+ validates :created_by, presence: true
33
+
34
+ # Ensures foreign key constraints are properly defined when applicable
35
+ validates :foreign_table_name, presence: true, if: -> { is_foreign_key }
36
+ validates :foreign_column_name, presence: true, if: -> { is_foreign_key }
37
+
38
+ # Validates relationship_type when it's a foreign key
39
+ validates :relationship_type, inclusion: { in: RELATIONSHIP_TYPES }, if: -> { is_foreign_key && relationship_type.present? }
40
+
41
+ # === Callbacks ===
42
+
43
+ # Normalizes column name format before saving
44
+ before_save :normalize_column_name
45
+
46
+ # Prevents deletion if the column is referenced as a foreign key
47
+ before_destroy :check_foreign_key_dependencies
48
+
49
+ # Logs changes after creation, update, and deletion
50
+ after_create :log_creation
51
+ after_update :log_update
52
+ before_destroy :log_deletion
53
+
54
+ # Default scope to exclude soft-deleted records
55
+ default_scope { without_deleted }
56
+
57
+ private
58
+
59
+ # Converts column name to lowercase and replaces spaces with underscores
60
+ def normalize_column_name
61
+ self.column_name = column_name.downcase.gsub(/\s+/, '_')
62
+ end
63
+
64
+ # Prevents deletion if the column is referenced as a foreign key in another table
65
+ def check_foreign_key_dependencies
66
+ table_name = db_design_dynamic_table.table_name
67
+ return unless DbdocEngine::DbDesignDynamicColumnQueries.column_referenced_as_foreign_key?(table_name, column_name)
68
+
69
+ errors.add(:base, "Cannot delete column '#{column_name}' because it is referenced as a foreign key in another table.")
70
+ throw :abort
71
+ end
72
+
73
+ # Logs column creation in the changelog
74
+ def log_creation
75
+ table_name = db_design_dynamic_table.table_name
76
+ entity_name = "#{table_name}.#{column_name}"
77
+
78
+ DbdocEngine::DbDesignChangelogQueries.log_change(
79
+ DbDesignChangelog::ACTION_CREATE,
80
+ DbDesignChangelog::ENTITY_COLUMN,
81
+ entity_name,
82
+ "Added column '#{column_name}' to table '#{table_name}'",
83
+ created_by,
84
+ nil,
85
+ {
86
+ column_name: column_name,
87
+ physical_column_name: physical_column_name,
88
+ data_type: data_type,
89
+ length: length,
90
+ decimal_precision: decimal_precision,
91
+ is_primary_key: is_primary_key,
92
+ is_foreign_key: is_foreign_key,
93
+ foreign_table_name: foreign_table_name,
94
+ foreign_column_name: foreign_column_name,
95
+ relationship_type: relationship_type,
96
+ not_null: not_null,
97
+ default_value: default_value,
98
+ sample_value: sample_value,
99
+ description: description
100
+ }
101
+ )
102
+ end
103
+
104
+ # Logs updates to column attributes in the changelog
105
+ def log_update
106
+ changes_data = collect_attribute_changes
107
+ return unless changes_data[:changes_desc].any?
108
+
109
+ # Use updated_by from the associated dynamic_table instead of created_by
110
+ user_who_updated = db_design_dynamic_table&.updated_by || updated_by || created_by
111
+
112
+ DbdocEngine::DbDesignChangelogQueries.log_change(
113
+ DbDesignChangelog::ACTION_UPDATE,
114
+ DbDesignChangelog::ENTITY_COLUMN,
115
+ changes_data[:entity_name],
116
+ "Updated column '#{column_name}' in table '#{changes_data[:table_name]}': #{changes_data[:changes_desc].join(', ')}",
117
+ user_who_updated,
118
+ changes_data[:previous_values],
119
+ changes_data[:new_values]
120
+ )
121
+ end
122
+
123
+ # Collects changes to column attributes for logging
124
+ def collect_attribute_changes
125
+ table_name = db_design_dynamic_table.table_name
126
+ entity_name = "#{table_name}.#{column_name}"
127
+ changes_desc = []
128
+ previous_values = {}
129
+ new_values = {}
130
+
131
+ # Mapping attribute names to human-readable labels for logging
132
+ attribute_mappings.each do |attr, label|
133
+ next unless saved_change_to_attribute?(attr.to_s)
134
+
135
+ old_val, new_val = saved_change_to_attribute(attr.to_s)
136
+
137
+ # Format boolean values for better readability
138
+ if [true, false].include?(old_val)
139
+ old_val = old_val ? 'Yes' : 'No'
140
+ new_val = new_val ? 'Yes' : 'No'
141
+ end
142
+
143
+ changes_desc << "#{label} changed from '#{old_val}' to '#{new_val}'"
144
+ previous_values[attr] = old_val
145
+ new_values[attr] = new_val
146
+ end
147
+
148
+ {
149
+ table_name: table_name,
150
+ entity_name: entity_name,
151
+ changes_desc: changes_desc,
152
+ previous_values: previous_values,
153
+ new_values: new_values
154
+ }
155
+ end
156
+
157
+ # Define attribute mappings for logging
158
+ def attribute_mappings
159
+ {
160
+ column_name: 'name',
161
+ physical_column_name: 'physical name',
162
+ data_type: 'data type',
163
+ length: 'length',
164
+ decimal_precision: 'decimal_precision',
165
+ is_primary_key: 'primary key status',
166
+ is_foreign_key: 'foreign key status',
167
+ foreign_table_name: 'foreign table',
168
+ foreign_column_name: 'foreign column',
169
+ relationship_type: 'relationship type',
170
+ is_candidate_key: 'candidate key status',
171
+ is_unique_key: 'unique key status',
172
+ is_indexed: 'index status',
173
+ not_null: 'nullable status',
174
+ default_value: 'default value',
175
+ sample_value: 'sample value',
176
+ description: 'description'
177
+ }
178
+ end
179
+
180
+ # Logs column deletion in the changelog
181
+ def log_deletion
182
+ table_name = db_design_dynamic_table.table_name
183
+ entity_name = "#{table_name}.#{column_name}"
184
+
185
+ DbdocEngine::DbDesignChangelogQueries.log_change(
186
+ DbDesignChangelog::ACTION_DELETE,
187
+ DbDesignChangelog::ENTITY_COLUMN,
188
+ entity_name,
189
+ "Deleted column '#{column_name}' from table '#{table_name}'",
190
+ created_by,
191
+ {
192
+ column_name: column_name,
193
+ physical_column_name: physical_column_name,
194
+ data_type: data_type,
195
+ length: length,
196
+ decimal_precision: decimal_precision,
197
+ is_primary_key: is_primary_key,
198
+ is_foreign_key: is_foreign_key,
199
+ foreign_table_name: foreign_table_name,
200
+ foreign_column_name: foreign_column_name,
201
+ relationship_type: relationship_type,
202
+ not_null: not_null,
203
+ default_value: default_value,
204
+ sample_value: sample_value,
205
+ description: description
206
+ },
207
+ nil
208
+ )
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,124 @@
1
+ # Model for dynamic tables
2
+ module DbdocEngine
3
+ class DbDesignDynamicTable < ApplicationRecord
4
+ # Include concern to support soft deletion functionality
5
+ include DbdocEngine::Concerns::SoftDeletable
6
+ self.table_name = 'db_design_dynamic_tables'
7
+ # Associations
8
+ belongs_to :db_design_table_group
9
+ has_many :db_design_dynamic_columns, dependent: :destroy
10
+
11
+ # Validations
12
+ validates :table_name, presence: true, uniqueness: { case_sensitive: false },
13
+ format: { with: /\A[a-z0-9_]+\z/ } # Only lowercase and underscores allowed
14
+ validates :created_by, presence: true
15
+ validates :physical_table_name, presence: true
16
+ validates :db_design_table_group_id, presence: true
17
+
18
+ # Accept nested attributes for associated models
19
+ accepts_nested_attributes_for :db_design_dynamic_columns, allow_destroy: true
20
+
21
+ # Lifecycle callbacks
22
+ before_save :normalize_table_name
23
+ before_destroy :prevent_deletion_if_referenced
24
+ before_destroy :log_deletion
25
+ after_create :log_creation
26
+ after_update :log_update
27
+
28
+ # Scope to exclude soft-deleted records
29
+ default_scope { without_deleted }
30
+
31
+ private
32
+
33
+ # Normalize the table name: downcase, remove extra spaces, convert spaces to underscores
34
+ def normalize_table_name
35
+ self.table_name = table_name.downcase.strip.gsub(/\s+/, '_').gsub(/_{2,}/, '_')
36
+ end
37
+
38
+ # Prevent deletion if this table is referenced by any foreign key in another table
39
+ def prevent_deletion_if_referenced
40
+ DbDesignDynamicColumn.joins(:referencing_relations).where(db_design_dynamic_table_id: id)
41
+ # NOTE: You might want to add a condition to actually abort deletion with an error here.
42
+ end
43
+
44
+ # Log creation of a new dynamic table
45
+ def log_creation
46
+ DbdocEngine::DbDesignChangelogQueries.log_change(
47
+ DbDesignChangelog::ACTION_CREATE,
48
+ DbDesignChangelog::ENTITY_TABLE,
49
+ table_name,
50
+ "Created new table '#{table_name}'",
51
+ created_by,
52
+ nil,
53
+ {
54
+ table_name: table_name,
55
+ group_id: db_design_table_group_id,
56
+ description: description
57
+ }
58
+ )
59
+ end
60
+
61
+ # Log updates with a summary of changes
62
+ def log_update
63
+ changes_desc = []
64
+ previous_values = {}
65
+ new_values = {}
66
+
67
+ # Track table name change
68
+ if saved_change_to_table_name?
69
+ old_name, new_name = saved_change_to_table_name
70
+ changes_desc << "name changed from '#{old_name}' to '#{new_name}'"
71
+ previous_values[:table_name] = old_name
72
+ new_values[:table_name] = new_name
73
+ end
74
+
75
+ # Track table group change
76
+ if saved_change_to_db_design_table_group_id?
77
+ old_group_id, new_group_id = saved_change_to_db_design_table_group_id
78
+ old_group = DbdocEngine::DbDesignTableGroupQueries.find_group_name(old_group_id) || 'unknown'
79
+ new_group = DbdocEngine::DbDesignTableGroupQueries.find_group_name(new_group_id) || 'unknown'
80
+ changes_desc << "group changed from '#{old_group}' to '#{new_group}'"
81
+ previous_values[:group_id] = old_group_id
82
+ new_values[:group_id] = new_group_id
83
+ end
84
+
85
+ # Track description change
86
+ if saved_change_to_description?
87
+ old_desc, new_desc = saved_change_to_description
88
+ changes_desc << 'description updated'
89
+ previous_values[:description] = old_desc
90
+ new_values[:description] = new_desc
91
+ end
92
+
93
+ # Only log if there are changes
94
+ return unless changes_desc.any?
95
+
96
+ DbdocEngine::DbDesignChangelogQueries.log_change(
97
+ DbDesignChangelog::ACTION_UPDATE,
98
+ DbDesignChangelog::ENTITY_TABLE,
99
+ table_name,
100
+ "Updated table '#{table_name}': #{changes_desc.join(', ')}",
101
+ updated_by,
102
+ previous_values,
103
+ new_values
104
+ )
105
+ end
106
+
107
+ # Log deletion of the dynamic table
108
+ def log_deletion
109
+ DbdocEngine::DbDesignChangelogQueries.log_change(
110
+ DbDesignChangelog::ACTION_DELETE,
111
+ DbDesignChangelog::ENTITY_TABLE,
112
+ table_name,
113
+ "Deleted table '#{table_name}'",
114
+ updated_by,
115
+ {
116
+ table_name: table_name,
117
+ group_id: db_design_table_group_id,
118
+ description: description
119
+ },
120
+ nil
121
+ )
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,88 @@
1
+ # Model representing a group of dynamic tables
2
+ module DbdocEngine
3
+ class DbDesignTableGroup < ApplicationRecord
4
+ # Include concern to enable soft deletion functionality
5
+ include DbdocEngine::Concerns::SoftDeletable
6
+ self.table_name = 'db_design_table_groups'
7
+ # Associations
8
+ has_many :db_design_dynamic_tables, dependent: :destroy # Deleting a group will delete all its associated tables
9
+
10
+ # Validations
11
+ validates :group_name, presence: true, uniqueness: true # Ensure unique group name
12
+ validates :group_color, presence: true # Group must have a color assigned
13
+ validates :created_by, presence: true, if: :new_record? # Track who created the group
14
+ validates :updated_by, presence: true, unless: :new_record?, on: :update
15
+
16
+ # Callbacks
17
+ after_create :log_creation # Log group creation
18
+ after_update :log_update # Log group updates
19
+ before_destroy :log_deletion # Log group deletion before removing it
20
+
21
+ # Scope to exclude soft-deleted records from default queries
22
+ default_scope { without_deleted }
23
+
24
+ private
25
+
26
+ # Log the creation of a new table group
27
+ def log_creation
28
+ DbdocEngine::DbDesignChangelogQueries.log_change(
29
+ DbDesignChangelog::ACTION_CREATE,
30
+ DbDesignChangelog::ENTITY_TABLE_GROUP,
31
+ group_name,
32
+ "Created new table group '#{group_name}'",
33
+ created_by || updated_by || 'system', # Default to 'system' if no user info is available
34
+ nil, # No previous values for creation
35
+ { group_name: group_name, group_color: group_color } # New values being logged
36
+ )
37
+ end
38
+
39
+ # Log changes made to the table group
40
+ def log_update
41
+ changes_desc = [] # Store descriptions of changes
42
+ previous_values = {} # Store old values
43
+ new_values = {} # Store new values
44
+
45
+ # Track group name changes
46
+ if saved_change_to_group_name?
47
+ old_name, new_name = saved_change_to_group_name
48
+ changes_desc << "name changed from '#{old_name}' to '#{new_name}'"
49
+ previous_values[:group_name] = old_name
50
+ new_values[:group_name] = new_name
51
+ end
52
+
53
+ # Track group color changes
54
+ if saved_change_to_group_color?
55
+ old_color, new_color = saved_change_to_group_color
56
+ changes_desc << "color changed from '#{old_color}' to '#{new_color}'"
57
+ previous_values[:group_color] = old_color
58
+ new_values[:group_color] = new_color
59
+ end
60
+
61
+ # Only log updates if there are actual changes
62
+ return unless changes_desc.any?
63
+
64
+ DbdocEngine::DbDesignChangelogQueries.log_change(
65
+ DbDesignChangelog::ACTION_UPDATE,
66
+ DbDesignChangelog::ENTITY_TABLE_GROUP,
67
+ group_name,
68
+ "Updated table group '#{group_name}': #{changes_desc.join(', ')}",
69
+ updated_by || 'system',
70
+ previous_values,
71
+ new_values
72
+ )
73
+ end
74
+
75
+ # Log deletion of a table group
76
+ def log_deletion
77
+ DbdocEngine::DbDesignChangelogQueries.log_change(
78
+ DbDesignChangelog::ACTION_DELETE,
79
+ DbDesignChangelog::ENTITY_TABLE_GROUP,
80
+ group_name,
81
+ "Deleted table group '#{group_name}'",
82
+ updated_by || 'system',
83
+ { group_name: group_name, group_color: group_color }, # Old values being removed
84
+ nil # No new values after deletion
85
+ )
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,21 @@
1
+ module DbdocEngine
2
+ class User < ApplicationRecord
3
+ self.table_name = "dbdoc_engine_users"
4
+ has_secure_password
5
+
6
+ validates :username, presence: true, uniqueness: true
7
+ validates :role, presence: true, inclusion: { in: %w[user admin superadmin] }
8
+
9
+ def admin?
10
+ role == "admin"
11
+ end
12
+
13
+ def superadmin?
14
+ role == "superadmin"
15
+ end
16
+
17
+ def admin_or_higher?
18
+ admin? || superadmin?
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,71 @@
1
+ # app/queries/admin_dashboard_queries.rb
2
+
3
+ # Service class for handling admin dashboard statistics and data formatting
4
+ module DbdocEngine
5
+ class AdminDashboardQueries
6
+ class << self
7
+ # Get total count of table groups
8
+ # @return [Integer] Number of existing table groups
9
+ def groups_count
10
+ DbDesignTableGroup.count
11
+ end
12
+
13
+ # Get total count of dynamic tables
14
+ # @return [Integer] Number of existing tables
15
+ def tables_count
16
+ DbDesignDynamicTable.count
17
+ end
18
+
19
+ # Get total count of columns across all tables
20
+ # @return [Integer] Number of existing columns
21
+ def columns_count
22
+ DbDesignDynamicColumn.count
23
+ end
24
+
25
+ # Get filtered changelogs for audit history
26
+ # @param params [Hash] Filter parameters:
27
+ # - :action_type (optional) Filter by specific action type
28
+ # @return [ActiveRecord::Relation] Filtered changelogs ordered by timestamp
29
+ def filtered_changelogs(params)
30
+ scope = DbDesignChangelog.order(change_timestamp: :desc)
31
+ scope = scope.where(action_type: params[:action_type]) if params[:action_type].present?
32
+ scope
33
+ end
34
+
35
+ # Format changelog data for chart visualization
36
+ # @param changelogs [Array<DbDesignChangelog>] Collection of changelog records
37
+ # @return [Hash] Structured data for chart rendering with:
38
+ # - labels: Chart category labels
39
+ # - datasets: Chart data series with colors and values
40
+ def prepare_chart_data(changelogs)
41
+ # Define entity types for chart categorization
42
+ entities = %w[table_group table column]
43
+
44
+ # Extract unique action types from changelogs
45
+ action_types = changelogs.pluck(:action_type).uniq
46
+
47
+ # Build datasets for each action type
48
+ datasets = action_types.map do |action|
49
+ {
50
+ label: action.titleize,
51
+ # Count occurrences per entity type for this action
52
+ data: entities.map { |e| changelogs.count { |log| log.action_type == action && log.entity_type == e } },
53
+ # Assign color based on action type
54
+ backgroundColor: case action
55
+ when 'create' then '#4CAF50' # Green
56
+ when 'update' then '#FFC107' # Amber
57
+ when 'delete' then '#F44336' # Red
58
+ else '#CCCCCC' # Default gray
59
+ end
60
+ }
61
+ end
62
+
63
+ # Return structured chart data
64
+ {
65
+ labels: %w[Groups Tables Columns], # X-axis labels
66
+ datasets: datasets # Data series collection
67
+ }
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,68 @@
1
+ # queries/db_design_changelog_queries.rb
2
+
3
+ # Query class handling database operations for change logs
4
+ module DbdocEngine
5
+ class DbDesignChangelogQueries
6
+ class << self
7
+ # Fetch filtered changelogs based on provided parameters
8
+ # @param params [Hash] Filter parameters including:
9
+ # - entity_type: Filter by entity type (table_group/table/column)
10
+ # - entity_name: Partial match for entity name
11
+ # - action_type: Filter by action type (create/update/delete)
12
+ # - date_from: Start date filter
13
+ # - date_to: End date filter
14
+ # @return [ActiveRecord::Relation] Filtered and ordered changelogs
15
+ def filtered_changelogs(params = {})
16
+ scope = DbDesignChangelog.all
17
+
18
+ scope = scope.where(entity_type: params[:entity_type]) if params[:entity_type].present?
19
+ scope = scope.where('entity_name ILIKE ?', "%#{params[:entity_name]}%") if params[:entity_name].present?
20
+ scope = scope.where(action_type: params[:action_type]) if params[:action_type].present?
21
+
22
+ if params[:date_from].present? && params[:date_to].present?
23
+ from_date = Date.parse(params[:date_from]).beginning_of_day
24
+ to_date = Date.parse(params[:date_to]).end_of_day
25
+ scope = scope.where(change_timestamp: from_date..to_date)
26
+ end
27
+
28
+ scope.order(change_timestamp: :desc)
29
+ end
30
+
31
+ # Find a specific changelog entry by ID
32
+ # @param id [Integer] Changelog ID to find
33
+ # @return [DbDesignChangelog, nil] Found changelog or nil
34
+ def find_changelog(id)
35
+ DbDesignChangelog.find_by(id: id)
36
+ end
37
+
38
+ # Create a new changelog entry with audit information
39
+ # @param action_type [String] Type of action performed
40
+ # @param entity_type [String] Type of entity affected
41
+ # @param entity_name [String] Name of the affected entity
42
+ # @param description [String] Human-readable change description
43
+ # @param changed_by [String] Identifier of who made the change
44
+ # @param previous_values [Hash] Optional previous state values
45
+ # @param new_values [Hash] Optional new state values
46
+ # @return [DbDesignChangelog] Newly created changelog record
47
+ def log_change(action_type, entity_type, entity_name, description, changed_by, previous_values = nil, new_values = nil)
48
+ DbDesignChangelog.create!(
49
+ change_timestamp: Time.current,
50
+ action_type: action_type,
51
+ entity_type: entity_type,
52
+ entity_name: entity_name,
53
+ description: description,
54
+ changed_by: changed_by,
55
+ previous_values: previous_values,
56
+ new_values: new_values
57
+ )
58
+ end
59
+
60
+ # Get group name by ID through TableGroupQueries
61
+ # @param id [Integer] Group ID to lookup
62
+ # @return [String, nil] Group name or nil if not found
63
+ def find_group_name(id)
64
+ DbDesignTableGroupQueries.find_group_name(id)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,37 @@
1
+ # queries/db_design_dynamic_column_queries.rb
2
+
3
+ # Query class for dynamic column-related database operations
4
+ module DbdocEngine
5
+ class DbDesignDynamicColumnQueries
6
+ # Returns the DbDesignDynamicColumn model (centralized reference)
7
+ def self.model
8
+ DbDesignDynamicColumn
9
+ end
10
+
11
+ class << self
12
+ # Counts the number of columns in the given table.
13
+ # Uses .size to leverage eager-loaded associations when available,
14
+ # avoiding N+1 COUNT queries in loops.
15
+ def count_columns_in_table(table)
16
+ table.db_design_dynamic_columns.size
17
+ end
18
+
19
+ # Checks if a column is referenced as a foreign key in any (non-deleted) table
20
+ def column_referenced_as_foreign_key?(table_name, column_name)
21
+ # Joins with parent table, filters by foreign key reference and non-deleted tables
22
+ model.joins(:db_design_dynamic_table)
23
+ .where(
24
+ foreign_table_name: table_name,
25
+ foreign_column_name: column_name,
26
+ db_design_dynamic_tables: { deleted_at: nil }
27
+ )
28
+ .exists?
29
+ end
30
+
31
+ def all_foreign_keys_with_tables
32
+ model.joins(:db_design_dynamic_table)
33
+ .where(is_foreign_key: true, db_design_dynamic_tables: { deleted_at: nil })
34
+ end
35
+ end
36
+ end
37
+ end