open-loam 0.1.1 → 0.3.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 (287) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +148 -8
  3. data/LICENSE +1 -1
  4. data/README.md +27 -25
  5. data/app/jobs/{loam → open_loam}/custom_field_reindex_job.rb +6 -6
  6. data/app/jobs/{loam → open_loam}/event_delivery_job.rb +8 -8
  7. data/app/jobs/open_loam/event_log_prune_job.rb +15 -0
  8. data/app/jobs/{loam → open_loam}/event_redelivery_sweep_job.rb +4 -4
  9. data/app/jobs/{loam → open_loam}/webhook_delivery_job.rb +17 -8
  10. data/app/models/open_loam/api_token.rb +63 -0
  11. data/app/models/{loam → open_loam}/audit_record.rb +3 -3
  12. data/app/models/{loam → open_loam}/auth_attempt.rb +5 -5
  13. data/app/models/{loam → open_loam}/business_rule.rb +10 -10
  14. data/app/models/{loam → open_loam}/business_rule_run.rb +5 -5
  15. data/app/models/{loam → open_loam}/comment.rb +9 -9
  16. data/app/models/{loam → open_loam}/config.rb +7 -7
  17. data/app/models/{loam → open_loam}/custom_field_value.rb +5 -5
  18. data/app/models/{loam → open_loam}/dashboard_widget.rb +5 -5
  19. data/app/models/open_loam/dictionary.rb +20 -0
  20. data/app/models/{loam → open_loam}/dictionary_entry.rb +7 -7
  21. data/app/models/{loam → open_loam}/event_delivery.rb +4 -4
  22. data/app/models/open_loam/event_record.rb +42 -0
  23. data/app/models/{loam → open_loam}/field_definition.rb +8 -8
  24. data/app/models/{loam → open_loam}/inbound_webhook_delivery.rb +4 -4
  25. data/app/models/{loam → open_loam}/inbound_webhook_source.rb +13 -13
  26. data/app/models/{loam → open_loam}/membership.rb +6 -6
  27. data/app/models/{loam → open_loam}/mfa_credential.rb +8 -8
  28. data/app/models/{loam → open_loam}/notification.rb +10 -10
  29. data/app/models/{loam → open_loam}/pending_action.rb +70 -26
  30. data/app/models/{loam → open_loam}/perspective.rb +5 -5
  31. data/app/models/{loam → open_loam}/progress_job.rb +7 -7
  32. data/app/models/{loam → open_loam}/record_lock.rb +5 -5
  33. data/app/models/{loam → open_loam}/scheduled_job.rb +7 -7
  34. data/app/models/open_loam/search_token.rb +9 -0
  35. data/app/models/{loam → open_loam}/sso_identity.rb +4 -4
  36. data/app/models/open_loam/sso_provider.rb +66 -0
  37. data/app/models/open_loam/tenant.rb +20 -0
  38. data/app/models/{loam → open_loam}/translation.rb +5 -5
  39. data/app/models/open_loam/webhook_endpoint.rb +39 -0
  40. data/app/views/{loam → open_loam}/attachments/_attachments.html.erb +1 -1
  41. data/app/views/{loam → open_loam}/comments/_comments.html.erb +1 -1
  42. data/app/views/{loam → open_loam}/custom_fields/_fields.html.erb +6 -2
  43. data/lib/generators/{loam → open_loam}/entity/entity_generator.rb +9 -9
  44. data/lib/generators/{loam → open_loam}/entity/templates/api_controller.rb +2 -1
  45. data/lib/generators/{loam → open_loam}/entity/templates/controller.rb +32 -22
  46. data/lib/generators/{loam → open_loam}/entity/templates/entity_test.rb +12 -12
  47. data/lib/generators/{loam → open_loam}/entity/templates/migration.rb +5 -5
  48. data/lib/generators/{loam → open_loam}/entity/templates/model.rb +10 -10
  49. data/lib/generators/{loam → open_loam}/entity/templates/policy.rb +1 -1
  50. data/lib/generators/{loam → open_loam}/entity/templates/views/_form.html.erb +8 -5
  51. data/lib/generators/{loam → open_loam}/entity/templates/views/deleted.html.erb +7 -7
  52. data/lib/generators/open_loam/entity/templates/views/edit.html.erb +3 -0
  53. data/lib/generators/open_loam/entity/templates/views/index.html.erb +94 -0
  54. data/lib/generators/open_loam/entity/templates/views/new.html.erb +3 -0
  55. data/lib/generators/open_loam/entity/templates/views/show.html.erb +38 -0
  56. data/lib/generators/{loam → open_loam}/install/install_generator.rb +58 -47
  57. data/lib/generators/open_loam/install/templates/AGENTS.md +345 -0
  58. data/lib/generators/{loam → open_loam}/install/templates/admin/api_docs_controller.rb +2 -2
  59. data/lib/generators/{loam → open_loam}/install/templates/admin/api_docs_index.html.erb +1 -1
  60. data/lib/generators/open_loam/install/templates/admin/api_tokens_controller.rb +48 -0
  61. data/lib/generators/{loam → open_loam}/install/templates/admin/api_tokens_index.html.erb +23 -0
  62. data/lib/generators/{loam → open_loam}/install/templates/admin/base_controller.rb +101 -38
  63. data/lib/generators/{loam → open_loam}/install/templates/admin/business_rules_controller.rb +7 -7
  64. data/lib/generators/{loam → open_loam}/install/templates/admin/business_rules_form.html.erb +2 -2
  65. data/lib/generators/{loam → open_loam}/install/templates/admin/comments_controller.rb +4 -4
  66. data/lib/generators/{loam → open_loam}/install/templates/admin/configs_controller.rb +9 -9
  67. data/lib/generators/open_loam/install/templates/admin/dashboard_controller.rb +12 -0
  68. data/lib/generators/{loam → open_loam}/install/templates/admin/dashboard_widgets_controller.rb +5 -5
  69. data/lib/generators/{loam → open_loam}/install/templates/admin/dictionaries_controller.rb +5 -5
  70. data/lib/generators/{loam → open_loam}/install/templates/admin/dictionaries_form.html.erb +1 -1
  71. data/lib/generators/{loam → open_loam}/install/templates/admin/dictionary_entries_controller.rb +2 -2
  72. data/lib/generators/{loam → open_loam}/install/templates/admin/event_deliveries_controller.rb +5 -5
  73. data/lib/generators/{loam → open_loam}/install/templates/admin/event_deliveries_index.html.erb +1 -1
  74. data/lib/generators/{loam → open_loam}/install/templates/admin/events_controller.rb +7 -5
  75. data/lib/generators/{loam → open_loam}/install/templates/admin/features_controller.rb +9 -9
  76. data/lib/generators/{loam → open_loam}/install/templates/admin/field_definitions_controller.rb +5 -5
  77. data/lib/generators/{loam → open_loam}/install/templates/admin/field_definitions_new.html.erb +2 -2
  78. data/lib/generators/open_loam/install/templates/admin/history_controller.rb +64 -0
  79. data/lib/generators/{loam → open_loam}/install/templates/admin/history_index.html.erb +2 -2
  80. data/lib/generators/{loam → open_loam}/install/templates/admin/imports_controller.rb +25 -13
  81. data/lib/generators/{loam → open_loam}/install/templates/admin/inbound_webhook_sources_controller.rb +7 -7
  82. data/lib/generators/{loam → open_loam}/install/templates/admin/inbound_webhook_sources_new.html.erb +2 -4
  83. data/lib/generators/open_loam/install/templates/admin/layout.html.erb +76 -0
  84. data/lib/generators/{loam → open_loam}/install/templates/admin/mfa_controller.rb +11 -6
  85. data/lib/generators/{loam → open_loam}/install/templates/admin/notifications_controller.rb +5 -3
  86. data/lib/generators/{loam → open_loam}/install/templates/admin/overrides_controller.rb +3 -3
  87. data/lib/generators/{loam → open_loam}/install/templates/admin/overrides_index.html.erb +2 -2
  88. data/lib/generators/{loam → open_loam}/install/templates/admin/pending_actions_controller.rb +3 -3
  89. data/lib/generators/{loam → open_loam}/install/templates/admin/perspectives_controller.rb +13 -7
  90. data/lib/generators/{loam → open_loam}/install/templates/admin/perspectives_index.html.erb +1 -1
  91. data/lib/generators/open_loam/install/templates/admin/progress_jobs_controller.rb +23 -0
  92. data/lib/generators/{loam → open_loam}/install/templates/admin/progress_jobs_index.html.erb +1 -1
  93. data/lib/generators/{loam → open_loam}/install/templates/admin/record_locks_controller.rb +4 -4
  94. data/lib/generators/{loam → open_loam}/install/templates/admin/scheduled_jobs_controller.rb +7 -7
  95. data/lib/generators/{loam → open_loam}/install/templates/admin/scheduled_jobs_form.html.erb +1 -1
  96. data/lib/generators/{loam → open_loam}/install/templates/admin/scheduled_jobs_index.html.erb +1 -1
  97. data/lib/generators/{loam → open_loam}/install/templates/admin/search_controller.rb +7 -5
  98. data/lib/generators/{loam → open_loam}/install/templates/admin/sessions_controller.rb +33 -34
  99. data/lib/generators/{loam → open_loam}/install/templates/admin/sso_providers_controller.rb +6 -6
  100. data/lib/generators/{loam → open_loam}/install/templates/admin/sso_providers_form.html.erb +2 -2
  101. data/lib/generators/{loam → open_loam}/install/templates/admin/sso_providers_index.html.erb +8 -1
  102. data/lib/generators/{loam → open_loam}/install/templates/admin/sudo_controller.rb +7 -5
  103. data/lib/generators/{loam → open_loam}/install/templates/admin/sudo_new.html.erb +1 -1
  104. data/lib/generators/{loam → open_loam}/install/templates/admin/translations_controller.rb +7 -7
  105. data/lib/generators/{loam → open_loam}/install/templates/admin/translations_index.html.erb +3 -3
  106. data/lib/generators/{loam → open_loam}/install/templates/admin/webhook_endpoints_controller.rb +4 -4
  107. data/lib/generators/{loam → open_loam}/install/templates/admin/webhook_endpoints_index.html.erb +1 -1
  108. data/lib/generators/{loam → open_loam}/install/templates/admin.css +1 -1
  109. data/lib/generators/open_loam/install/templates/api_base_controller.rb +127 -0
  110. data/lib/generators/{loam → open_loam}/install/templates/guardrails_test.rb +17 -17
  111. data/lib/generators/open_loam/install/templates/import_job.rb +38 -0
  112. data/lib/generators/{loam → open_loam}/install/templates/inbound_webhooks_controller.rb +4 -4
  113. data/lib/generators/{loam → open_loam}/install/templates/initializer.rb +87 -75
  114. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_api_tokens.rb +15 -0
  115. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_audit_records.rb +14 -0
  116. data/lib/generators/{loam/install/templates/migrations/create_loam_auth_attempts.rb → open_loam/install/templates/migrations/create_open_loam_auth_attempts.rb} +3 -3
  117. data/lib/generators/{loam/install/templates/migrations/create_loam_business_rules.rb → open_loam/install/templates/migrations/create_open_loam_business_rules.rb} +10 -10
  118. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_comments.rb +13 -0
  119. data/lib/generators/{loam/install/templates/migrations/create_loam_configs.rb → open_loam/install/templates/migrations/create_open_loam_configs.rb} +7 -7
  120. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_custom_field_values.rb +19 -0
  121. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_dashboard_widgets.rb +12 -0
  122. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_dictionaries.rb +11 -0
  123. data/lib/generators/{loam/install/templates/migrations/create_loam_dictionary_entries.rb → open_loam/install/templates/migrations/create_open_loam_dictionary_entries.rb} +6 -6
  124. data/lib/generators/{loam/install/templates/migrations/create_loam_event_deliveries.rb → open_loam/install/templates/migrations/create_open_loam_event_deliveries.rb} +5 -5
  125. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_event_records.rb +13 -0
  126. data/lib/generators/{loam/install/templates/migrations/create_loam_field_definitions.rb → open_loam/install/templates/migrations/create_open_loam_field_definitions.rb} +4 -4
  127. data/lib/generators/{loam/install/templates/migrations/create_loam_inbound_webhooks.rb → open_loam/install/templates/migrations/create_open_loam_inbound_webhooks.rb} +9 -10
  128. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_memberships.rb +11 -0
  129. data/lib/generators/{loam/install/templates/migrations/create_loam_mfa_credentials.rb → open_loam/install/templates/migrations/create_open_loam_mfa_credentials.rb} +4 -4
  130. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_notifications.rb +15 -0
  131. data/lib/generators/{loam/install/templates/migrations/create_loam_pending_actions.rb → open_loam/install/templates/migrations/create_open_loam_pending_actions.rb} +9 -9
  132. data/lib/generators/{loam/install/templates/migrations/create_loam_perspectives.rb → open_loam/install/templates/migrations/create_open_loam_perspectives.rb} +5 -5
  133. data/lib/generators/{loam/install/templates/migrations/create_loam_progress_jobs.rb → open_loam/install/templates/migrations/create_open_loam_progress_jobs.rb} +5 -5
  134. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_record_locks.rb +16 -0
  135. data/lib/generators/{loam/install/templates/migrations/create_loam_scheduled_jobs.rb → open_loam/install/templates/migrations/create_open_loam_scheduled_jobs.rb} +5 -5
  136. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_search_tokens.rb +18 -0
  137. data/lib/generators/{loam/install/templates/migrations/create_loam_sso_providers.rb → open_loam/install/templates/migrations/create_open_loam_sso_providers.rb} +15 -12
  138. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_tenants.rb +9 -0
  139. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_translations.rb +15 -0
  140. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_webhook_endpoints.rb +13 -0
  141. data/lib/generators/{loam → open_loam}/install/templates/migrations/create_users.rb +1 -1
  142. data/lib/generators/{loam → open_loam}/install/templates/user.rb +5 -5
  143. data/lib/generators/{loam → open_loam}/primary_key_options.rb +23 -23
  144. data/lib/open-loam.rb +2 -2
  145. data/lib/{loam → open_loam}/attachable.rb +4 -4
  146. data/lib/{loam → open_loam}/auditable.rb +23 -23
  147. data/lib/{loam → open_loam}/auth_throttle.rb +20 -12
  148. data/lib/{loam → open_loam}/base32.rb +1 -1
  149. data/lib/{loam → open_loam}/bulk.rb +7 -7
  150. data/lib/{loam → open_loam}/business_rules/actions.rb +7 -7
  151. data/lib/{loam → open_loam}/business_rules/condition.rb +5 -5
  152. data/lib/{loam → open_loam}/business_rules.rb +19 -19
  153. data/lib/{loam → open_loam}/commentable.rb +6 -6
  154. data/lib/{loam → open_loam}/configs.rb +19 -19
  155. data/lib/{loam → open_loam}/cron.rb +2 -2
  156. data/lib/{loam → open_loam}/csv.rb +2 -2
  157. data/lib/{loam → open_loam}/current.rb +5 -5
  158. data/lib/{loam → open_loam}/custom_field_index.rb +33 -30
  159. data/lib/{loam → open_loam}/custom_fields.rb +17 -17
  160. data/lib/{loam → open_loam}/dashboard.rb +5 -5
  161. data/lib/{loam → open_loam}/dictionaries.rb +12 -12
  162. data/lib/{loam → open_loam}/durable_events.rb +19 -19
  163. data/lib/{loam → open_loam}/encryptable.rb +39 -34
  164. data/lib/{loam → open_loam}/encryption/cipher.rb +3 -3
  165. data/lib/{loam → open_loam}/encryption/key_provider.rb +19 -3
  166. data/lib/{loam → open_loam}/encryption.rb +58 -15
  167. data/lib/open_loam/engine.rb +65 -0
  168. data/lib/{loam → open_loam}/enrichers.rb +5 -5
  169. data/lib/{loam → open_loam}/errors.rb +20 -10
  170. data/lib/{loam → open_loam}/eval.rb +2 -2
  171. data/lib/open_loam/event_log.rb +77 -0
  172. data/lib/{loam → open_loam}/event_stream.rb +14 -14
  173. data/lib/{loam → open_loam}/eventful.rb +12 -12
  174. data/lib/{loam → open_loam}/events.rb +5 -5
  175. data/lib/{loam → open_loam}/export.rb +12 -12
  176. data/lib/{loam → open_loam}/features.rb +15 -15
  177. data/lib/{loam → open_loam}/generated_key.rb +6 -6
  178. data/lib/{loam → open_loam}/import.rb +12 -15
  179. data/lib/{loam → open_loam}/inbound_webhooks.rb +19 -18
  180. data/lib/{loam → open_loam}/lifecycle.rb +54 -25
  181. data/lib/{loam/locales/loam.en.yml → open_loam/locales/open_loam.en.yml} +5 -5
  182. data/lib/{loam → open_loam}/mcp/server.rb +9 -9
  183. data/lib/{loam → open_loam}/mcp.rb +31 -22
  184. data/lib/{loam → open_loam}/notifications.rb +5 -5
  185. data/lib/{loam → open_loam}/open_api.rb +16 -16
  186. data/lib/open_loam/outbound_url.rb +100 -0
  187. data/lib/{loam → open_loam}/overrides.rb +10 -10
  188. data/lib/{loam → open_loam}/pending_actions.rb +9 -9
  189. data/lib/{loam → open_loam}/permissions.rb +8 -8
  190. data/lib/{loam → open_loam}/perspectives.rb +9 -9
  191. data/lib/{loam → open_loam}/policy.rb +21 -8
  192. data/lib/{loam → open_loam}/progress.rb +6 -6
  193. data/lib/{loam → open_loam}/record_locks.rb +9 -9
  194. data/lib/{loam → open_loam}/scheduler.rb +20 -20
  195. data/lib/{loam → open_loam}/search/driver.rb +3 -3
  196. data/lib/{loam → open_loam}/search/like_driver.rb +5 -5
  197. data/lib/{loam → open_loam}/search/token_driver.rb +16 -16
  198. data/lib/{loam → open_loam}/search.rb +5 -5
  199. data/lib/{loam → open_loam}/searchable.rb +22 -22
  200. data/lib/{loam → open_loam}/soft_deletable.rb +10 -10
  201. data/lib/{loam → open_loam}/sso/claims.rb +1 -1
  202. data/lib/{loam → open_loam}/sso/fake_provider.rb +1 -1
  203. data/lib/open_loam/sso/http_client.rb +51 -0
  204. data/lib/{loam → open_loam}/sso/oidc_provider.rb +2 -2
  205. data/lib/{loam → open_loam}/sso.rb +39 -18
  206. data/lib/{loam → open_loam}/telemetry.rb +6 -6
  207. data/lib/{loam → open_loam}/tenant_record.rb +10 -10
  208. data/lib/{loam → open_loam}/test_helpers.rb +4 -4
  209. data/lib/{loam → open_loam}/totp.rb +3 -3
  210. data/lib/{loam → open_loam}/translatable.rb +24 -24
  211. data/lib/{loam → open_loam}/undo.rb +11 -11
  212. data/lib/open_loam/version.rb +3 -0
  213. data/lib/{loam → open_loam}/webhooks.rb +4 -4
  214. data/lib/{loam → open_loam}/widgets.rb +9 -9
  215. data/lib/{loam → open_loam}/workflow.rb +45 -45
  216. data/lib/open_loam.rb +124 -0
  217. data/lib/tasks/open_loam.rake +203 -0
  218. data/lib/tasks/{loam_eval.rake → open_loam_eval.rake} +7 -7
  219. data/lib/tasks/open_loam_mcp.rake +20 -0
  220. metadata +247 -242
  221. data/app/models/loam/api_token.rb +0 -40
  222. data/app/models/loam/dictionary.rb +0 -20
  223. data/app/models/loam/search_token.rb +0 -9
  224. data/app/models/loam/sso_provider.rb +0 -35
  225. data/app/models/loam/tenant.rb +0 -20
  226. data/app/models/loam/webhook_endpoint.rb +0 -26
  227. data/lib/generators/loam/entity/templates/views/edit.html.erb +0 -3
  228. data/lib/generators/loam/entity/templates/views/index.html.erb +0 -90
  229. data/lib/generators/loam/entity/templates/views/new.html.erb +0 -3
  230. data/lib/generators/loam/entity/templates/views/show.html.erb +0 -36
  231. data/lib/generators/loam/install/templates/AGENTS.md +0 -341
  232. data/lib/generators/loam/install/templates/admin/api_tokens_controller.rb +0 -30
  233. data/lib/generators/loam/install/templates/admin/dashboard_controller.rb +0 -10
  234. data/lib/generators/loam/install/templates/admin/history_controller.rb +0 -38
  235. data/lib/generators/loam/install/templates/admin/layout.html.erb +0 -76
  236. data/lib/generators/loam/install/templates/admin/progress_jobs_controller.rb +0 -19
  237. data/lib/generators/loam/install/templates/api_base_controller.rb +0 -90
  238. data/lib/generators/loam/install/templates/import_job.rb +0 -25
  239. data/lib/generators/loam/install/templates/migrations/create_loam_api_tokens.rb +0 -15
  240. data/lib/generators/loam/install/templates/migrations/create_loam_audit_records.rb +0 -14
  241. data/lib/generators/loam/install/templates/migrations/create_loam_comments.rb +0 -13
  242. data/lib/generators/loam/install/templates/migrations/create_loam_custom_field_values.rb +0 -19
  243. data/lib/generators/loam/install/templates/migrations/create_loam_dashboard_widgets.rb +0 -12
  244. data/lib/generators/loam/install/templates/migrations/create_loam_dictionaries.rb +0 -11
  245. data/lib/generators/loam/install/templates/migrations/create_loam_memberships.rb +0 -11
  246. data/lib/generators/loam/install/templates/migrations/create_loam_notifications.rb +0 -15
  247. data/lib/generators/loam/install/templates/migrations/create_loam_record_locks.rb +0 -16
  248. data/lib/generators/loam/install/templates/migrations/create_loam_search_tokens.rb +0 -18
  249. data/lib/generators/loam/install/templates/migrations/create_loam_tenants.rb +0 -9
  250. data/lib/generators/loam/install/templates/migrations/create_loam_translations.rb +0 -15
  251. data/lib/generators/loam/install/templates/migrations/create_loam_webhook_endpoints.rb +0 -13
  252. data/lib/loam/engine.rb +0 -57
  253. data/lib/loam/sso/http_client.rb +0 -42
  254. data/lib/loam/version.rb +0 -3
  255. data/lib/loam.rb +0 -122
  256. data/lib/tasks/loam.rake +0 -171
  257. data/lib/tasks/loam_mcp.rake +0 -20
  258. /data/lib/generators/{loam → open_loam}/install/templates/admin/business_rules_edit.html.erb +0 -0
  259. /data/lib/generators/{loam → open_loam}/install/templates/admin/business_rules_index.html.erb +0 -0
  260. /data/lib/generators/{loam → open_loam}/install/templates/admin/business_rules_new.html.erb +0 -0
  261. /data/lib/generators/{loam → open_loam}/install/templates/admin/configs_edit.html.erb +0 -0
  262. /data/lib/generators/{loam → open_loam}/install/templates/admin/configs_index.html.erb +0 -0
  263. /data/lib/generators/{loam → open_loam}/install/templates/admin/dashboard_index.html.erb +0 -0
  264. /data/lib/generators/{loam → open_loam}/install/templates/admin/dashboard_widgets_index.html.erb +0 -0
  265. /data/lib/generators/{loam → open_loam}/install/templates/admin/dictionaries_edit.html.erb +0 -0
  266. /data/lib/generators/{loam → open_loam}/install/templates/admin/dictionaries_index.html.erb +0 -0
  267. /data/lib/generators/{loam → open_loam}/install/templates/admin/dictionaries_new.html.erb +0 -0
  268. /data/lib/generators/{loam → open_loam}/install/templates/admin/features_index.html.erb +0 -0
  269. /data/lib/generators/{loam → open_loam}/install/templates/admin/field_definitions_index.html.erb +0 -0
  270. /data/lib/generators/{loam → open_loam}/install/templates/admin/imports_new.html.erb +0 -0
  271. /data/lib/generators/{loam → open_loam}/install/templates/admin/imports_preview.html.erb +0 -0
  272. /data/lib/generators/{loam → open_loam}/install/templates/admin/imports_summary.html.erb +0 -0
  273. /data/lib/generators/{loam → open_loam}/install/templates/admin/inbound_webhook_sources_index.html.erb +0 -0
  274. /data/lib/generators/{loam → open_loam}/install/templates/admin/mfa_activated.html.erb +0 -0
  275. /data/lib/generators/{loam → open_loam}/install/templates/admin/mfa_new.html.erb +0 -0
  276. /data/lib/generators/{loam → open_loam}/install/templates/admin/mfa_show.html.erb +0 -0
  277. /data/lib/generators/{loam → open_loam}/install/templates/admin/notifications_index.html.erb +0 -0
  278. /data/lib/generators/{loam → open_loam}/install/templates/admin/pagination.rb +0 -0
  279. /data/lib/generators/{loam → open_loam}/install/templates/admin/pending_actions_index.html.erb +0 -0
  280. /data/lib/generators/{loam → open_loam}/install/templates/admin/scheduled_jobs_edit.html.erb +0 -0
  281. /data/lib/generators/{loam → open_loam}/install/templates/admin/scheduled_jobs_new.html.erb +0 -0
  282. /data/lib/generators/{loam → open_loam}/install/templates/admin/search_index.html.erb +0 -0
  283. /data/lib/generators/{loam → open_loam}/install/templates/admin/sessions_mfa_challenge.html.erb +0 -0
  284. /data/lib/generators/{loam → open_loam}/install/templates/admin/sessions_new.html.erb +0 -0
  285. /data/lib/generators/{loam → open_loam}/install/templates/admin/sso_providers_edit.html.erb +0 -0
  286. /data/lib/generators/{loam → open_loam}/install/templates/admin/sso_providers_new.html.erb +0 -0
  287. /data/lib/generators/{loam → open_loam}/install/templates/admin/webhook_endpoints_new.html.erb +0 -0
