cccux 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 (65) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +67 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +382 -0
  5. data/Rakefile +8 -0
  6. data/app/assets/config/cccux_manifest.js +1 -0
  7. data/app/assets/stylesheets/cccux/application.css +102 -0
  8. data/app/controllers/cccux/ability_permissions_controller.rb +271 -0
  9. data/app/controllers/cccux/application_controller.rb +37 -0
  10. data/app/controllers/cccux/authorization_controller.rb +10 -0
  11. data/app/controllers/cccux/cccux_controller.rb +64 -0
  12. data/app/controllers/cccux/dashboard_controller.rb +172 -0
  13. data/app/controllers/cccux/home_controller.rb +19 -0
  14. data/app/controllers/cccux/roles_controller.rb +290 -0
  15. data/app/controllers/cccux/simple_controller.rb +7 -0
  16. data/app/controllers/cccux/users_controller.rb +112 -0
  17. data/app/controllers/concerns/cccux/application_controller_concern.rb +32 -0
  18. data/app/helpers/cccux/application_helper.rb +4 -0
  19. data/app/helpers/cccux/authorization_helper.rb +228 -0
  20. data/app/jobs/cccux/application_job.rb +4 -0
  21. data/app/mailers/cccux/application_mailer.rb +6 -0
  22. data/app/models/cccux/ability.rb +142 -0
  23. data/app/models/cccux/ability_permission.rb +61 -0
  24. data/app/models/cccux/application_record.rb +5 -0
  25. data/app/models/cccux/role.rb +90 -0
  26. data/app/models/cccux/role_ability.rb +49 -0
  27. data/app/models/cccux/user_role.rb +42 -0
  28. data/app/models/concerns/cccux/authorizable.rb +25 -0
  29. data/app/models/concerns/cccux/scoped_ownership.rb +183 -0
  30. data/app/models/concerns/cccux/user_concern.rb +87 -0
  31. data/app/views/cccux/ability_permissions/edit.html.erb +58 -0
  32. data/app/views/cccux/ability_permissions/index.html.erb +108 -0
  33. data/app/views/cccux/ability_permissions/new.html.erb +308 -0
  34. data/app/views/cccux/dashboard/index.html.erb +69 -0
  35. data/app/views/cccux/dashboard/model_discovery.html.erb +148 -0
  36. data/app/views/cccux/home/index.html.erb +42 -0
  37. data/app/views/cccux/roles/_flash.html.erb +10 -0
  38. data/app/views/cccux/roles/_form.html.erb +78 -0
  39. data/app/views/cccux/roles/_role.html.erb +67 -0
  40. data/app/views/cccux/roles/edit.html.erb +317 -0
  41. data/app/views/cccux/roles/index.html.erb +51 -0
  42. data/app/views/cccux/roles/new.html.erb +3 -0
  43. data/app/views/cccux/roles/show.html.erb +99 -0
  44. data/app/views/cccux/users/edit.html.erb +117 -0
  45. data/app/views/cccux/users/index.html.erb +99 -0
  46. data/app/views/cccux/users/new.html.erb +94 -0
  47. data/app/views/cccux/users/show.html.erb +138 -0
  48. data/app/views/layouts/cccux/admin.html.erb +168 -0
  49. data/app/views/layouts/cccux/application.html.erb +17 -0
  50. data/app/views/shared/_footer.html.erb +101 -0
  51. data/config/routes.rb +63 -0
  52. data/db/migrate/20250626194001_create_cccux_roles.rb +15 -0
  53. data/db/migrate/20250626194007_create_cccux_ability_permissions.rb +18 -0
  54. data/db/migrate/20250626194011_create_cccux_user_roles.rb +13 -0
  55. data/db/migrate/20250626194016_create_cccux_role_abilities.rb +10 -0
  56. data/db/migrate/20250627170611_add_owned_to_cccux_role_abilities.rb +9 -0
  57. data/db/migrate/20250705193709_add_context_to_cccux_role_abilities.rb +9 -0
  58. data/db/migrate/20250706214415_add_ownership_configuration_to_role_abilities.rb +21 -0
  59. data/db/seeds.rb +136 -0
  60. data/lib/cccux/engine.rb +50 -0
  61. data/lib/cccux/version.rb +3 -0
  62. data/lib/cccux.rb +7 -0
  63. data/lib/tasks/cccux.rake +703 -0
  64. data/lib/tasks/view_helpers.rake +274 -0
  65. metadata +188 -0
