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,106 @@
1
+ # queries/db_design_dynamic_table_commands.rb
2
+
3
+ # Service class for handling complex operations related to dynamic tables and their columns
4
+ # Uses transactions to ensure atomicity of operations
5
+ module DbdocEngine
6
+ class DbDesignDynamicTableCommands
7
+ class << self
8
+ # Creates a new dynamic table with associated columns in a transaction
9
+ # @param params [Hash] Table parameters including nested columns attributes
10
+ # @return [Hash] Result hash with :success status and either the table or errors
11
+ def create_with_columns(params)
12
+ table = DbDesignDynamicTable.new(params)
13
+
14
+ # Important: Validate the table OUTSIDE the transaction first
15
+ # This ensures uniqueness validations run before the transaction starts
16
+ unless table.valid?
17
+ return { success: false, errors: collect_errors(table) }
18
+ end
19
+
20
+ begin
21
+ # Wrap operations in transaction to ensure all-or-nothing behavior
22
+ ActiveRecord::Base.transaction do
23
+ # Now validate columns too
24
+ if valid_columns?(table)
25
+ table.save!
26
+ return { success: true, table: table }
27
+ end
28
+ # Rollback transaction if validations fail
29
+ raise ActiveRecord::Rollback
30
+ end
31
+ rescue ActiveRecord::RecordInvalid
32
+ # This would catch any exceptions if save! fails
33
+ end
34
+
35
+ # Return failure status with collected errors
36
+ { success: false, errors: collect_errors(table) }
37
+ end
38
+
39
+ # Updates an existing dynamic table and its columns in a transaction
40
+ # @param table [DbDesignDynamicTable] Existing table to update
41
+ # @param params [Hash] New parameters including nested columns attributes
42
+ # @return [Hash] Result hash with :success status and either the table or errors
43
+ def update_with_columns(table, params)
44
+ table.assign_attributes(params)
45
+
46
+ # Validate table outside transaction first
47
+ unless table.valid?
48
+ return { success: false, errors: collect_errors(table) }
49
+ end
50
+
51
+ # Transaction block for atomic updates
52
+ ActiveRecord::Base.transaction do
53
+ # Now validate columns
54
+ if valid_columns?(table)
55
+ table.save!
56
+ return { success: true, table: table }
57
+ end
58
+
59
+ # Collect errors and rollback if validations fail
60
+ raise ActiveRecord::Rollback
61
+ end
62
+
63
+ # Return failure status with collected errors
64
+ { success: false, errors: collect_errors(table) }
65
+ end
66
+
67
+ private
68
+
69
+ # Validates table attributes
70
+ # @param table [DbDesignDynamicTable] Table instance to validate
71
+ # @return [Boolean] Validation status
72
+ def valid_table?(table)
73
+ table.valid?
74
+ end
75
+
76
+ # Validates all associated columns
77
+ # @param table [DbDesignDynamicTable] Table instance with columns to validate
78
+ # @return [Boolean] Overall validation status of all columns
79
+ def valid_columns?(table)
80
+ table.db_design_dynamic_columns.all?(&:valid?)
81
+ end
82
+
83
+ # Aggregates validation errors from table and columns into a single hash
84
+ # @param table [DbDesignDynamicTable] Table instance with errors
85
+ # @return [Hash] Structured error messages with keys indicating error sources
86
+ def collect_errors(table)
87
+ errors = {}
88
+
89
+ # Collect table-level errors
90
+ table.errors.each do |error|
91
+ errors[error.attribute] = error.message
92
+ end
93
+
94
+ # Collect column errors with indexed keys to prevent overwrites
95
+ table.db_design_dynamic_columns.each_with_index do |col, idx|
96
+ col.errors.each do |error|
97
+ # Format: "column_0_columnname" for first column's name error
98
+ errors["column#{idx}_#{error.attribute}"] = error.message
99
+ end
100
+ end
101
+
102
+ errors
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,194 @@
1
+ # queries/db_design_dynamic_table_queries.rb
2
+
3
+ # Query class handling database operations for dynamic tables and their relationships
4
+ module DbdocEngine
5
+ class DbDesignDynamicTableQueries
6
+ # Associations to eager load with table queries
7
+ INCLUDES_ASSOCIATIONS = %i[db_design_dynamic_columns db_design_table_group].freeze
8
+
9
+ class << self
10
+ # Centralized access to the DynamicTable model class
11
+ # @return [Class] DbDesignDynamicTable model class
12
+ def model
13
+ DbDesignDynamicTable
14
+ end
15
+
16
+ # Count tables belonging to a specific group
17
+ # @param group [DbDesignTableGroup] Group to count tables for
18
+ # @return [Integer] Number of tables in the group
19
+ def count_tables_in_group(group, include_deleted = false)
20
+ if include_deleted
21
+ group.db_design_dynamic_tables.with_deleted.size
22
+ else
23
+ group.db_design_dynamic_tables.size
24
+ end
25
+ end
26
+
27
+ # Find a table by ID with eager-loaded columns and group
28
+ # @param id [Integer] Table ID to find
29
+ # @return [DbDesignDynamicTable, nil] Found table or nil
30
+ def find_table_with_columns_and_group(id)
31
+ model.includes(INCLUDES_ASSOCIATIONS).find_by(id: id)
32
+ end
33
+
34
+ # Get all active tables with their columns and groups
35
+ # @return [ActiveRecord::Relation] Collection of tables with associations
36
+ def all_tables_with_columns_and_groups
37
+ model.includes(INCLUDES_ASSOCIATIONS)
38
+ end
39
+
40
+ # Ordered table groups with associated tables
41
+ def all_tables_ordered_by_group
42
+ model.includes(INCLUDES_ASSOCIATIONS)
43
+ .joins(:db_design_table_group)
44
+ .order(Arel.sql('LOWER(db_design_table_groups.group_name) ASC,
45
+ LOWER(db_design_dynamic_tables.table_name) ASC'))
46
+ end
47
+
48
+ # Get soft-deleted tables with their associations
49
+ # @return [ActiveRecord::Relation] Collection of deleted tables
50
+ def all_deleted_tables_with_columns_and_groups
51
+ model.with_deleted.only_deleted.includes(INCLUDES_ASSOCIATIONS)
52
+ end
53
+
54
+ # Count all active tables
55
+ # @return [Integer] Total active table count
56
+ def count_all_tables
57
+ model.count
58
+ end
59
+
60
+ # Count all soft-deleted tables
61
+ # @return [Integer] Total deleted table count
62
+ def count_all_deleted_tables
63
+ model.with_deleted.only_deleted.count
64
+ end
65
+
66
+ # Find table including soft-deleted records
67
+ # @param id [Integer] Table ID to find
68
+ # @return [DbDesignDynamicTable] Table with deleted records
69
+ def find_table_with_deleted(id)
70
+ model.with_deleted.find(id)
71
+ end
72
+
73
+ # Get all tables except specified ID
74
+ # @param id [Integer] Table ID to exclude
75
+ # @return [ActiveRecord::Relation] Tables excluding specified ID
76
+ def find_all_except(id)
77
+ model.where.not(id: id)
78
+ end
79
+
80
+ # Find table by its name
81
+ # @param table_name [String] Name to search for
82
+ # @return [DbDesignDynamicTable, nil] Matching table or nil
83
+ def find_by_table_name(table_name)
84
+ model.find_by(table_name: table_name)
85
+ end
86
+
87
+ # Check if a column is referenced as foreign key
88
+ # @param table_name [String] Source table name
89
+ # @param column_name [String] Column name to check
90
+ # @param only_active [Boolean] Only check active tables
91
+ # @return [Boolean] Reference existence status
92
+ def check_column_is_referenced(table_name, column_name, only_active: false)
93
+ query = DbDesignDynamicColumn.where(
94
+ foreign_table_name: table_name,
95
+ foreign_column_name: column_name
96
+ )
97
+
98
+ query = active_tables_scope(query) if only_active
99
+ query.exists?
100
+ end
101
+
102
+ # Get group name for a table group ID
103
+ # @param group_id [Integer] Group ID to lookup
104
+ # @return [String, nil] Group name or nil
105
+ def find_table_group_name(group_id)
106
+ DbDesignTableGroupQueries.find_group_name(group_id)
107
+ end
108
+
109
+ # Get table name by ID
110
+ # @param table_id [Integer] Table ID to lookup
111
+ # @return [String, nil] Table name or nil
112
+ def find_table_name(table_id)
113
+ model.find_by(id: table_id)&.table_name
114
+ end
115
+
116
+ # Check if group contains active/non-deleted tables
117
+ # @param group_id [Integer] Group ID to check
118
+ # @param include_deleted [Boolean] Include deleted tables in check
119
+ # @return [Boolean] Existence status
120
+ def group_has_active_tables?(group_id, include_deleted = false)
121
+ query = model.where(db_design_table_group_id: group_id)
122
+ query = include_deleted ? query.with_deleted : query.without_deleted
123
+ query.exists?
124
+ end
125
+
126
+ # Find table with eager-loaded group information
127
+ # @param id [Integer] Table ID to find
128
+ # @return [DbDesignDynamicTable] Table with group association
129
+ def find_table_with_group(id)
130
+ model.includes(:db_design_table_group).find(id)
131
+ end
132
+
133
+ # Filter relation by group ID
134
+ # @param relation [ActiveRecord::Relation] Base query to filter
135
+ # @param group_id [Integer] Group ID to filter by
136
+ # @return [ActiveRecord::Relation] Filtered query
137
+ def filter_by_group(relation, group_id)
138
+ relation.where(db_design_table_group_id: group_id)
139
+ end
140
+
141
+ # Search tables by name or group name
142
+ # @param relation [ActiveRecord::Relation] Base query to search
143
+ # @param search_term [String] Term to search for
144
+ # @return [ActiveRecord::Relation] Modified query with search filters
145
+ def search_tables(relation, search_term)
146
+ return relation if search_term.blank?
147
+
148
+ sanitized_term = "%#{search_term.downcase}%"
149
+ relation.left_joins(:db_design_table_group)
150
+ .where("LOWER(db_design_dynamic_tables.table_name) LIKE :term OR LOWER(db_design_table_groups.group_name) LIKE :term",
151
+ term: sanitized_term)
152
+ end
153
+
154
+ def active_tables_with_columns
155
+ model.where(deleted_at: nil)
156
+ .where(
157
+ "EXISTS (SELECT 1 FROM db_design_dynamic_columns " \
158
+ "WHERE db_design_dynamic_columns.db_design_dynamic_table_id = db_design_dynamic_tables.id)"
159
+ )
160
+ end
161
+
162
+ def table_exists?(table_name)
163
+ model.exists?(table_name: table_name, deleted_at: nil)
164
+ end
165
+
166
+ private
167
+
168
+ # Scope query to active tables only
169
+ # @param query [ActiveRecord::Relation] Query to modify
170
+ # @return [ActiveRecord::Relation] Scoped query
171
+ def active_tables_scope(query)
172
+ query.joins(:db_design_dynamic_table)
173
+ .where(db_design_dynamic_tables: { deleted_at: nil })
174
+ end
175
+
176
+ # Check for table references (private implementation)
177
+ # @param table_id [Integer] Table ID to check
178
+ # @return [Boolean] Reference existence status
179
+ def check_table_references(_table_id)
180
+ DbDesignDynamicColumn
181
+ .joins(:db_design_dynamic_table)
182
+ .where(foreign_table_name: table.table_name)
183
+ .exists?
184
+ end
185
+
186
+ # Find table with columns and group (private implementation)
187
+ # @param id [Integer] Table ID to find
188
+ # @return [DbDesignDynamicTable, nil] Found table or nil
189
+ def find_with_columns_and_group(id)
190
+ model.includes(INCLUDES_ASSOCIATIONS).find_by(id: id)
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,154 @@
1
+ # queries/db_design_table_group_queries.rb
2
+
3
+ # Query class handling database operations for table groups and their associations
4
+ module DbdocEngine
5
+ class DbDesignTableGroupQueries
6
+ # Eager loading configuration for tables and columns associations
7
+ TABLE_INCLUDES = { db_design_dynamic_tables: :db_design_dynamic_columns }.freeze
8
+
9
+ class << self
10
+ # Centralized model reference for table groups
11
+ # @return [Class] DbDesignTableGroup model class
12
+ def model
13
+ DbDesignTableGroup
14
+ end
15
+
16
+ # Fetch all table groups with their associated tables and columns
17
+ # @return [ActiveRecord::Relation] Collection of groups with eager-loaded associations
18
+ def all_groups_with_tables_and_columns
19
+ model.includes(TABLE_INCLUDES).all
20
+ end
21
+
22
+ # Find a specific group by ID with preloaded tables and columns
23
+ # @param id [Integer] Group ID to find
24
+ # @return [DbDesignTableGroup, nil] Found group or nil
25
+ def find_group_with_tables_and_columns(id)
26
+ model.includes(TABLE_INCLUDES).find_by(id: id)
27
+ end
28
+
29
+ # Count all active (non-deleted) table groups
30
+ # @return [Integer] Total count of active groups
31
+ def count_all_groups
32
+ model.count
33
+ end
34
+
35
+ # Retrieve lightweight group data for selection lists
36
+ # @return [ActiveRecord::Relation] Collection of groups with only ID and name
37
+ def select_all_groups
38
+ model.select(:id, :group_name)
39
+ end
40
+
41
+ # Find a group including soft-deleted records
42
+ # @param id [Integer] Group ID to find
43
+ # @return [DbDesignTableGroup] Group including soft-deleted ones
44
+ def find_with_deleted(id)
45
+ model.with_deleted.find(id)
46
+ end
47
+
48
+ # Unified search across groups with optional deleted records
49
+ # @param search_term [String] Optional search string
50
+ # @param include_deleted [Boolean] Flag to include soft-deleted records
51
+ # @return [ActiveRecord::Relation] Filtered collection of groups
52
+ def search_all_groups(search_term = nil, include_deleted = false)
53
+ query = include_deleted ? model.with_deleted : model.all
54
+ query = apply_search_scope(query, search_term)
55
+ query.order(Arel.sql("LOWER(group_name) ASC")) # Add ordering here
56
+ end
57
+
58
+ # Search specifically in soft-deleted groups
59
+ # @param search_term [String] Optional search string
60
+ # @return [ActiveRecord::Relation] Filtered collection of deleted groups
61
+ def search_deleted_groups(search_term = nil)
62
+ apply_search_scope(model.only_deleted, search_term)
63
+ end
64
+
65
+ # Get group names excluding a specific entry
66
+ # @param excluded_id [Integer] ID to exclude from results
67
+ # @return [Array<String>] Array of group names
68
+ def group_names_except(excluded_id)
69
+ model.where.not(id: excluded_id).pluck(:group_name)
70
+ end
71
+
72
+ # Find group name by ID
73
+ # @param group_id [Integer] Group ID to lookup
74
+ # @return [String, nil] Group name or nil
75
+ def find_group_name(group_id)
76
+ model.find_by(id: group_id)&.group_name
77
+ end
78
+
79
+ # Check if a group name exists in active records
80
+ # @param group_name [String] Name to check for existence
81
+ # @return [Boolean] Existence status
82
+ def group_exists?(group_name)
83
+ model.without_deleted.where(group_name: group_name).exists?
84
+ end
85
+
86
+ # Create a new table group
87
+ # @param params [Hash] Group attributes
88
+ # @return [DbDesignTableGroup] New group instance (persisted if valid)
89
+ def create_table_group(params)
90
+ model.new(params).tap(&:save)
91
+ end
92
+
93
+ # Update existing table group
94
+ # @param table_group [DbDesignTableGroup] Group instance to update
95
+ # @param params [Hash] New attributes
96
+ # @return [Boolean] Update success status
97
+ def update_table_group(table_group, params)
98
+ table_group.update(params)
99
+ end
100
+
101
+ # Soft delete a table group
102
+ # @param table_group [DbDesignTableGroup] Group to mark as deleted
103
+ # @return [Boolean] Deletion success status
104
+ def soft_delete(table_group)
105
+ table_group.soft_delete
106
+ end
107
+
108
+ # Restore a soft-deleted group
109
+ # @param table_group [DbDesignTableGroup] Group to restore
110
+ # @return [Boolean] Restoration success status
111
+ def restore(table_group)
112
+ table_group.restore
113
+ end
114
+
115
+ # Permanently delete a group
116
+ # @param table_group [DbDesignTableGroup] Group to destroy
117
+ # @return [Boolean] Deletion success status
118
+ def permanent_delete(table_group)
119
+ table_group.destroy
120
+ end
121
+
122
+ # Get all groups for selection dropdowns
123
+ # @return [ActiveRecord::Relation] Ordered list of groups with ID and name
124
+ def all_groups_for_selection
125
+ model.select(:id, :group_name).order(:group_name)
126
+ end
127
+
128
+ # Find group with associated tables
129
+ # @param group_id [Integer] Group ID to find
130
+ # @return [DbDesignTableGroup] Group with associated tables
131
+ def find_group_with_tables(group_id)
132
+ model.includes(:db_design_dynamic_tables).find(group_id)
133
+ end
134
+
135
+ def groups_with_tables_having_columns
136
+ model.includes(db_design_dynamic_tables: :db_design_dynamic_columns)
137
+ .references(:db_design_dynamic_tables)
138
+ .where(db_design_dynamic_tables: { deleted_at: nil })
139
+ end
140
+
141
+ private
142
+
143
+ # Internal search scope handler
144
+ # @param query [ActiveRecord::Relation] Base query to modify
145
+ # @param search_term [String] Search string for filtering
146
+ # @return [ActiveRecord::Relation] Modified query with search filters
147
+ def apply_search_scope(query, search_term)
148
+ return query if search_term.blank?
149
+
150
+ query.where("group_name ILIKE ?", "%#{search_term.strip}%")
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,38 @@
1
+ # app/services/db_design_dynamic_table_export_service.rb
2
+ module DbdocEngine
3
+ class DbDesignDynamicTableExportService
4
+ def initialize(params, controller)
5
+ @params = params # Parameters passed from the controller
6
+ @controller = controller # Reference to the controller instance
7
+ end
8
+
9
+ # Handles export of a single table to Excel
10
+ def export_single
11
+ table = DbdocEngine::DbDesignDynamicTableQueries.find_table_with_deleted(@params[:id]) # Find table by ID (including soft-deleted)
12
+ columns = table.db_design_dynamic_columns # Get all columns for the table
13
+
14
+ @controller.instance_variable_set(:@table, table) # Set instance variable for table
15
+ @controller.instance_variable_set(:@columns, columns) # Set instance variable for columns
16
+
17
+ @controller.respond_to do |format| # Prepare Excel response
18
+ format.xlsx do
19
+ filename = "#{table.table_name.parameterize}_details.xlsx" # Set download filename
20
+ @controller.response.headers["Content-Disposition"] = "attachment; filename=#{filename}"
21
+ end
22
+ end
23
+ end
24
+
25
+ # Handles export of all tables to Excel
26
+ def export_all
27
+ tables = DbdocEngine::DbDesignDynamicTableQueries.all_tables_with_columns_and_groups # Get all tables with columns and groups
28
+ @controller.instance_variable_set(:@tables, tables) # Set instance variable for use in view
29
+
30
+ @controller.respond_to do |format| # Prepare Excel response
31
+ format.xlsx do
32
+ filename = "all_tables_details_#{Date.today.strftime('%Y%m%d')}.xlsx" # Set download filename with today's date
33
+ @controller.response.headers["Content-Disposition"] = "attachment; filename=#{filename}"
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,49 @@
1
+ # Handles create and update operations for DbDesignDynamicTable
2
+ module DbdocEngine
3
+ class DbDesignDynamicTableHandlerService
4
+ def initialize(controller_context, table_params, table = nil)
5
+ @controller = controller_context # Reference to the controller instance
6
+ @table_params = table_params # Table parameters from the controller
7
+ @table = table # Existing table object (optional, for update)
8
+ end
9
+
10
+ # Handles table creation operation
11
+ def create
12
+ result = service.create_table # Call service to create the table
13
+
14
+ if result[:success]
15
+ @controller.flash[:notice] = I18n.t("dbdoc.table_creation") # Success message
16
+ @controller.redirect_to @controller.admin_db_design_dynamic_tables_path # Redirect to the table list
17
+ else
18
+ handle_failure(result, :new) # Handle failure (render 'new' view)
19
+ end
20
+ end
21
+
22
+ # Handles table update operation
23
+ def update
24
+ result = service.update_table(@table) # Call service to update the table
25
+
26
+ if result[:success]
27
+ @controller.flash[:notice] = I18n.t("dbdoc.table_updation") # Success message
28
+ @controller.redirect_to @controller.admin_db_design_dynamic_tables_path # Redirect to the table list
29
+ else
30
+ handle_failure(result, :edit) # Handle failure (render 'edit' view)
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ # Initializes and caches the service object for table operations
37
+ def service
38
+ @service ||= DbDesignDynamicTablesService.new(@table_params) # Instantiate the service with table parameters
39
+ end
40
+
41
+ # Handles failure scenario (render the appropriate view with errors)
42
+ def handle_failure(result, render_view)
43
+ @controller.instance_variable_set(:@table, @table || DbDesignDynamicTable.new(@table_params)) # Set table instance
44
+ @controller.send(:load_other_tables) # Load other tables for reference in the view
45
+ @controller.send(:handle_errors, @controller.instance_variable_get(:@table), result) # Handle and display errors
46
+ @controller.render render_view, status: :unprocessable_entity # Render the appropriate view with errors
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,21 @@
1
+ # Service for managing dynamic table creation and updates.
2
+ # This service handles the creation and updating of dynamic tables,
3
+ # including the validation of table and nested column data,
4
+ # and ensures that all changes are committed within a transaction.
5
+ module DbdocEngine
6
+ class DbDesignDynamicTablesService
7
+ def initialize(params)
8
+ @params = params
9
+ end
10
+
11
+ # Create new table.
12
+ def create_table
13
+ DbDesignDynamicTableCommands.create_with_columns(@params)
14
+ end
15
+
16
+ # Update existing table.
17
+ def update_table(table)
18
+ DbDesignDynamicTableCommands.update_with_columns(table, @params)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,43 @@
1
+ # Service for handling errors in table operations
2
+ module DbdocEngine
3
+ class ErrorHandlerService
4
+ attr_reader :table, :result, :table_params
5
+
6
+ def initialize(table, result, table_params = nil)
7
+ @table = table
8
+ @result = result
9
+ @table_params = table_params
10
+ end
11
+
12
+ # Process errors and add them to the appropriate objects
13
+ def process_errors
14
+ return unless result[:errors].present?
15
+
16
+ result[:errors].each do |attribute, messages|
17
+ if attribute.to_s.start_with?("db_design_dynamic_columns.")
18
+ handle_column_error(attribute, messages)
19
+ else
20
+ handle_table_error(attribute, messages)
21
+ end
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ # Handle errors for specific columns
28
+ def handle_column_error(attribute, messages)
29
+ match = attribute.to_s.match(/db_design_dynamic_columns\.(\d+)/)
30
+ return unless match
31
+
32
+ index = match[1].to_i
33
+ return unless table.db_design_dynamic_columns[index]
34
+
35
+ table.db_design_dynamic_columns[index].errors.add(:base, messages)
36
+ end
37
+
38
+ # Handle errors for the table itself
39
+ def handle_table_error(attribute, messages)
40
+ Array(messages).each { |message| table.errors.add(attribute, message) }
41
+ end
42
+ end
43
+ end