@@ -1,6 +1,6 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # Undo/redo built ON the audit trail (L-704). Every change already records a
3
- # Loam::AuditRecord with its changeset; undoing one applies the inverse and
3
+ # OpenLoam::AuditRecord with its changeset; undoing one applies the inverse and
4
4
  # writes a NEW audit record — so the inverse is itself undoable, and "redo" is
5
5
  # just undoing that undo entry. No separate command log to keep in sync.
6
6
  #
@@ -12,11 +12,11 @@ module Loam
12
12
  # * encrypted fields — the audit stores "[encrypted]", never the old value,
13
13
  # so there is nothing to revert to;
14
14
  # * the workflow column — a direct write is blocked by the transition gate
15
- # (Loam::Workflow), so state is undone by performing the reverse TRANSITION,
15
+ # (OpenLoam::Workflow), so state is undone by performing the reverse TRANSITION,
16
16
  # not here;
17
17
  # * fields the actor's role may not write (when a policy is supplied).
18
18
  module Undo
19
- class NotUndoableError < Loam::Error; end
19
+ class NotUndoableError < OpenLoam::Error; end
20
20
 
21
21
  IGNORE = %w[id lock_version created_at updated_at].freeze
22
22
 
@@ -53,7 +53,7 @@ module Loam
53
53
 
