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,18 +1,18 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # The one way to read and write per-tenant settings.
3
3
  #
4
- # Loam::Configs.get("rental.late_fee_per_day") # resolved for the current tenant
5
- # Loam::Configs.set("rental.late_fee_per_day", 45) # this tenant's override
6
- # Loam::Configs.set("rental.currency", "PLN", scope: :global)
7
- # Loam::Configs.reset("rental.late_fee_per_day") # drop the override → fall back
4
+ # OpenLoam::Configs.get("rental.late_fee_per_day") # resolved for the current tenant
5
+ # OpenLoam::Configs.set("rental.late_fee_per_day", 45) # this tenant's override
6
+ # OpenLoam::Configs.set("rental.currency", "PLN", scope: :global)
7
+ # OpenLoam::Configs.reset("rental.late_fee_per_day") # drop the override → fall back
8
8
  #
9
9
  # Resolution order, most specific first:
10
10
  # 1. the current tenant's OVERRIDE row (tenant_id = current tenant)
11
11
  # 2. the GLOBAL row (tenant_id NULL)
12
- # 3. the declared default in Loam.config_defaults[key]
12
+ # 3. the declared default in OpenLoam.config_defaults[key]
13
13
  # 4. the `default:` argument (nil)
14
14
  #
15
- # Reads are memoized per request in Loam::Current.config_cache, keyed by
15
+ # Reads are memoized per request in OpenLoam::Current.config_cache, keyed by
16
16
  # [key, tenant_id], and the whole cache is dropped on any write. That is the
17
17
  # deliberately-simple prototype cache; a Rails.cache-backed layer shared across
18
18
  # requests is the scaling path, and it would slot in behind this same API.
@@ -36,7 +36,7 @@ module Loam
36
36
  end
37
37
 
38
38
  # Write a setting. scope: :tenant writes the current tenant's override and
39
- # requires a tenant in context (a missing one raises, like every Loam
39
+ # requires a tenant in context (a missing one raises, like every OpenLoam
40
40
  # write); scope: :global writes the app-wide row (tenant_id NULL). Upsert.
41
41
  def set(key, value, scope: :tenant)
42
42
  key = key.to_s
@@ -66,8 +66,8 @@ module Loam
66
66
  # defaults, plus any global or current-tenant rows already written. Sorted
67
67
  # and de-duplicated, for the settings screen.
68
68
  def defined_keys
69
- rows = Loam::Config.where(tenant_id: [nil, Loam.tenant&.id]).distinct.pluck(:key)
70
- (Loam.config_defaults.keys + rows).map(&:to_s).uniq.sort
69
+ rows = OpenLoam::Config.where(tenant_id: [nil, OpenLoam.tenant&.id]).distinct.pluck(:key)
70
+ (OpenLoam.config_defaults.keys + rows).map(&:to_s).uniq.sort
71
71
  end
72
72
 
73
73
  private
@@ -76,39 +76,39 @@ module Loam
76
76
  # the caller then applies its own `default:` without caching it.
77
77
  def resolve(key)
78
78
  if (tenant_id = current_tenant_id)
79
- override = Loam::Config.find_by(key: key, tenant_id: tenant_id)
79
+ override = OpenLoam::Config.find_by(key: key, tenant_id: tenant_id)
80
80
  return [true, override.value_json] if override
81
81
  end
82
82
 
83
- global = Loam::Config.find_by(key: key, tenant_id: nil)
83
+ global = OpenLoam::Config.find_by(key: key, tenant_id: nil)
84
84
  return [true, global.value_json] if global
85
85
 
86
- return [true, Loam.config_defaults[key]] if Loam.config_defaults.key?(key)
86
+ return [true, OpenLoam.config_defaults[key]] if OpenLoam.config_defaults.key?(key)
87
87
 
88
88
  [false, nil]
89
89
  end
90
90
 
91
91
  # The row(s) a write/read targets. :global is the tenant_id NULL row;
92
92
  # :tenant is the current tenant's — which requires a tenant in context,
93
- # so Loam.tenant! raises MissingTenantError when there is none.
93
+ # so OpenLoam.tenant! raises MissingTenantError when there is none.
94
94
  def row_for(key, scope)