@@ -0,0 +1,271 @@
1
+ module Cccux
2
+ class AbilityPermissionsController < CccuxController
3
+ # Ensure only Role Managers can access permission management
4
+ before_action :ensure_role_manager
5
+
6
+ before_action :set_ability_permission, only: [:show, :edit, :update, :destroy]
7
+
8
+ def index
9
+ @ability_permissions = Cccux::AbilityPermission.all.order(:subject, :action)
10
+ @grouped_permissions = @ability_permissions.group_by(&:subject)
11
+ end
12
+
13
+ def show
14
+ end
15
+
16
+ def new
17
+ @ability_permission = Cccux::AbilityPermission.new
18
+ @ability_permission.subject = params[:subject] if params[:subject].present?
19
+ @available_subjects = get_available_subjects
20
+ @available_actions = get_available_actions
21
+ @subject_actions_map = get_subject_actions_map
22
+ end
23
+
24
+ def create
25
+ # Handle bulk creation if actions is an array
26
+ if params[:ability_permission][:actions].is_a?(Array)
27
+ create_bulk_permissions
28
+ else
29
+ create_single_permission
30
+ end
31
+ end
32
+
33
+ def edit
34
+ @available_subjects = get_available_subjects
35
+ @available_actions = get_available_actions
36
+ @subject_actions_map = get_subject_actions_map
37
+ end
38
+
39
+ def update
40
+ if @ability_permission.update(ability_permission_params)
41
+ redirect_to cccux.ability_permissions_path, notice: 'Permission was successfully updated.'
42
+ else
43
+ @available_subjects = get_available_subjects
44
+ @available_actions = get_available_actions
45
+ @subject_actions_map = get_subject_actions_map
46
+ render :edit
47
+ end
48
+ end
49
+
50
+ def destroy
51
+ @ability_permission.destroy
52
+ redirect_to cccux.ability_permissions_path, notice: 'Permission was successfully deleted.'
53
+ end
54
+
55
+ def actions_for_subject
56
+ subject = params[:subject]
57
+ actions = get_actions_for_subject(subject)
58
+ render json: { actions: actions }
59
+ end
60
+
61
+ private
62
+
63
+ def set_ability_permission
64
+ @ability_permission = Cccux::AbilityPermission.find(params[:id])
65
+ end
66
+
67
+ def ability_permission_params
68
+ params.require(:ability_permission).permit(:subject, :action, :description, :active)
69
+ end
70
+
71
+ def bulk_permission_params
72
+ params.require(:ability_permission).permit(:subject, :description, :active, actions: [])
73
+ end
74
+
75
+ def create_bulk_permissions
76
+ subject = params[:ability_permission][:subject]
77
+ actions = params[:ability_permission][:actions].reject(&:blank?)
78
+ description_template = params[:ability_permission][:description]
79
+ active = params[:ability_permission][:active]
80
+
81
+ created_permissions = []
82
+ failed_permissions = []
83
+
84
+ actions.each do |action|
85
+ permission = Cccux::AbilityPermission.new(
86
+ subject: subject,
87
+ action: action,
88
+ description: description_template.present? ? "#{action.capitalize} #{subject.pluralize.downcase}" : "",
89
+ active: active
90
+ )
91
+
92
+ if permission.save
93
+ created_permissions << permission
94
+ else
95
+ failed_permissions << { action: action, errors: permission.errors.full_messages }
96
+ end
97
+ end
98
+
99
+ if failed_permissions.empty?
100
+ redirect_to cccux.ability_permissions_path,
101
+ notice: "Successfully created #{created_permissions.count} permissions for #{subject}!"
102
+ else
103
+ @ability_permission = Cccux::AbilityPermission.new(subject: subject)
104
+ @available_subjects = get_available_subjects
105
+ @available_actions = get_available_actions
106
+ @subject_actions_map = get_subject_actions_map
107
+ flash[:alert] = "Some permissions could not be created: #{failed_permissions.map { |f| f[:action] }.join(', ')}"
108
+ render :new
109
+ end
110
+ end
111
+
112
+ def create_single_permission
113
+ @ability_permission = Cccux::AbilityPermission.new(ability_permission_params)
114
+
115
+ if @ability_permission.save
116
+ redirect_to cccux.ability_permissions_path, notice: 'Permission was successfully created.'
117
+ else
118
+ @available_subjects = get_available_subjects
119
+ @available_actions = get_available_actions
120
+ @subject_actions_map = get_subject_actions_map
121
+ render :new
122
+ end
123
+ end
124
+
125
+ def get_available_subjects
126
+ # Get existing subjects plus discovered models
127
+ existing_subjects = Cccux::AbilityPermission.distinct.pluck(:subject).compact
128
+ discovered_models = discover_application_models
129
+ (existing_subjects + discovered_models).uniq.sort
130
+ end
131
+
132
+ def get_available_actions
133
+ # Get existing actions plus common CRUD actions
134
+ existing_actions = Cccux::AbilityPermission.distinct.pluck(:action).compact
135
+ common_actions = %w[read create update destroy manage index show new edit]
136
+ (existing_actions + common_actions).uniq.sort
137
+ end
138
+
139
+ def get_subject_actions_map
140
+ subjects = get_available_subjects
141
+ map = {}
142
+
143
+ subjects.each do |subject|
144
+ map[subject] = get_actions_for_subject(subject)
145
+ end
146
+
147
+ map
148
+ end
149
+
150
+ def get_actions_for_subject(subject)
151
+ return [] if subject.blank?
152
+
153
+ # Start with common CRUD actions
154
+ actions = %w[read create update destroy]
155
+
156
+ # Add route-discovered actions
157
+ route_actions = discover_actions_for_model(subject)
158
+ actions += route_actions
159
+
160
+ # Add existing actions for this subject
161
+ existing_actions = Cccux::AbilityPermission.where(subject: subject).distinct.pluck(:action).compact
162
+ actions += existing_actions
163
+
164
+ actions.uniq.sort
165
+ end
166
+
167
+ def discover_application_models
168
+ models = []
169
+
170
+ begin
171
+ Rails.application.eager_load!
172
+
173
+ # Get all ApplicationRecord descendants from the host app
174
+ ApplicationRecord.descendants.each do |model_class|
175
+ next if model_class.abstract_class?
176
+ next if model_class.name.nil?
177
+ next if skip_model?(model_class)
178
+
179
+ models << model_class.name
180
+ end
181
+
182
+ # Also add CCCUX engine models (so they can be managed through permissions)
183
+ cccux_models = %w[Cccux::Role Cccux::AbilityPermission Cccux::UserRole Cccux::RoleAbility]
184
+ models += cccux_models
185
+
186
+ rescue => e
187
+ Rails.logger.warn "Error discovering models: #{e.message}"
188
+ end
189
+
190
+ models.uniq.sort
191
+ end
192
+
193
+ def discover_actions_for_model(subject)
194
+ actions = []
195
+
196
+ begin
197
+ # Convert subject to potential route patterns
198
+ resource_name = subject.underscore.pluralize
199
+
200
+ # For CCCUX models, also check the engine routes
201
+ if subject.start_with?('Cccux::')
202
+ cccux_resource_name = subject.gsub('Cccux::', '').underscore.pluralize
203
+
204
+ # Look through CCCUX engine routes for this resource
205
+ Cccux::Engine.routes.routes.each do |route|
206
+ next unless route.path.spec.to_s.include?(cccux_resource_name)
207
+
208
+ # Extract action from route
209
+ if route.defaults[:action]
210
+ action = route.defaults[:action]
211
+ # Map HTTP verbs to standard actions and include custom actions
212
+ case action
213
+ when 'index' then actions << 'read'
214
+ when 'show' then actions << 'read'
215
+ when 'create' then actions << 'create'
216
+ when 'update' then actions << 'update'
217
+ when 'destroy' then actions << 'destroy'
218
+ when 'edit', 'new' then next # Skip these as they're UI actions
219
+ else
220
+ # Custom actions like 'reorder', 'toggle_active', etc.
221
+ actions << action
222
+ end
223
+ end
224
+ end
225
+ end
226
+
227
+ # Also look through main Rails routes for this resource
228
+ Rails.application.routes.routes.each do |route|
229
+ next unless route.path.spec.to_s.include?(resource_name)
230
+
231
+ # Extract action from route
232
+ if route.defaults[:action]
233
+ action = route.defaults[:action]
234
+ # Map HTTP verbs to standard actions and include custom actions
235
+ case action
236
+ when 'index' then actions << 'read'
237
+ when 'show' then actions << 'read'
238
+ when 'create' then actions << 'create'
239
+ when 'update' then actions << 'update'
240
+ when 'destroy' then actions << 'destroy'
241
+ when 'edit', 'new' then next # Skip these as they're UI actions
242
+ else
243
+ # Custom actions
244
+ actions << action
245
+ end
246
+ end
247
+ end
248
+
249
+ rescue => e
250
+ Rails.logger.warn "Error discovering actions for #{subject}: #{e.message}"
251
+ end
252
+
253
+ actions.uniq
254
+ end
255
+
256
+ def skip_model?(model_class)
257
+ excluded_patterns = [
258
+ /^ActiveRecord::/,
259
+ /^ActiveStorage::/,
260
+ /^ActionText::/,
261
+ /^ActionMailbox::/,
262
+ /^ApplicationRecord$/,
263
+ /Version$/,
264
+ /Schema/,
265
+ /Migration/
266
+ ]
267
+
268
+ excluded_patterns.any? { |pattern| model_class.name.match?(pattern) }
269
+ end
270
+ end
271
+ end
@@ -0,0 +1,37 @@
1
+ module Cccux
2
+ class ApplicationController < ActionController::Base
3
+ # CanCanCan authorization - shared across all CCCUX controllers
4
+ include CanCan::ControllerAdditions
5
+
6
+ # Handle CanCan authorization errors gracefully
7
+ rescue_from CanCan::AccessDenied do |exception|
8
+ redirect_to main_app.root_path, alert: 'Access denied.'
9
+ end
10
+
11
+ # Handle 404 errors gracefully
12
+ rescue_from ActiveRecord::RecordNotFound do |exception|
13
+ redirect_to main_app.root_path, alert: 'The requested resource was not found.'
14
+ end
15
+
16
+ before_action :configure_permitted_parameters, if: :devise_controller?
17
+ before_action :set_current_user
18
+
19
+ protected
20
+
21
+ # Override current_ability to use CCCUX Ability class
22
+ def current_ability
23
+ @current_ability ||= Cccux::Ability.new(current_user)
24
+ end
25
+
26
+ def set_current_user
27
+ @current_user = current_user if defined?(current_user)
28
+ end
29
+
30
+ private
31
+
32
+ def configure_permitted_parameters
33
+ devise_parameter_sanitizer.permit(:sign_up, keys: [:first_name, :last_name])
34
+ devise_parameter_sanitizer.permit(:account_update, keys: [:first_name, :last_name])
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,10 @@
1
+ module Cccux
2
+ class AuthorizationController < ApplicationController
3
+ # Provide CCCUX authorization without forcing a specific layout
4
+ # This allows host app controllers to use their own layouts
5
+ layout 'application'
6
+
7
+ # Automatically load and authorize resources for all actions
8
+ load_and_authorize_resource
9
+ end
10
+ end
@@ -0,0 +1,64 @@
1
+ module Cccux
2
+ class CccuxController < ApplicationController
3
+ layout 'cccux/admin'
4
+
5
+ # Override the default error message for admin interface
6
+ rescue_from CanCan::AccessDenied do |exception|
7
+ redirect_to main_app.root_path, alert: 'Access denied. Only Role Managers can access the admin interface.'
8
+ end
9
+
10
+ rescue_from ActionController::RoutingError do |exception|
11
+ redirect_to main_app.root_path, alert: 'The requested page was not found.'
12
+ end
13
+
14
+ # Handle parameter errors (like invalid IDs)
15
+ rescue_from ActionController::ParameterMissing do |exception|
16
+ redirect_to main_app.root_path, alert: 'Invalid request parameters.'
17
+ end
18
+
19
+ # Automatically load and authorize resources for all actions
20
+ # This works because CCCUX provides default roles (Guest, Basic User)
21
+ # so every user has permissions to check against
22
+ load_and_authorize_resource
23
+
24
+ protected
25
+
26
+ # Override resource_class to handle namespaced models
27
+ def resource_class
28
+ # For CCCUX controllers, use the namespaced model
29
+ if self.class.name.start_with?('Cccux::')
30
+ # Extract the model name from controller name (e.g., RolesController -> Cccux::Role)
31
+ model_name = self.class.name.gsub('Cccux::', '').gsub('Controller', '').singularize
32
+ "Cccux::#{model_name}".constantize
33
+ else
34
+ # For host app controllers, use the default behavior
35
+ super
36
+ end
37
+ rescue NameError
38
+ # Fallback to default behavior if constantization fails
39
+ super
40
+ end
41
+
42
+ private
43
+
44
+ def ensure_role_manager
45
+ # Check if user is authenticated
46
+ unless defined?(current_user) && current_user&.persisted?
47
+ respond_to do |format|
48
+ format.html { redirect_to main_app.root_path, alert: 'You must be logged in to access the admin interface.' }
49
+ format.json { render json: { success: false, error: 'You must be logged in to access the admin interface.' }, status: :unauthorized }
50
+ end
51
+ return
52
+ end
53
+
54
+ # Check if user has Role Manager role
55
+ unless current_user.has_role?('Role Manager')
56
+ respond_to do |format|
57
+ format.html { redirect_to main_app.root_path, alert: 'Access denied. Only Role Managers can access the admin interface.' }
58
+ format.json { render json: { success: false, error: 'Access denied. Only Role Managers can access the admin interface.' }, status: :forbidden }
59
+ end
60
+ return
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,172 @@
1
+ module Cccux
2
+ class DashboardController < CccuxController
3
+ # Skip CanCanCan resource loading for dashboard since it doesn't work with a specific model
4
+ skip_load_and_authorize_resource
5
+
6
+ # Ensure only Role Managers can access the dashboard
7
+ before_action :ensure_role_manager
8
+
9
+ def index
10
+ @user_count = User.count
11
+ @role_count = Cccux::Role.count
12
+ @permission_count = Cccux::AbilityPermission.count
13
+ @total_assignments = Cccux::UserRole.count + (Cccux::Role.joins(:ability_permissions).count)
14
+ end
15
+
16
+ def model_discovery
17
+ @detected_models = detect_application_models
18
+ @existing_models = get_models_with_permissions
19
+ @missing_models = @detected_models - @existing_models
20
+ @actions = %w[read create update destroy]
21
+
22
+ # Debug logging
23
+ Rails.logger.info "DEBUG: Detected models: #{@detected_models.inspect}"
24
+ Rails.logger.info "DEBUG: Existing models: #{@existing_models.inspect}"
25
+ Rails.logger.info "DEBUG: Missing models: #{@missing_models.inspect}"
26
+
27
+ # Additional debug for web context
28
+ Rails.logger.info "DEBUG: Controller context - detected count: #{@detected_models.count}"
29
+ Rails.logger.info "DEBUG: Controller context - missing count: #{@missing_models.count}"
30
+ Rails.logger.info "DEBUG: Controller context - Order in detected? #{@detected_models.include?('Order')}"
31
+ Rails.logger.info "DEBUG: Controller context - Order in existing? #{@existing_models.include?('Order')}"
32
+ end
33
+
34
+ def sync_permissions
35
+ models_to_add = params[:models] || []
36
+ actions = %w[read create update destroy]
37
+
38
+ added_permissions = []
39
+
40
+ models_to_add.each do |model_name|
41
+ next if model_name.blank?
42
+
43
+ actions.each do |action|
44
+ permission = Cccux::AbilityPermission.find_or_create_by(
45
+ action: action,
46
+ subject: model_name
47
+ ) do |p|
48
+ p.description = "#{action.capitalize} #{model_name.pluralize.downcase}"
49
+ p.active = true
50
+ end
51
+
52
+ if permission.persisted? && !permission.previously_persisted?
53
+ added_permissions << permission
54
+ end
55
+ end
56
+ end
57
+
58
+ if added_permissions.any?
59
+ redirect_to cccux.model_discovery_path,
60
+ notice: "Successfully added #{added_permissions.count} permissions for #{models_to_add.count} models!"
61
+ else
62
+ redirect_to cccux.model_discovery_path,
63
+ alert: "No new permissions were added. Models may already have permissions."
64
+ end
65
+ end
66
+
67
+ # Handle any unmatched routes in CCCUX - redirect to home
68
+ def not_found
69
+ redirect_to main_app.root_path, alert: 'The requested page was not found.'
70
+ end
71
+
72
+ private
73
+
74
+ def detect_application_models
75
+ models = []
76
+
77
+ begin
78
+ # Direct approach: Get models from database tables (bypasses all autoloading issues)
79
+ Rails.logger.info "Detecting models from database tables..."
80
+
81
+ application_tables = ActiveRecord::Base.connection.tables.reject do |table|
82
+ # Skip Rails internal tables and CCCUX tables
83
+ table.start_with?('schema_migrations', 'ar_internal_metadata', 'cccux_') ||
84
+ skip_table?(table)
85
+ end
86
+
87
+ Rails.logger.info "Found application tables: #{application_tables}"
88
+
89
+ application_tables.each do |table|
90
+ # Convert table name to model name
91
+ model_name = table.singularize.camelize
92
+
93
+ # Verify the model exists and is valid
94
+ begin
95
+ if Object.const_defined?(model_name)
96
+ model_class = Object.const_get(model_name)
97
+ if model_class.respond_to?(:table_name) &&
98
+ model_class.table_name == table &&
99
+ !skip_model_by_name?(model_name)
100
+ models << model_name
101
+ Rails.logger.info "✅ Found model: #{model_name} (table: #{table})"
102
+ end
103
+ else
104
+ # Model constant doesn't exist yet, but table does - likely a valid model
105
+ unless skip_model_by_name?(model_name)
106
+ models << model_name
107
+ Rails.logger.info "✅ Found model from table: #{model_name} (table: #{table})"
108
+ end
109
+ end
110
+ rescue => e
111
+ Rails.logger.debug "Skipped table #{table}: #{e.message}"
112
+ end
113
+ end
114
+
115
+ rescue => e
116
+ Rails.logger.error "Error detecting models from database: #{e.message}"
117
+ Rails.logger.error e.backtrace.join("\n")
118
+ end
119
+
120
+ # Always include CCCUX engine models for management (but not User since host app owns it)
121
+ cccux_models = %w[Cccux::Role Cccux::AbilityPermission Cccux::UserRole Cccux::RoleAbility]
122
+ models += cccux_models
123
+
124
+ # Debug what we found
125
+ Rails.logger.info "Model detection summary:"
126
+ Rails.logger.info " Total models found: #{models.uniq.count}"
127
+ Rails.logger.info " Application models: #{models.reject { |m| m.start_with?('Cccux::') }.join(', ')}"
128
+ Rails.logger.info " CCCUX models: #{models.select { |m| m.start_with?('Cccux::') }.join(', ')}"
129
+
130
+ models.uniq.sort
131
+ end
132
+
133
+ def get_models_with_permissions
134
+ Cccux::AbilityPermission.distinct.pluck(:subject).compact.sort
135
+ end
136
+
137
+ def skip_model?(model_class)
138
+ skip_model_by_name?(model_class.name)
139
+ end
140
+
141
+ def skip_model_by_name?(model_name)
142
+ # Skip models that shouldn't have permissions
143
+ excluded_patterns = [
144
+ /^ActiveRecord::/,
145
+ /^ActiveStorage::/,
146
+ /^ActionText::/,
147
+ /^ActionMailbox::/,
148
+ /^ApplicationRecord$/,
149
+ /Version$/, # PaperTrail versions
150
+ /Schema/,
151
+ /Migration/
152
+ ]
153
+
154
+ excluded_patterns.any? { |pattern| model_name.match?(pattern) }
155
+ end
156
+
157
+ def skip_table?(table_name)
158
+ # Skip tables that are not meant to have corresponding models
159
+ excluded_tables = [
160
+ 'active_storage_blobs',
161
+ 'active_storage_attachments',
162
+ 'active_storage_variant_records',
163
+ 'action_text_rich_texts',
164
+ 'action_mailbox_inbound_emails',
165
+ 'versions' # PaperTrail
166
+ ]
167
+
168
+ excluded_tables.include?(table_name) ||
169
+ table_name.end_with?('_versions') # PaperTrail version tables
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cccux
4
+ class HomeController < ApplicationController
5
+ def index
6
+ # This controller can be used as a fallback root route
7
+ # It will redirect to the host app's root or show a simple welcome page
8
+
9
+ # Try to redirect to host app's root first
10
+ # if Rails.application.routes.recognize_path('/', method: :get) rescue false
11
+ # redirect_to '/'
12
+ # return
13
+ # end
14
+
15
+ # If no host app root, show a simple welcome page
16
+ render :index
17
+ end
18
+ end
19
+ end