54
54
  def revert_fields(record, audit, policy)
55
55
  ensure_latest!(audit)
56
- workflow_column = record.class.respond_to?(:loam_workflow) ? record.class.loam_workflow&.column.to_s : nil
56
+ workflow_column = record.class.respond_to?(:open_loam_workflow) ? record.class.open_loam_workflow&.column.to_s : nil
57
57
 
58
58
  updates = {}
59
59
  (audit.changeset || {}).each do |field, values|
@@ -69,10 +69,10 @@ module Loam
69
69
  raise NotUndoableError, "nothing revertable in this change" if updates.empty?
70
70
 
71
71
  # Relabel this save's audit as "undo" so the history reads legibly (and the
72
- # entry is itself undoable = redo). loam_audit_as is the same hook
72
+ # entry is itself undoable = redo). open_loam_audit_as is the same hook
73
73
  # SoftDeletable uses for soft_delete/restore.
74
- if record.respond_to?(:loam_audit_as, true)
75
- record.send(:loam_audit_as, "undo") { record.update!(updates) }
74
+ if record.respond_to?(:open_loam_audit_as, true)
75
+ record.send(:open_loam_audit_as, "undo") { record.update!(updates) }
76
76
  else
77
77
  record.update!(updates)
78
78
  end
@@ -92,7 +92,7 @@ module Loam
92
92
  # The audit must be the record's most recent update/undo — else undoing it