95
95
  case scope
96
- when :global then Loam::Config.where(key: key, tenant_id: nil)
97
- when :tenant then Loam::Config.where(key: key, tenant_id: Loam.tenant!.id)
96
+ when :global then OpenLoam::Config.where(key: key, tenant_id: nil)
97
+ when :tenant then OpenLoam::Config.where(key: key, tenant_id: OpenLoam.tenant!.id)
98
98
  else raise ArgumentError, "unknown config scope #{scope.inspect} (use :tenant or :global)"
99
99
  end
100
100
  end
101
101
 
102
102
  def current_tenant_id
103
- Loam.tenant&.id
103
+ OpenLoam.tenant&.id
104
104
  end
105
105
 
106
106
  def cache
107
- Loam::Current.config_cache ||= {}
107
+ OpenLoam::Current.config_cache ||= {}
108
108
  end
109
109
 
110
110
  def clear_cache
111
- Loam::Current.config_cache = {}
111
+ OpenLoam::Current.config_cache = {}
112
112
  end
113
113
  end
114
114
  end
@@ -1,9 +1,9 @@
1
1
  require "set"
2
2
 
3
- module Loam
3
+ module OpenLoam
4
4
  # A small, dependency-free "next run" calculator for a standard 5-field cron
5
5
  # expression ("min hour dom month dow") plus an "interval:N" (every N seconds)
6
- # form — same stdlib-only spirit as Loam::Totp / Loam::Base32. Timezone-aware;
6
+ # form — same stdlib-only spirit as OpenLoam::Totp / OpenLoam::Base32. Timezone-aware;
7
7
  # always returns UTC. Supports `*`, lists (`1,15`), ranges (`1-5`), and steps
8
8
  # (`*/15`, `0-30/10`), and the classic cron day-of-month / day-of-week OR quirk
9
9
  # (when BOTH are restricted, a match on either fires).
@@ -1,9 +1,9 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # CSV formula-injection defense. A cell whose value starts with =, +, -, @, or
3
3
  # a tab/CR is treated as a FORMULA by Excel/Google Sheets when the file is
4
4
  # opened — so `=cmd|...` or `=HYPERLINK(...)` in imported/exported data can run.
5
5
  # Prefixing such a value with a single quote neutralizes it while leaving the
6
- # visible text intact. Applied on every CSV cell Loam writes (export + the
6
+ # visible text intact. Applied on every CSV cell OpenLoam writes (export + the
7
7
  # import error file).
8
8
  module Csv
9
9
  DANGEROUS = /\A[=+\-@\t\r]/
@@ -1,20 +1,20 @@
1
1
  require "active_support/current_attributes"
2
2
 
3
- module Loam
3
+ module OpenLoam
4
4
  # Per-request (and per-job) execution context. Every tenant-scoped query,
5
5
  # audit record, and published event reads from here.
6
6
  class Current < ActiveSupport::CurrentAttributes
7
- # config_cache memoizes resolved Loam::Configs values for the duration of
7
+ # config_cache memoizes resolved OpenLoam::Configs values for the duration of
8
8
  # one request/job, keyed by [key, tenant_id]. It resets with everything else
9
9
  # here, so a value can never be read stale across requests.
10
10
  #
11
- # mutation_mode is the approval-gate seam (Loam::PendingActions): nil/:direct
11
+ # mutation_mode is the approval-gate seam (OpenLoam::PendingActions): nil/:direct
12
12
  # means writes commit normally; :confirm means a caller (an MCP tool acting
13
13
  # for an AI agent) stages writes for human approval instead. It lives here so
14
14
  # it is per-request and resets automatically.
15
- # dictionary_cache memoizes resolved Loam::Dictionaries lookups for one
15
+ # dictionary_cache memoizes resolved OpenLoam::Dictionaries lookups for one
16
16
  # request/job, keyed by [kind, key, tenant_id] — same posture as config_cache.
17
- # locale is the current content-translation language (Loam::Translatable) —
17
+ # locale is the current content-translation language (OpenLoam::Translatable) —
18
18
  # request state like tenant/actor, set in a before_action and reset here.
19
19
  attribute :tenant, :actor, :config_cache, :mutation_mode, :dictionary_cache, :locale
20
20
  end
@@ -1,12 +1,12 @@
1
- module Loam
2
- # The read-model index for custom fields (Loam::CustomFieldValue) — a typed-EAV
1
+ module OpenLoam
2
+ # The read-model index for custom fields (OpenLoam::CustomFieldValue) — a typed-EAV
3
3
  # projection that makes custom-field filter/sort/search index-backed.
4
4
  #
5
- # Loam::CustomFieldIndex.filter(DamageReport, "severity", "eq", "critical") # a relation
6
- # Loam::CustomFieldIndex.order(DamageReport, "severity", :asc)
7
- # Loam::CustomFieldIndex.reindex(DamageReport) # rebuild
5
+ # OpenLoam::CustomFieldIndex.filter(DamageReport, "severity", "eq", "critical") # a relation
6
+ # OpenLoam::CustomFieldIndex.order(DamageReport, "severity", :asc)
7
+ # OpenLoam::CustomFieldIndex.reindex(DamageReport) # rebuild
8
8
  #
9
- # SAFETY: `field_key` must name a real Loam::FieldDefinition (no arbitrary
9
+ # SAFETY: `field_key` must name a real OpenLoam::FieldDefinition (no arbitrary
10
10
  # keys); values are cast to the declared type; queries are tenant-scoped
11
11
  # throughout (index rows carry tenant_id, the model relation is default-scoped),
12
12
  # so a filter can't reach across tenants. Encrypted data lives in real columns,
@@ -16,7 +16,7 @@ module Loam
16
16
 
17
17
  module_function
18
18
 
19
- # --- maintenance (called from Loam::CustomFields) ---
19
+ # --- maintenance (called from OpenLoam::CustomFields) ---
20
20
 
21
21
  # Re-project one record's custom fields (delete + insert). Soft-delete keeps
22
22
  # the rows (the filter's base scope hides the record anyway — the same
@@ -26,18 +26,18 @@ module Loam
26
26
  return unless model.respond_to?(:custom_field_definitions)
27
27
 
28
28
  type = index_type(model)
29
- Loam::CustomFieldValue.where(indexable_type: type, indexable_id: record.id).delete_all
29
+ OpenLoam::CustomFieldValue.where(indexable_type: type, indexable_id: record.id).delete_all
30
30
 
31
31
  rows = model.custom_field_definitions.filter_map { |definition| row_for(record, definition, type) }
32
- Loam::CustomFieldValue.insert_all(rows) if rows.any?
32
+ OpenLoam::CustomFieldValue.insert_all(rows) if rows.any?
33
33
  end
34
34
 
35
35
  def remove(record)
36
- Loam::CustomFieldValue.where(indexable_type: index_type(record.class), indexable_id: record.id).delete_all
36
+ OpenLoam::CustomFieldValue.where(indexable_type: index_type(record.class), indexable_id: record.id).delete_all
37
37
  end
38
38
 
39
39
  def reindex(model)
40
- Loam::CustomFieldValue.where(indexable_type: index_type(model)).delete_all
40
+ OpenLoam::CustomFieldValue.where(indexable_type: index_type(model)).delete_all
41
41
  model.find_each { |record| project(record) }
42
42
  end
43
43
 
@@ -47,7 +47,7 @@ module Loam
47
47
  field_key = field_key.to_s
48
48
  refuse_unknown_field!(model, field_key)
49
49
  refuse_unreadable_field!(model, field_key)
50
- raise Loam::Error, "unknown custom-field op #{op.inspect}" unless OPS.include?(op.to_s)
50
+ raise OpenLoam::Error, "unknown custom-field op #{op.inspect}" unless OPS.include?(op.to_s)
51
51
 
52
52
  if covered?(model, field_key)
53
53
  self.partial = false
@@ -89,12 +89,12 @@ module Loam
89
89
  self.partial = true
90
90
  schedule_reindex(model)
91
91
  end