93
93
  # would overwrite newer edits. Tenant-scoped via AuditRecord's default scope.
94
94
  def ensure_latest!(audit)
95
- latest = Loam::AuditRecord
95
+ latest = OpenLoam::AuditRecord
96
96
  .where(auditable_type: audit.auditable_type, auditable_id: audit.auditable_id, action: %w[update undo])
97
97
  .order(:id).last
98
98
  return if latest.nil? || latest.id == audit.id
@@ -105,7 +105,7 @@ module Loam
105
105
  # restore. Never AuditRecord#auditable, which is unscoped.
106
106
  def load_record(audit)
107
107
  klass = audit.auditable_type.safe_constantize
108
- raise NotUndoableError, "unknown type #{audit.auditable_type.inspect}" unless klass.is_a?(Class) && klass < Loam::TenantRecord
108
+ raise NotUndoableError, "unknown type #{audit.auditable_type.inspect}" unless klass.is_a?(Class) && klass < OpenLoam::TenantRecord
109
109
 
110
110
  scope = klass.respond_to?(:with_deleted) ? klass.with_deleted : klass.all
111
111
  record = scope.find_by(id: audit.auditable_id)
@@ -120,7 +120,7 @@ module Loam
120
120
  end
121
121
 
122
122
  def ensure_current_tenant!(audit)