92
- table = Loam::CustomFieldValue.table_name
92
+ table = OpenLoam::CustomFieldValue.table_name
93
93
  conn = model.connection
94
94
  join = "LEFT JOIN #{table} ON #{table}.indexable_type = #{conn.quote(index_type(model))} " \
95
95
  "AND #{table}.indexable_id = #{model.table_name}.id " \
96
96
  "AND #{table}.field_key = #{conn.quote(field_key.to_s)} " \
97
- "AND #{table}.tenant_id = #{Loam.tenant!.id}"
97
+ "AND #{table}.tenant_id = #{conn.quote(OpenLoam.tenant!.id)}"
98
98
  model.joins(join).order(Arel.sql("#{table}.value_text #{dir.to_s.casecmp('desc').zero? ? 'DESC' : 'ASC'}"))
99
99
  end
100
100
 
@@ -132,22 +132,22 @@ module Loam
132
132
  # Was the last filter/order served over an incomplete index? The admin surfaces
133
133
  # this as an honest "results may be incomplete, reindexing…".
134
134
  def partial?
135
- Thread.current[:loam_index_partial] == true
135
+ Thread.current[:open_loam_index_partial] == true
136
136
  end
137
137
 
138
138
  def partial=(value)
139
- Thread.current[:loam_index_partial] = value
139
+ Thread.current[:open_loam_index_partial] = value
140
140
  end
141
141
 
142
142
  # Enqueue a background reindex to heal a gap — DEDUPED so a hot gappy field
143
143
  # doesn't enqueue one per request. In-process dedup here (single-process
144
144
  # prototype); a DB/cache marker is the multi-process path.
145
145
  def schedule_reindex(model)
146
- key = [ Loam.tenant!.id, model.base_class.name ]
146
+ key = [ OpenLoam.tenant!.id, model.base_class.name ]
147
147
  return if pending_reindex.include?(key)
148
148
 
149
149
  pending_reindex << key
150
- Loam::CustomFieldReindexJob.perform_later(Loam.tenant!.id, model.base_class.name)
150
+ OpenLoam::CustomFieldReindexJob.perform_later(OpenLoam.tenant!.id, model.base_class.name)
151
151
  end
152
152
 
153
153
  def clear_pending(tenant_id, model_name)
@@ -188,7 +188,7 @@ module Loam
188
188
  # --- internals ---
189
189
 
190
190
  def base(model)
191
- Loam::CustomFieldValue.where(indexable_type: index_type(model)) # tenant-scoped by default_scope
191
+ OpenLoam::CustomFieldValue.where(indexable_type: index_type(model)) # tenant-scoped by default_scope
192
192
  end
193
193
 
194
194
  def index_type(model_or_class)
@@ -202,16 +202,19 @@ module Loam
202
202
  when "neq" then rows.where.not(value_text: value.to_s)
203
203
  when "contains" then rows.where("value_text LIKE ?", "%#{sanitize_like(value)}%")
204
204
  when "present" then rows.where.not(value_text: [ nil, "" ])
205
- when "gt" then numeric(rows, value, ">")
206
- when "gte" then numeric(rows, value, ">=")
207
- when "lt" then numeric(rows, value, "<")
208
- when "lte" then numeric(rows, value, "<=")
205
+ when "gt", "gte", "lt", "lte" then numeric(rows, value, op)
209
206
  end
210
207
  end
211
208
 
212
- # Numeric comparisons hit the indexed value_number column.
213
- def numeric(rows, value, operator)
214
- rows.where("value_number #{operator} ?", value.to_f)
209
+ # Numeric comparisons hit the indexed value_number column. Built through
210
+ # Arel rather than an interpolated fragment: `op` reaches here from
211
+ # params[:cf_op], and a comparison operator cannot be a bound parameter, so
212
+ # the only safe form is one that never becomes a string.
213
+ NUMERIC_PREDICATES = { "gt" => :gt, "gte" => :gteq, "lt" => :lt, "lte" => :lteq }.freeze
214
+
215
+ def numeric(rows, value, op)
216
+ column = OpenLoam::CustomFieldValue.arel_table[:value_number]
217
+ rows.where(column.public_send(NUMERIC_PREDICATES.fetch(op), value.to_f))
215
218
  end