123
- raise NotUndoableError, "not in this tenant" unless audit.tenant_id == Loam.tenant!.id
123
+ raise NotUndoableError, "not in this tenant" unless audit.tenant_id == OpenLoam.tenant!.id
124
124
  end
125
125
  end
126
126
  end
@@ -0,0 +1,3 @@
1
+ module OpenLoam
2
+ VERSION = "0.3.0"
3
+ end
@@ -1,12 +1,12 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # Domain events, delivered over HTTP to whoever asked for them.
3
3
  #
4
- # One global subscriber listens to every Loam event and, in the tenant the
4
+ # One global subscriber listens to every OpenLoam event and, in the tenant the
5
5
  # event was published in, enqueues a delivery per matching active endpoint.
6
6
  # Matching happens at dispatch time rather than at subscribe time because
7
7
  # endpoints are data: a tenant can add one from the admin without a reboot.
8
8
  module Webhooks
9
- # Wired once from Loam::Engine. Idempotent: subscribing twice would deliver
9
+ # Wired once from OpenLoam::Engine. Idempotent: subscribing twice would deliver
10
10
  # every event twice.
11
11
  def self.subscribe!
12
12
  @subscription ||= Events.subscribe_all { |event_name, payload| dispatch(event_name, payload) }
@@ -22,7 +22,7 @@ module Loam
22
22
  # and scalars by convention, never records.
23
23
  deliverable = payload.transform_keys(&:to_s)
24
24
 
25
- Loam.as_tenant(tenant) do
25
+ OpenLoam.as_tenant(tenant) do
26
26
  WebhookEndpoint.active.each do |endpoint|
27
27
  next unless endpoint.matches?(event_name)
28
28
 
@@ -1,9 +1,9 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # A registry of dashboard widgets — small, module-provided tiles (a metric, a
3
3
  # short list) shown on the admin home. A widget is a DATA PROVIDER plus a
4
4
  # title and an optional role filter; it never renders arbitrary code:
5
5
  #
6
- # Loam::Widgets.register(key: "open_rentals", title: "Open rentals", roles: %w[manager]) do |actor|
6
+ # OpenLoam::Widgets.register(key: "open_rentals", title: "Open rentals", roles: %w[manager]) do |actor|
7
7
  # { kind: "count", value: Rental.where(status: "open").count } # tenant-scoped query
8
8
  # end
9
9
  #
@@ -37,33 +37,33 @@ module Loam
37
37
  # can't see it (so its data is never computed), otherwise
38
38
  # { key, title, data } — or { key, title, error: } when the provider raises.
39
39
  def resolve(key, actor:, role:)
40
- return nil if Loam::Overrides.disabled?(:widgets, key) # customization without forking
40
+ return nil if OpenLoam::Overrides.disabled?(:widgets, key) # customization without forking
41
41
 
42
42
  widget = find(key)
43
43
  return nil unless widget&.visible_to?(role)
44
44
 
45
- provider = Loam::Overrides.replacement(:widgets, key) || widget.provider
45
+ provider = OpenLoam::Overrides.replacement(:widgets, key) || widget.provider
46
46
  { key: widget.key, title: widget.title, data: provider.call(actor) }
47
47
  rescue StandardError => error
48
48
  { key: widget&.key, title: widget&.title, error: error.message }
49
49
  end
50
50
 
51
- # The widgets every Loam app starts with — registered at boot from the
51
+ # The widgets every OpenLoam app starts with — registered at boot from the
52
52
  # engine, so a fresh install has a useful default dashboard.
53
53
  def register_builtins!
54
54
  register(key: "audit_recent", title: "Recent activity") do |_actor|
55
- items = Loam::AuditRecord.order(created_at: :desc).limit(8)
55
+ items = OpenLoam::AuditRecord.order(created_at: :desc).limit(8)
56
56
  .map { |a| "#{a.action} #{a.auditable_type} ##{a.auditable_id}" }
57
57
  { kind: "list", items: items }
58
58
  end
59
59
  register(key: "notifications_unread", title: "Unread notifications") do |actor|
60
- { kind: "count", value: Loam::Notification.unread.where(user_id: actor&.id).count }
60
+ { kind: "count", value: OpenLoam::Notification.unread.where(user_id: actor&.id).count }
61
61
  end
62
62
  register(key: "pending_approvals", title: "Pending approvals", roles: %w[manager]) do |_actor|
63
- { kind: "count", value: Loam::PendingAction.pending.count }
63
+ { kind: "count", value: OpenLoam::PendingAction.pending.count }
64
64
  end
65
65
  register(key: "open_progress", title: "Running tasks") do |_actor|
66
- { kind: "count", value: Loam::ProgressJob.where(status: "running").count }
66
+ { kind: "count", value: OpenLoam::ProgressJob.where(status: "running").count }
67
67
  end
68
68
  end
69
69
 
@@ -1,9 +1,9 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # Declarative state machine for a tenant-scoped entity: the states a record
3
3
  # can be in, the legal moves between them, and who may make each move.
4
4
  #
5
- # class PurchaseOrder < Loam::TenantRecord
6
- # include Loam::Workflow
5
+ # class PurchaseOrder < OpenLoam::TenantRecord
6
+ # include OpenLoam::Workflow
7
7
  #
8
8
  # workflow :status, initial: "draft" do
9
9
  # state "draft"
@@ -18,14 +18,14 @@ module Loam
18
18
  # order.submit! # draft -> pending_approval, publishes
19
19
  # # "<domain>.purchase_order.submit"
20
20
  # order.workflow_transitions_available # => [:approve] for a manager, [] for anyone else
21
- # PurchaseOrder.loam_workflow # the frozen definition, for agents and admin UI
21
+ # PurchaseOrder.open_loam_workflow # the frozen definition, for agents and admin UI
22
22
  #
23
23
  # What this buys over hand-rolled `if status == "draft"`: an illegal move
24
24
  # raises instead of silently writing a bad value, the roles that may move a
25
25
  # record are declared in one readable place instead of scattered across
26
26
  # controllers, and the whole machine is introspectable.
27
27
  #
28
- # The state change itself is an ordinary attribute write, so Loam::Auditable
28
+ # The state change itself is an ordinary attribute write, so OpenLoam::Auditable
29
29
  # records it like any other change — a workflow adds no second audit trail.
30
30
  module Workflow
31
31
  extend ActiveSupport::Concern
@@ -99,22 +99,22 @@ module Loam
99
99
 
100
100
  included do
101
101
  # nil until `workflow` is called; inherited by subclasses.
102
- class_attribute :loam_workflow, instance_writer: false, default: nil
102
+ class_attribute :open_loam_workflow, instance_writer: false, default: nil
103
103
 
104
104
  # THE gate: the workflow column may only change through a transition
105
- # (loam_perform_transition!), which enforces from-state and role. A direct
105
+ # (open_loam_perform_transition!), which enforces from-state and role. A direct
106
106
  # write — the edit form, Bulk.set_field, an import, a business-rule
107
107
  # set_field — would otherwise let a member self-"approve", skipping the
108
108
  # transition's roles:. Closing it here closes ALL paths at once. Creation
109
109
  # (the initial state) is exempt (`on: :update`).
110
- validate :loam_workflow_column_only_via_transition, on: :update
110
+ validate :open_loam_workflow_column_only_via_transition, on: :update
111
111
  end
112
112
 
113
113
  class_methods do
114
114
  # The DSL. `initial:` defaults to the first declared state.
115
115
  def workflow(column, initial: nil, &block)
116
116
  definition = Builder.new(column, initial).build(&block)