216
219
 
217
220
  def row_for(record, definition, type)
@@ -235,7 +238,7 @@ module Loam
235
238
 
236
239
  def refuse_unknown_field!(model, field_key)
237
240
  known = model.respond_to?(:custom_field_definitions) && model.custom_field_definitions.exists?(name: field_key)
238
- raise Loam::Error, "no custom field #{field_key.inspect} on #{model.name}" unless known
241
+ raise OpenLoam::Error, "no custom field #{field_key.inspect} on #{model.name}" unless known
239
242
  end
240
243
 
241
244
  # THE oracle guard: filtering or sorting on a custom field the current role
@@ -243,16 +246,16 @@ module Loam
243
246
  # records match. Enforced against the current actor's role; a system/background
244
247
  # context (no actor — reindex, a business rule) is trusted and not gated.
245
248
  def refuse_unreadable_field!(model, field_key)
246
- actor = Loam::Current.actor
249
+ actor = OpenLoam::Current.actor
247
250
  return if actor.nil?
248
251
 
249
252
  definition = model.custom_field_definitions.find_by(name: field_key.to_s)
250
253
  return if definition.nil? # unknown field is refuse_unknown_field!'s job
251
254
 
252
- role = Loam::Membership.find_by(user_id: actor.id)&.role
255
+ role = OpenLoam::Membership.find_by(user_id: actor.id)&.role
253
256
  return if definition.readable_by?(role)
254
257
 
255
- raise Loam::FieldAccessError, "custom field #{field_key.inspect} is not readable by #{role.inspect}"
258
+ raise OpenLoam::FieldAccessError, "custom field #{field_key.inspect} is not readable by #{role.inspect}"
256
259
  end
257
260
 
258
261
  def sanitize_like(value)
@@ -1,35 +1,35 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # Runtime, migration-free fields — stored in the `custom_fields` json
3
3
  # column every generated entity table carries. Included automatically by
4
- # `rails g loam:entity`. Shape is declared at runtime via a
5
- # Loam::FieldDefinition (typically through the admin "Field definitions"
4
+ # `rails g open_loam:entity`. Shape is declared at runtime via a
5
+ # OpenLoam::FieldDefinition (typically through the admin "Field definitions"
6
6
  # screen), not in code — that's what makes it migration-free.
7
7
  #
8
8
  # equipment.custom_field(:serial_number) # => "SN-123" (cast)
9
9
  # equipment.set_custom_field(:serial_number, "SN-123")
10
10
  #
11
- # Reading/writing a name with no matching Loam::FieldDefinition for this
12
- # tenant + entity raises Loam::UnknownCustomFieldError immediately — the
13
- # same "fail loudly at the access site" philosophy as Loam.tenant!.
11
+ # Reading/writing a name with no matching OpenLoam::FieldDefinition for this
12
+ # tenant + entity raises OpenLoam::UnknownCustomFieldError immediately — the
13
+ # same "fail loudly at the access site" philosophy as OpenLoam.tenant!.
14
14
  module CustomFields
15
15
  extend ActiveSupport::Concern
16
16
 
17
17
  included do
18
- # Keep the read-model index (Loam::CustomFieldIndex) current so custom-field
18
+ # Keep the read-model index (OpenLoam::CustomFieldIndex) current so custom-field
19
19
  # filter/sort is index-backed. Re-project only when the custom_fields column
20
20
  # actually changed (a soft-delete touches only deleted_at, so the rows stay
21
21
  # and the base scope hides the record — the L-912 posture). A no-op for a
22
- # model with no custom_fields column. Guarded by respond_to? so Loam::
22
+ # model with no custom_fields column. Guarded by respond_to? so OpenLoam::
23
23
  # CustomFields stays includable in a plain class for a DSL-only test.
24
24
  if respond_to?(:after_save)
25
- after_save :loam_reindex_custom_fields, if: :loam_custom_fields_dirty?
26
- after_destroy :loam_deindex_custom_fields
25
+ after_save :open_loam_reindex_custom_fields, if: :open_loam_custom_fields_dirty?
26
+ after_destroy :open_loam_deindex_custom_fields
27
27
  end
28
28
  end
29
29
 
30
30
  class_methods do
31
31
  def custom_field_definitions
32
- Loam::FieldDefinition.where(entity_type: name)
32
+ OpenLoam::FieldDefinition.where(entity_type: name)
33
33
  end
34
34
  end
35
35
 
@@ -53,15 +53,15 @@ module Loam
53
53
  value = custom_field(name)
54
54
  return value unless definition.field_type == "dictionary"
55
55
 
56
- Loam::Dictionaries.label_for(definition.dictionary_key, value)
56
+ OpenLoam::Dictionaries.label_for(definition.dictionary_key, value)
57
57
  end
58
58
 
59
59
  private
60
60
 
61
- def loam_reindex_custom_fields = Loam::CustomFieldIndex.project(self)
62
- def loam_deindex_custom_fields = Loam::CustomFieldIndex.remove(self)
61
+ def open_loam_reindex_custom_fields = OpenLoam::CustomFieldIndex.project(self)
62
+ def open_loam_deindex_custom_fields = OpenLoam::CustomFieldIndex.remove(self)
63
63
 
64
- def loam_custom_fields_dirty?
64
+ def open_loam_custom_fields_dirty?
65
65
  return false unless self.class.respond_to?(:custom_field_definitions)
66
66
 
67
67
  # On create every column counts as changed; on update, only re-project when
@@ -71,8 +71,8 @@ module Loam
71
71
 
72
72
  def custom_field_definition!(name)
73
73
  custom_field_definitions_by_name.fetch(name.to_s) do
74
- raise UnknownCustomFieldError, "No Loam::FieldDefinition named #{name.inspect} for #{self.class.name} " \
75
- "(tenant #{Loam.tenant&.id.inspect})"
74
+ raise UnknownCustomFieldError, "No OpenLoam::FieldDefinition named #{name.inspect} for #{self.class.name} " \
75
+ "(tenant #{OpenLoam.tenant&.id.inspect})"
76
76
  end
77
77
  end
78
78
 
@@ -1,17 +1,17 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # Resolves the admin dashboard for the current tenant/actor: the ordered,
3
3
  # role-visible, computed widgets to show. Uses the tenant's configured
4
- # Loam::DashboardWidget rows (a manager arranges them); falls back to every
4
+ # OpenLoam::DashboardWidget rows (a manager arranges them); falls back to every
5
5
  # registered widget in registration order when the tenant has configured none,
6
6
  # so the dashboard is useful out of the box.
7
7
  module Dashboard
8
8
  module_function
9
9
 
10
10
  def for(actor:, role:)
11
- configured = Loam::DashboardWidget.active.ordered.pluck(:widget_key)
12
- keys = configured.presence || Loam::Widgets.keys
11
+ configured = OpenLoam::DashboardWidget.active.ordered.pluck(:widget_key)
12
+ keys = configured.presence || OpenLoam::Widgets.keys
13
13
 
14
- keys.filter_map { |key| Loam::Widgets.resolve(key, actor: actor, role: role) }
14
+ keys.filter_map { |key| OpenLoam::Widgets.resolve(key, actor: actor, role: role) }
15
15
  end
16
16
  end
17
17
  end
@@ -1,18 +1,18 @@
1
- module Loam
2
- # Read API for per-tenant managed lookup lists (Loam::Dictionary /
3
- # Loam::DictionaryEntry). Everything is tenant-scoped automatically and
4
- # memoized per request in Loam::Current.dictionary_cache (keyed with the tenant
1
+ module OpenLoam
2
+ # Read API for per-tenant managed lookup lists (OpenLoam::Dictionary /
3
+ # OpenLoam::DictionaryEntry). Everything is tenant-scoped automatically and
4
+ # memoized per request in OpenLoam::Current.dictionary_cache (keyed with the tenant
5
5
  # id, so an as_tenant switch mid-request can't read another tenant's list) —
6
- # the same simple prototype cache as Loam::Configs.
6
+ # the same simple prototype cache as OpenLoam::Configs.
7
7
  #