117
- self.loam_workflow = definition
117
+ self.open_loam_workflow = definition
118
118
 
119
119
  before_validation on: :create do
120
120
  self[definition.column] = definition.initial if self[definition.column].blank?
@@ -125,29 +125,29 @@ module Loam
125
125
  validates definition.column,
126
126
  inclusion: { in: definition.states, message: "is not one of: #{definition.states.join(', ')}" }
127
127
 
128
- include loam_workflow_module(definition)
128
+ include open_loam_workflow_module(definition)
129
129
  end
130
130
 
131
131
  # Transition and predicate methods live in their own module rather than
132
132
  # on the class, so an app can override `submit!` and still call `super`.
133
- def loam_workflow_module(definition)
133
+ def open_loam_workflow_module(definition)
134
134
  model = self
135
135
 
136
136
  Module.new do
137
137
  definition.transitions.each_value do |transition|
138
- define_method("#{transition.name}!") { loam_perform_transition!(transition) }
138
+ define_method("#{transition.name}!") { open_loam_perform_transition!(transition) }
139
139
  end
140
140
 
141
141
  definition.states.each do |state|
142
142
  predicate = "#{state}?"
143
- next unless model.loam_workflow_predicate_free?(predicate)
143
+ next unless model.open_loam_workflow_predicate_free?(predicate)
144
144
 
145
145
  # A state predicate must never shadow a real column: DamageReport
146
146
  # has both a workflow state "approved" and an `approved` boolean
147
147
  # column, and `record.approved?` has to stay the column's. The
148
148
  # column check happens at CALL time, not class-load time — at load
149
149
  # the schema may not be readable yet (fresh CI database, db:create),
150
- # and a load-time decision made Loam behave differently on CI than
150
+ # and a load-time decision made OpenLoam behave differently on CI than
151
151
  # on a warmed-up dev machine.
152
152
  define_method(predicate) do
153
153
  if self.class.columns_hash.key?(state)
@@ -162,41 +162,41 @@ module Loam
162
162
 
163
163
  # Explicitly defined methods still win — only truly free names get a
164
164
  # workflow predicate. Column collisions are handled inside the predicate
165
- # itself (see loam_workflow_module), where the schema is reliably known.
166
- def loam_workflow_predicate_free?(predicate)
165
+ # itself (see open_loam_workflow_module), where the schema is reliably known.
166
+ def open_loam_workflow_predicate_free?(predicate)
167
167
  !method_defined?(predicate) && !private_method_defined?(predicate)
168
168
  end
169
169
  end
170
170
 
171
171
  # Transition names that are legal right now: from this record's state, for
172
- # the actor in Loam::Current. Never raises — an admin screen asks this to
172
+ # the actor in OpenLoam::Current. Never raises — an admin screen asks this to
173
173
  # decide which buttons to render.
174
174
  def workflow_transitions_available
175
- role = loam_workflow_role
175
+ role = open_loam_workflow_role
176
176
 
177
- self.class.loam_workflow.transitions_from(loam_workflow_state)
177
+ self.class.open_loam_workflow.transitions_from(open_loam_workflow_state)
178
178
  .select { |transition| transition.roles.empty? || transition.roles.include?(role) }
179
179
  .map(&:name)
180
180
  end
181
181
 
182
- def loam_workflow_state
183
- self[self.class.loam_workflow.column].to_s
182
+ def open_loam_workflow_state
183
+ self[self.class.open_loam_workflow.column].to_s
184
184
  end
185
185
 
186
186
  private
187
187
 
188
- def loam_workflow_column_only_via_transition
189
- return unless self.class.loam_workflow
188
+ def open_loam_workflow_column_only_via_transition
189
+ return unless self.class.open_loam_workflow
190
190
 
191
- column = self.class.loam_workflow.column
191
+ column = self.class.open_loam_workflow.column
192
192
  return unless attribute_changed?(column)
193
- return if @loam_in_transition
193
+ return if @open_loam_in_transition
194
194
 
195
195
  errors.add(column, "can only change through a workflow transition, not a direct write")
196
196
  end
197
197
 
198
- def loam_perform_transition!(transition)
199
- from = loam_workflow_state
198
+ def open_loam_perform_transition!(transition)
199
+ from = open_loam_workflow_state
200
200
 
201
201
  unless transition.from.include?(from)
202
202
  raise InvalidTransitionError,
@@ -204,53 +204,53 @@ module Loam
204
204
  "but this record is #{from.inspect}"
205
205
  end
206
206
 
207
- loam_authorize_transition!(transition)
207
+ open_loam_authorize_transition!(transition)
208
208
 
209
- self[self.class.loam_workflow.column] = transition.to
209
+ self[self.class.open_loam_workflow.column] = transition.to
210
210
  begin
211
- @loam_in_transition = true # tells the guard THIS column change is blessed
211
+ @open_loam_in_transition = true # tells the guard THIS column change is blessed
212
212
  save!
213
213
  ensure
214
- @loam_in_transition = false
214
+ @open_loam_in_transition = false
215
215
  end
216
216
 