8
- # Loam::Dictionaries.entries("damage_severity") # active, ordered
9
- # Loam::Dictionaries.default("damage_severity") # the default entry
10
- # Loam::Dictionaries.label_for("damage_severity", "critical") # => "Critical"
8
+ # OpenLoam::Dictionaries.entries("damage_severity") # active, ordered
9
+ # OpenLoam::Dictionaries.default("damage_severity") # the default entry
10
+ # OpenLoam::Dictionaries.label_for("damage_severity", "critical") # => "Critical"
11
11
  module Dictionaries
12
12
  class << self
13
13
  # The dictionary for the current tenant, or nil.
14
14
  def get(key)
15
- memo([:dict, key.to_s]) { Loam::Dictionary.find_by(key: key.to_s) }
15
+ memo([:dict, key.to_s]) { OpenLoam::Dictionary.find_by(key: key.to_s) }
16
16
  end
17
17
 
18
18
  # Active entries, ordered by position. Empty for an unknown key.
@@ -38,14 +38,14 @@ module Loam
38
38
  end
39
39
 
40
40
  def clear_cache
41
- Loam::Current.dictionary_cache = {}
41
+ OpenLoam::Current.dictionary_cache = {}
42
42
  end
43
43
 
44
44
  private
45
45
 
46
46
  def memo(subkey)
47
- store = (Loam::Current.dictionary_cache ||= {})
48
- cache_key = subkey + [ Loam.tenant&.id ]
47
+ store = (OpenLoam::Current.dictionary_cache ||= {})
48
+ cache_key = subkey + [ OpenLoam.tenant&.id ]
49
49
  return store[cache_key] if store.key?(cache_key)
50
50
 
51
51
  store[cache_key] = yield
@@ -1,15 +1,15 @@
1
- module Loam
2
- # Durable (persistent) event subscribers — the twin of Loam::Events.subscribe,
1
+ module OpenLoam
2
+ # Durable (persistent) event subscribers — the twin of OpenLoam::Events.subscribe,
3
3
  # and the formal contract L-706 pins down.
4
4
  #
5
5
  # THE CONTRACT
6
- # * Ephemeral — Loam::Events.subscribe(&block): runs INLINE in the publisher's
6
+ # * Ephemeral — OpenLoam::Events.subscribe(&block): runs INLINE in the publisher's
7
7
  # thread, synchronously, best-effort. An exception in the block PROPAGATES
8
8
  # into whatever published the event. No persistence, no retry. Right for
9
9
  # cheap in-process fan-out (the webhook dispatcher enqueues its own jobs
10
10
  # this way).
11
- # * Persistent — Loam::DurableEvents.register(...): publishing commits a
12
- # Loam::EventDelivery row in the event's tenant, then hands the handler to a
11
+ # * Persistent — OpenLoam::DurableEvents.register(...): publishing commits a
12
+ # OpenLoam::EventDelivery row in the event's tenant, then hands the handler to a
13
13
  # background job. The handler's exception is CONTAINED in the job; delivery
14
14
  # is retried with backoff and, past MAX_ATTEMPTS, parked as `dead` for an
15
15
  # operator (Admin::EventDeliveriesController).
@@ -29,10 +29,10 @@ module Loam
29
29
  MAX_ATTEMPTS = 5
30
30
  # Backoff (seconds) indexed by attempt number; the last value is the cap.
31
31
  BACKOFF = [ 0, 60, 300, 1800, 7200 ].freeze
32
- SWEEP_KEY = "loam_event_redelivery_sweep".freeze
32
+ SWEEP_KEY = "open_loam_event_redelivery_sweep".freeze
33
33
 
34
34
  class << self
35
- # --- declarative registry (mirrors Loam::Scheduler.register) ---
35
+ # --- declarative registry (mirrors OpenLoam::Scheduler.register) ---
36
36
 
37
37
  # key: a stable identifier stored on every delivery row it produces.
38
38
  # to: an event name ("billing.invoice.paid") or a domain prefix
@@ -51,7 +51,7 @@ module Loam
51
51
  end
52
52
 
53
53
  def subscribers_for(event_name)
54
- registered.select { |entry| Loam::Events.pattern_matches?(entry[:pattern], event_name) }
54
+ registered.select { |entry| OpenLoam::Events.pattern_matches?(entry[:pattern], event_name) }
55
55
  end
56
56
 
57
57
  def handler_for(key)
@@ -59,19 +59,19 @@ module Loam
59
59
  entry && resolve(entry[:handler])
60
60
  end
61
61
 
62
- # --- wiring (mirrors Loam::Webhooks.subscribe!) ---
62
+ # --- wiring (mirrors OpenLoam::Webhooks.subscribe!) ---
63
63
 
64
- # Wired once from Loam::Engine. Idempotent: subscribing twice would persist
64
+ # Wired once from OpenLoam::Engine. Idempotent: subscribing twice would persist
65
65
  # every event twice.
66
66
  def subscribe!
67
- @subscription ||= Loam::Events.subscribe_all { |event_name, payload| capture(event_name, payload) }
67
+ @subscription ||= OpenLoam::Events.subscribe_all { |event_name, payload| capture(event_name, payload) }
68
68
  end
69
69
 
70
70
  # Persist a delivery row per matching durable subscriber, in the event's
71
71
  # tenant, then nudge a job per row. The ROW is the durable record; the job
72
72
  # is only the accelerator (the sweep redelivers rows whose job was lost).
73
73
  def capture(event_name, payload)
74
- tenant = Loam::Tenant.find_by(id: payload[:tenant_id])
74
+ tenant = OpenLoam::Tenant.find_by(id: payload[:tenant_id])
75
75
  return if tenant.nil? # nil-tenant events are not durably delivered (as Webhooks.dispatch)
76
76
 
77
77
  matches = subscribers_for(event_name)
@@ -81,18 +81,18 @@ module Loam
81
81
  # scalars by convention, never records (same rule as the webhook path).
82
82
  deliverable = payload.transform_keys(&:to_s)
83
83
 
84
- Loam.as_tenant(tenant) do
84
+ OpenLoam.as_tenant(tenant) do
85
85
  matches.each do |sub|
86
- delivery = Loam::EventDelivery.create!(
86
+ delivery = OpenLoam::EventDelivery.create!(
87
87
  subscriber_key: sub[:key], event_name: event_name.to_s,
88
88
  payload: deliverable, status: "pending", attempts: 0
89
89
  )
90
- Loam::EventDeliveryJob.perform_later(tenant.id, delivery.id)
90
+ OpenLoam::EventDeliveryJob.perform_later(tenant.id, delivery.id)
91
91
  end
92
92
  end
93
93
  end
94
94
 
95
- # --- delivery (called by Loam::EventDeliveryJob) ---
95
+ # --- delivery (called by OpenLoam::EventDeliveryJob) ---
96
96
 
97
97
  # Run one delivery and advance the row's state. Manual, ROW-STATE retries —
98
98
  # deliberately NOT ActiveJob retry_on, which keeps retry state in the queue
@@ -102,7 +102,7 @@ module Loam
102
102
  return unless delivery.status == "pending"
103
103
  return if delivery.next_attempt_at && delivery.next_attempt_at > now # duplicate nudge, backoff not elapsed
104
104
 
105
- Loam::Telemetry.span("durable_event_delivery",
105
+ OpenLoam::Telemetry.span("durable_event_delivery",
106
106
  subscriber_key: delivery.subscriber_key, event_name: delivery.event_name) do
107
107
  run_delivery(delivery, now)
108
108
  end
@@ -136,8 +136,8 @@ module Loam
136
136
  # no cross-tenant scan is needed.
137
137
  def redeliver_stuck(now: Time.current, limit: 500)
138
138
  count = 0
139
- Loam::EventDelivery.due(now).limit(limit).find_each do |delivery|
140
- Loam::EventDeliveryJob.perform_later(delivery.tenant_id, delivery.id)
139
+ OpenLoam::EventDelivery.due(now).limit(limit).find_each do |delivery|
140
+ OpenLoam::EventDeliveryJob.perform_later(delivery.tenant_id, delivery.id)
141
141
  count += 1
142
142
  end
143
143
  count