217
- Loam::Events.publish(
218
- "#{loam_workflow_event_domain}.#{model_name.param_key}.#{transition.name}",
217
+ OpenLoam::Events.publish(
218
+ "#{open_loam_workflow_event_domain}.#{model_name.param_key}.#{transition.name}",
219
219
  id: id, from: from, to: transition.to
220
220
  )
221
221
 
222
222
  self
223
223
  end
224
224
 
225
- def loam_authorize_transition!(transition)
225
+ def open_loam_authorize_transition!(transition)
226
226
  return if transition.roles.empty?
227
227
 
228
228
  allowed = transition.roles.join(", ")
229
- unless Loam::Current.actor
229
+ unless OpenLoam::Current.actor
230
230
  raise NotAuthorizedError,
231
231
  "#{self.class.name}##{transition.name}! is restricted to #{allowed}, but there is no actor in " \
232
- "Loam::Current — wrap the call in Loam.as_tenant(tenant, actor: user) { ... }"
232
+ "OpenLoam::Current — wrap the call in OpenLoam.as_tenant(tenant, actor: user) { ... }"
233
233
  end
234
234
 
235
- return if transition.roles.include?(loam_workflow_role)
235
+ return if transition.roles.include?(open_loam_workflow_role)
236
236
 
237
237
  raise NotAuthorizedError,
238
238
  "#{self.class.name}##{transition.name}! is restricted to #{allowed}; " \
239
- "this actor's role is #{loam_workflow_role.inspect}"
239
+ "this actor's role is #{open_loam_workflow_role.inspect}"
240
240
  end
241
241
 
242
- # Role resolution deliberately goes through Loam::Policy: "your role" means
242
+ # Role resolution deliberately goes through OpenLoam::Policy: "your role" means
243
243
  # exactly one thing — your membership role in the current tenant — here, in
244
244
  # policies, and in the admin.
245
- def loam_workflow_role
246
- actor = Loam::Current.actor
247
- actor && Loam::Policy.new(actor, self).role
245
+ def open_loam_workflow_role
246
+ actor = OpenLoam::Current.actor
247
+ actor && OpenLoam::Policy.new(actor, self).role
248
248
  end
249
249
 
250
250
  # Transitions publish into the model's own event domain when it has one
251
- # (Loam::Eventful), so workflow events sit beside the lifecycle events.
252
- def loam_workflow_event_domain
253
- self.class.respond_to?(:loam_event_domain) ? self.class.loam_event_domain : "app"
251
+ # (OpenLoam::Eventful), so workflow events sit beside the lifecycle events.
252
+ def open_loam_workflow_event_domain
253
+ self.class.respond_to?(:open_loam_event_domain) ? self.class.open_loam_event_domain : "app"
254
254
  end
255
255
  end
256
256
  end
data/lib/open_loam.rb ADDED
@@ -0,0 +1,124 @@
1
+ require "open_loam/version"
2
+ require "open_loam/errors"
3
+ require "open_loam/current"
4
+ require "open_loam/events"
5
+ require "open_loam/policy"
6
+ require "open_loam/lifecycle"
7
+ require "open_loam/configs"
8
+ require "open_loam/features"
9
+ require "open_loam/auth_throttle"
10
+ require "open_loam/outbound_url"
11
+ require "open_loam/dictionaries"
12
+ require "open_loam/custom_field_index"
13
+ require "open_loam/undo"
14
+ require "open_loam/permissions"
15
+ require "open_loam/telemetry"
16
+ require "open_loam/eval"
17
+ require "open_loam/mcp"
18
+ require "open_loam/mcp/server"
19
+ require "open_loam/progress"
20
+ require "open_loam/cron"
21
+ require "open_loam/scheduler"
22
+ require "open_loam/csv"
23
+ require "open_loam/export"
24
+ require "open_loam/import"
25
+ require "open_loam/bulk"
26
+ require "open_loam/widgets"
27
+ require "open_loam/dashboard"
28
+ require "open_loam/overrides"
29
+ require "open_loam/open_api"
30
+ require "open_loam/encryption"
31
+ require "open_loam/base32"
32
+ require "open_loam/totp"
33
+ require "open_loam/pending_actions"
34
+ require "open_loam/perspectives"
35
+ require "open_loam/record_locks"
36
+ require "open_loam/event_stream"
37
+ require "open_loam/enrichers"
38
+ require "open_loam/search"
39
+ require "open_loam/search/driver"
40
+ require "open_loam/search/like_driver"
41
+ require "open_loam/search/token_driver"
42
+ require "open_loam/notifications"
43
+ require "open_loam/sso"
44
+ require "open_loam/business_rules"
45
+ require "open_loam/webhooks"
46
+ require "open_loam/inbound_webhooks"
47
+ require "open_loam/durable_events"
48
+ require "open_loam/event_log"
49
+ require "open_loam/engine" if defined?(Rails::Engine)
50
+
51
+ module OpenLoam
52
+ # The current tenant, or a loud failure. This is THE guardrail: tenant-scoped
53
+ # code paths call this, so a missing tenant context can never silently widen
54
+ # a query to all tenants.
55
+ def self.tenant!
56
+ Current.tenant or raise MissingTenantError
57
+ end
58
+
59
+ def self.tenant
60
+ Current.tenant
61
+ end
62
+
63
+ def self.actor
64
+ Current.actor
65
+ end
66
+
67
+ # Feature-string permission check for the current actor's role (see
68
+ # OpenLoam::Permissions) — deny-by-default, wildcard-aware. `role:` overrides the
69
+ # actor's role. Returns false with no actor/role.
70
+ def self.can?(permission, role: nil)
71
+ role ||= Current.actor && OpenLoam::Membership.find_by(user_id: Current.actor.id)&.role
72
+ OpenLoam::Permissions.allow?(role, permission)
73
+ end
74
+
75
+ # The approval-gate seam. When a caller runs under :confirm (an MCP tool acting
76
+ # for an AI agent), a write should be STAGED for human approval via
77
+ # OpenLoam::PendingActions.stage instead of committed. OpenLoam does not intercept
78
+ # Active Record globally — this is the documented hook a write path checks.
79
+ def self.mutation_mode
80
+ Current.mutation_mode || :direct
81
+ end
82
+
83
+ def self.require_confirmation?
84
+ mutation_mode == :confirm
85
+ end
86
+
87
+ # Run a block with writes gated for approval. The one blessed way to enter
88
+ # confirm-mode; restores the previous mode afterwards.
89
+ def self.with_confirmation
90
+ previous = Current.mutation_mode
91
+ Current.mutation_mode = :confirm
92
+ yield
93
+ ensure
94
+ Current.mutation_mode = previous
95
+ end
96
+
97
+ # Run a block inside a tenant (and optional actor) context, restoring the
98
+ # previous context afterwards. The one blessed way to switch tenants.
99
+ def self.as_tenant(tenant, actor: nil)
100
+ previous_tenant = Current.tenant
101
+ previous_actor = Current.actor
102
+ Current.tenant = tenant
103
+ Current.actor = actor if actor
104
+ yield
105
+ ensure
106
+ Current.tenant = previous_tenant
107
+ Current.actor = previous_actor
108
+ end
109
+ end
110
+
111
+ ActiveSupport.on_load(:active_record) do
112
+ require "open_loam/generated_key"
113
+ require "open_loam/tenant_record"
114
+ require "open_loam/auditable"
115
+ require "open_loam/soft_deletable"
116
+ require "open_loam/eventful"
117
+ require "open_loam/custom_fields"
118
+ require "open_loam/workflow"
119
+ require "open_loam/searchable"
120
+ require "open_loam/encryptable"
121
+ require "open_loam/translatable"
122
+ require "open_loam/commentable"
123
+ require "open_loam/attachable"
124
+ end