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,8 +1,8 @@
1
- module Loam
1
+ module OpenLoam
2
2
  module Generators
3
- # Shared primary-key shaping for `loam:install` and `loam:entity`.
3
+ # Shared primary-key shaping for `open_loam:install` and `open_loam:entity`.
4
4
  #
5
- # Loam's migrations used to spell every key `bigint` — implicitly in
5
+ # OpenLoam's migrations used to spell every key `bigint` — implicitly in
6
6
  # `create_table`, and explicitly in `t.references` and the polymorphic
7
7
  # `*_id` columns. In an app whose own tables use string or uuid keys the
8
8
  # foreign keys then do not line up with the tables they point at, and every
@@ -10,7 +10,7 @@ module Loam
10
10
  #
11
11
  # The key type is taken from, in order: an explicit --primary-key-type, the
12
12
  # host app's own `config.generators` setting (so an app that already told
13
- # Rails what its keys look like does not have to tell Loam separately), and
13
+ # Rails what its keys look like does not have to tell OpenLoam separately), and
14
14
  # bigint as the Rails default.
15
15
  #
16
16
  # The templates ask for the *rendered fragment* rather than the type, so
@@ -34,15 +34,15 @@ module Loam
34
34
 
35
35
  private
36
36
 
37
- def loam_key_type
38
- @loam_key_type ||= begin
37
+ def open_loam_key_type
38
+ @open_loam_key_type ||= begin
39
39
  requested = (options[:primary_key_type] || host_app_primary_key_type).to_s
40
40
  requested = "bigint" if requested.empty?
41
41
 
42
42
  unless SUPPORTED.include?(requested)
43
43
  raise Thor::Error,
44
- "Unsupported --primary-key-type #{requested.inspect}. Loam generates #{SUPPORTED.join(', ')}. " \
45
- "A key type Loam does not know how to size would produce migrations that look right and " \
44
+ "Unsupported --primary-key-type #{requested.inspect}. OpenLoam generates #{SUPPORTED.join(', ')}. " \
45
+ "A key type OpenLoam does not know how to size would produce migrations that look right and " \
46
46
  "do not line up with your tables."
47
47
  end
48
48
 
@@ -60,40 +60,40 @@ module Loam
60
60
  nil
61
61
  end
62
62
 
63
- def loam_key_limit
63
+ def open_loam_key_limit
64
64
  return options[:key_limit].to_i if options[:key_limit]
65
65
 
66
- loam_key_type == :string ? DEFAULT_STRING_LIMIT : nil
66
+ open_loam_key_type == :string ? DEFAULT_STRING_LIMIT : nil
67
67
  end
68
68
 
69
69
  # Appended to `create_table :x` — "" for bigint, so the default path is
70
70
  # byte-for-byte what it was.
71
- def loam_id_option
72
- return "" if loam_key_type == :bigint
71
+ def open_loam_id_option
72
+ return "" if open_loam_key_type == :bigint
73
73
 
74
- "#{loam_key_fragment.sub('type:', 'id:')}"
74
+ "#{open_loam_key_fragment.sub('type:', 'id:')}"
75
75
  end
76
76
 
77
77
  # Appended inside `t.references :x, ...`.
78
- def loam_type_option
79
- return "" if loam_key_type == :bigint
78
+ def open_loam_type_option
79
+ return "" if open_loam_key_type == :bigint
80
80
 
81
- loam_key_fragment
81
+ open_loam_key_fragment
82
82
  end
83
83
 
84
84
  # The bare column type for a polymorphic or un-constrained `*_id`, which
85
85
  # cannot use t.references.
86
- def loam_key_column_type
87
- loam_key_type
86
+ def open_loam_key_column_type
87
+ open_loam_key_type
88
88
  end
89
89
 
90
- def loam_key_limit_option
91
- loam_key_limit ? ", limit: #{loam_key_limit}" : ""
90
+ def open_loam_key_limit_option
91
+ open_loam_key_limit ? ", limit: #{open_loam_key_limit}" : ""
92
92
  end
93
93
 
94
- def loam_key_fragment
95
- fragment = ", type: :#{loam_key_type}"
96
- fragment += ", limit: #{loam_key_limit}" if loam_key_limit
94
+ def open_loam_key_fragment
95
+ fragment = ", type: :#{open_loam_key_type}"
96
+ fragment += ", limit: #{open_loam_key_limit}" if open_loam_key_limit
97
97
  fragment
98
98
  end
99
99
  end
data/lib/open-loam.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # Bundler auto-requires the gem's name ("open-loam") on load. The library's
2
- # entry point and whole namespace are `loam` / `Loam::` — the gem is only
2
+ # entry point and whole namespace are `open_loam` / `OpenLoam::` — the gem is only
3
3
  # *distributed* as open-loam (the plain `loam` name is taken on RubyGems). This
4
4
  # shim bridges the two so `gem "open-loam"` works with the default require.
5
- require_relative "loam"
5
+ require_relative "open_loam"
@@ -1,10 +1,10 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # Files on a record, via ActiveStorage:
3
3
  #
4
4
  # equipment.files.attach(io: File.open(path), filename: "manual.pdf")
5
5
  # equipment.files.each { |file| url_for(file) }
6
6
  #
7
- # KNOWN BOUNDARY: ActiveStorage blobs live in global tables that Loam does
7
+ # KNOWN BOUNDARY: ActiveStorage blobs live in global tables that OpenLoam does
8
8
  # NOT tenant-scope. The association is scoped (a record only ever lists its
9
9
  # own files, and the record is tenant-scoped), but a signed blob URL is a
10
10
  # bearer capability: whoever holds the link can fetch the file, with no
@@ -15,8 +15,8 @@ module Loam
15
15
 
16
16
  included do
17
17
  unless respond_to?(:has_many_attached)
18
- raise Loam::Error,
19
- "#{name} includes Loam::Attachable but ActiveStorage is not available. " \
18
+ raise OpenLoam::Error,
19
+ "#{name} includes OpenLoam::Attachable but ActiveStorage is not available. " \
20
20
  "Run `bin/rails active_storage:install && bin/rails db:migrate`, or remove the include " \
21
21
  "(this app was probably generated with --skip-active-storage)."
22
22
  end
@@ -1,6 +1,6 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # Audit-by-default. Included in every generated entity: create/update/destroy
3
- # writes a Loam::AuditRecord tagged with tenant + actor + changeset. "Who
3
+ # writes a OpenLoam::AuditRecord tagged with tenant + actor + changeset. "Who
4
4
  # changed the excavator's price, and when" is answered structurally, not by
5
5
  # remembering to log.
6
6
  module Auditable
@@ -9,54 +9,54 @@ module Loam
9
9
  IGNORED_ATTRIBUTES = %w[updated_at created_at].freeze
10
10
 
11
11
  included do
12
- after_create { loam_audit("create") }
13
- after_update { loam_audit(loam_audit_action) if loam_audit_changes.any? }
14
- after_destroy { loam_audit("destroy") }
12
+ after_create { open_loam_audit("create") }
13
+ after_update { open_loam_audit(open_loam_audit_action) if open_loam_audit_changes.any? }
14
+ after_destroy { open_loam_audit("destroy") }
15
15
  end
16
16
 
17
17
  private
18
18
 
19
- # Relabel the audit that the save inside the block writes. Loam::SoftDeletable
19
+ # Relabel the audit that the save inside the block writes. OpenLoam::SoftDeletable
20
20
  # uses this to record a soft-delete (an UPDATE) as "soft_delete"/"restore"
21
21
  # rather than the "update" it would otherwise be — one audit path, reused,
22
22
  # not a second one to keep in sync.
23
- def loam_audit_as(action)
24
- previous = @loam_audit_action
25
- @loam_audit_action = action
23
+ def open_loam_audit_as(action)
24
+ previous = @open_loam_audit_action
25
+ @open_loam_audit_action = action
26
26
  yield
27
27
  ensure
28
- @loam_audit_action = previous
28
+ @open_loam_audit_action = previous
29
29
  end
30
30
 
31
- def loam_audit_action
32
- @loam_audit_action || "update"
31
+ def open_loam_audit_action
32
+ @open_loam_audit_action || "update"
33
33
  end
34
34
 
35
- def loam_audit(action)
36
- Loam::AuditRecord.create!(
35
+ def open_loam_audit(action)
36
+ OpenLoam::AuditRecord.create!(
37
37
  auditable_type: self.class.name,
38
38
  auditable_id: id,
39
39
  action: action,
40
- actor_id: Loam::Current.actor&.id,
41
- changeset: action == "destroy" ? {} : loam_audit_changes
40
+ actor_id: OpenLoam::Current.actor&.id,
41
+ changeset: action == "destroy" ? {} : open_loam_audit_changes
42
42
  )
43
43
  end
44
44
 
45
- def loam_audit_changes
45
+ def open_loam_audit_changes
46
46
  changes = saved_changes.except(*IGNORED_ATTRIBUTES)
47
- loam_redact_encrypted(changes)
47
+ open_loam_redact_encrypted(changes)
48
48
  end
49
49
 
50
50
  # The audit trail must record the FACT that an encrypted field changed, never
51
51
  # its value — neither the plaintext nor the ciphertext (a ciphertext still
52
52
  # leaks length and, over time, correlations). So an encrypted column's change
53
53
  # becomes "[encrypted]", and its blind-index sibling is dropped entirely.
54
- # A no-op for models without Loam::Encryptable.
55
- def loam_redact_encrypted(changes)
56
- return changes unless self.class.respond_to?(:loam_encrypted_attributes)
54
+ # A no-op for models without OpenLoam::Encryptable.
55
+ def open_loam_redact_encrypted(changes)
56
+ return changes unless self.class.respond_to?(:open_loam_encrypted_attributes)
57
57
 
58
- encrypted = self.class.loam_encrypted_attributes.map(&:to_s)
59
- hash_columns = self.class.loam_searchable_encrypted_attributes.map { |name| "#{name}_hash" }
58
+ encrypted = self.class.open_loam_encrypted_attributes.map(&:to_s)
59
+ hash_columns = self.class.open_loam_searchable_encrypted_attributes.map { |name| "#{name}_hash" }
60
60
 
61
61
  changes.each_with_object({}) do |(column, values), redacted|
62
62
  next if hash_columns.include?(column)
@@ -1,12 +1,12 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # Rate-limiting + lockout for authentication, to blunt online brute-force of
3
3
  # passwords and (especially) 6-digit TOTP codes. A DB-backed counter keyed by
4
4
  # the submitted identifier: after N failures within a window, the identifier is
5
5
  # locked; a success clears the counter.
6
6
  #
7
- # Loam::AuthThrottle.locked?(email) # refuse if true
8
- # Loam::AuthThrottle.record_failure(email, kind: "password", ip: request.ip)
9
- # Loam::AuthThrottle.clear(email) # on success
7
+ # OpenLoam::AuthThrottle.locked?(email) # refuse if true
8
+ # OpenLoam::AuthThrottle.record_failure(email, kind: "password", ip: request.ip)
9
+ # OpenLoam::AuthThrottle.clear(email, kind: "password") # on success
10
10
  #
11
11
  # PER-IDENTIFIER is the primary defense (an attacker targets one account /
12
12
  # code). A per-ip throttle to blunt spraying across accounts is a clean
@@ -15,7 +15,7 @@ module Loam
15
15
  # (needs a cache store + a gem); this DB counter is the portable,
16
16
  # single-process-correct prototype.
17
17
  #
18
- # Thresholds come from Loam::Configs (per-tenant-overridable, but read globally
18
+ # Thresholds come from OpenLoam::Configs (per-tenant-overridable, but read globally
19
19
  # here at the auth layer with sane defaults):
20
20
  # security.max_auth_attempts (default 10)
21
21
  # security.auth_window_minutes (default 15) — failures counted in this window
@@ -24,15 +24,15 @@ module Loam
24
24
  module_function
25
25
 
26
26
  def max_attempts
27
- Loam::Configs.get("security.max_auth_attempts", default: 10).to_i
27
+ OpenLoam::Configs.get("security.max_auth_attempts", default: 10).to_i
28
28
  end
29
29
 
30
30
  def window
31
- Loam::Configs.get("security.auth_window_minutes", default: 15).to_i.minutes
31
+ OpenLoam::Configs.get("security.auth_window_minutes", default: 15).to_i.minutes
32
32
  end
33
33
 
34
34
  def lockout
35
- Loam::Configs.get("security.auth_lockout_minutes", default: 15).to_i.minutes
35
+ OpenLoam::Configs.get("security.auth_lockout_minutes", default: 15).to_i.minutes
36
36
  end
37
37
 
38
38
  # Log a failed attempt. Recorded for ANY submitted identifier — existing or
@@ -41,11 +41,14 @@ module Loam
41
41
  identifier = normalize(identifier)
42
42
  return if identifier.blank?
43
43
 
44
- Loam::AuthAttempt.create!(identifier: identifier, kind: kind.to_s, ip: ip)
44
+ OpenLoam::AuthAttempt.create!(identifier: identifier, kind: kind.to_s, ip: ip)
45
45
  end
46
46
 
47
47
  # Locked if there are >= max failures within the window. Old attempts age out
48
48
  # of the window automatically (the window query IS the expiry — no reaper).
49
+ #
50
+ # Both auth call sites ask WITHOUT a kind on purpose: failures on either
51
+ # factor lock both, so grinding TOTP also costs the attacker the password form.
49
52
  def locked?(identifier, kind: nil)
50
53
  recent_failures(identifier, kind: kind) >= max_attempts
51
54
  end
@@ -58,8 +61,13 @@ module Loam
58
61
 
59
62
  # Reset the counter — call on a SUCCESSFUL auth so a legitimate user who
60
63
  # eventually gets in isn't left throttled.
61
- def clear(identifier)
62
- attempts(identifier).delete_all
64
+ #
65
+ # Pass `kind:` — clearing every kind on one factor's success lets an attacker
66
+ # holding the password reset the TOTP counter before each guess.
67
+ def clear(identifier, kind: nil)
68
+ scope = attempts(identifier)
69
+ scope = scope.where(kind: Array(kind).map(&:to_s)) if kind
70
+ scope.delete_all
63
71
  end
64
72
 
65
73
  # Seconds until the identifier unlocks (for the "try again in N" message),
@@ -73,7 +81,7 @@ module Loam
73
81
  end
74
82
 
75
83
  def attempts(identifier)
76
- Loam::AuthAttempt.where(identifier: normalize(identifier))
84
+ OpenLoam::AuthAttempt.where(identifier: normalize(identifier))
77
85
  end
78
86
 
79
87
  def normalize(identifier)
@@ -1,4 +1,4 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # RFC 4648 Base32 — the encoding authenticator apps expect for a TOTP secret.
3
3
  # Ruby ships Base64 but not Base32, and this is ~15 lines, so no dependency.
4
4
  # No padding on encode (authenticators don't want it); decode ignores case,
@@ -1,11 +1,11 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # Datatable bulk actions over selected records — policy-checked PER record and
3
3
  # tenant-scoped. The ids are resolved THROUGH the tenant scope
4
4
  # (`model.where(id: ids)`), so a forged cross-tenant id is simply not found —
5
5
  # a bulk op can never reach another tenant's rows.
6
6
  #
7
- # Loam::Bulk.soft_delete(Equipment, params[:ids])
8
- # Loam::Bulk.set_field(Equipment, params[:ids], field: "status", value: "retired")
7
+ # OpenLoam::Bulk.soft_delete(Equipment, params[:ids])
8
+ # OpenLoam::Bulk.set_field(Equipment, params[:ids], field: "status", value: "retired")
9
9
  #
10
10
  # Returns the number of records actually affected (a record the actor may not
11
11
  # touch, or an id from another tenant, is silently skipped).
@@ -32,7 +32,7 @@ module Loam
32
32
  end
33
33
 
34
34
  # A relation for "export selected" — tenant-scoped, so it composes with
35
- # Loam::Export.csv and can't leak another tenant's rows.
35
+ # OpenLoam::Export.csv and can't leak another tenant's rows.
36
36
  def selected(model, ids)
37
37
  model.where(id: Array(ids))
38
38
  end
@@ -40,20 +40,20 @@ module Loam
40
40
  def each_permitted(model, ids)
41
41
  count = 0
42
42
  selected(model, ids).find_each do |record|
43
- count += 1 if yield(record, Loam::Policy.for(record))
43
+ count += 1 if yield(record, OpenLoam::Policy.for(record))
44
44
  end
45
45
  count
46
46
  end
47
47
 
48
48
  def writable_column?(model, field)
49
- model.column_names.include?(field) && Loam::Import::PLUMBING.exclude?(field) && !workflow_column?(model, field)
49
+ model.column_names.include?(field) && OpenLoam::Import::PLUMBING.exclude?(field) && !workflow_column?(model, field)
50
50
  end
51
51
 
52
52
  # A workflow status column may ONLY change through a transition (role-gated).
53
53
  # Skip it here so a bulk set-field can't self-"approve" (the model guard is
54
54
  # the backstop; this keeps the bulk op a clean no-op rather than an error).
55
55
  def workflow_column?(model, field)
56
- model.respond_to?(:loam_workflow) && model.loam_workflow&.column == field
56
+ model.respond_to?(:open_loam_workflow) && model.open_loam_workflow&.column == field
57
57
  end
58
58
  end
59
59
  end
@@ -1,4 +1,4 @@
1
- module Loam
1
+ module OpenLoam
2
2
  module BusinessRules
3
3
  # The typed action vocabulary — a FIXED, safe set the engine dispatches. No
4
4
  # arbitrary code: an action is a json descriptor with a `type` and known
@@ -9,7 +9,7 @@ module Loam
9
9
 
10
10
  # Run one action against the record that triggered the rule. Returns a
11
11
  # short label of what it did (for the execution log). `:veto` is the
12
- # block_transition signal (see Loam::BusinessRules.veto?).
12
+ # block_transition signal (see OpenLoam::BusinessRules.veto?).
13
13
  def self.run(action, record)
14
14
  case action["type"].to_s
15
15
  when "notify" then run_notify(action, record)
@@ -24,10 +24,10 @@ module Loam
24
24
  title = action["title"].to_s
25
25
  body = action["body"]
26
26
  if action["role"].present?
27
- Loam::Notifications.notify_role(action["role"], title: title, body: body)
27
+ OpenLoam::Notifications.notify_role(action["role"], title: title, body: body)
28
28
  elsif action["user_id"].present?
29
29
  user = User.find_by(id: action["user_id"])
30
- Loam::Notifications.notify(user, title: title, body: body) if user
30
+ OpenLoam::Notifications.notify(user, title: title, body: body) if user
31
31
  end
32
32
  "notify"
33
33
  end
@@ -35,7 +35,7 @@ module Loam
35
35
  # Publish validates the name format (raising InvalidEventNameError), so a
36
36
  # malformed event name is caught by the engine's per-rule isolation.
37
37
  def self.run_emit_event(action)
38
- Loam::Events.publish(action["name"], (action["payload"] || {}).symbolize_keys)
38
+ OpenLoam::Events.publish(action["name"], (action["payload"] || {}).symbolize_keys)
39
39
  "emit_event(#{action["name"]})"
40
40
  end
41
41
 
@@ -65,7 +65,7 @@ module Loam
65
65
  end
66
66
 
67
67
  def self.refuse_workflow_column!(record, field)
68
- return unless record.class.respond_to?(:loam_workflow) && record.class.loam_workflow&.column == field
68
+ return unless record.class.respond_to?(:open_loam_workflow) && record.class.open_loam_workflow&.column == field
69
69
 
70
70
  raise ArgumentError, "set_field must not write the workflow column #{field.inspect} — use a transition"
71
71
  end
@@ -83,7 +83,7 @@ module Loam
83
83
  # but never write one whose tenant is not the tenant in context.
84
84
  def self.refuse_cross_tenant!(record)
85
85
  return unless record.respond_to?(:tenant_id)
86
- return if record.tenant_id == Loam.tenant!.id
86
+ return if record.tenant_id == OpenLoam.tenant!.id
87
87
 
88
88
  raise ArgumentError, "set_field refuses a record outside the current tenant"
89
89
  end
@@ -1,4 +1,4 @@
1
- module Loam
1
+ module OpenLoam
2
2
  module BusinessRules
3
3
  # The SAFE condition evaluator — the critical security boundary of the rules
4
4
  # engine. A rule is DATA an admin edits, so this must NEVER run arbitrary
@@ -8,7 +8,7 @@ module Loam
8
8
  # else is refused — no `send` of arbitrary methods, no SQL, no eval.
9
9
  module Condition
10
10
  OPS = %w[eq neq gt gte lt lte in contains present blank].freeze
11
- REFUSED = :__loam_field_refused__
11
+ REFUSED = :__open_loam_field_refused__
12
12
 
13
13
  def self.matches?(node, record)
14
14
  node = node.is_a?(Hash) ? node : {}
@@ -48,7 +48,7 @@ module Loam
48
48
  elsif custom_field?(record, field)
49
49
  begin
50
50
  record.custom_field(field)
51
- rescue Loam::UnknownCustomFieldError
51
+ rescue OpenLoam::UnknownCustomFieldError
52
52
  REFUSED
53
53
  end
54
54
  else
@@ -59,8 +59,8 @@ module Loam
59
59
  def self.refused_column?(klass, field)
60
60
  return true if field == "tenant_id"
61
61
 
62
- klass.respond_to?(:loam_encrypted_attributes) &&
63
- klass.loam_encrypted_attributes.map(&:to_s).include?(field)
62
+ klass.respond_to?(:open_loam_encrypted_attributes) &&
63
+ klass.open_loam_encrypted_attributes.map(&:to_s).include?(field)
64
64
  end
65
65
 
66
66
  def self.custom_field?(record, field)
@@ -1,12 +1,12 @@
1
- require "loam/business_rules/condition"
2
- require "loam/business_rules/actions"
1
+ require "open_loam/business_rules/condition"
2
+ require "open_loam/business_rules/actions"
3
3
 
4
- module Loam
5
- # The engine that evaluates Loam::BusinessRule rows: WHEN a trigger fires and
4
+ module OpenLoam
5
+ # The engine that evaluates OpenLoam::BusinessRule rows: WHEN a trigger fires and
6
6
  # the condition holds, THEN run the actions — in the event's tenant context,
7
7
  # in priority order, each rule isolated so one failure never breaks dispatch.
8
8
  #
9
- # It listens to every Loam event (like Loam::Webhooks), so a tenant can add a
9
+ # It listens to every OpenLoam event (like OpenLoam::Webhooks), so a tenant can add a
10
10
  # rule from the admin without a reboot. Evaluation runs INLINE in whatever
11
11
  # published the event — cheap for the prototype; a job-based dispatcher is the
12
12
  # scaling path.
@@ -15,19 +15,19 @@ module Loam
15
15
  # rules, but only so deep, so a self-triggering rule terminates.
16
16
 
17
17
  class << self
18
- # Wired once from Loam::Engine. Idempotent — subscribing twice would run
19
- # every rule twice (see Loam::Webhooks.subscribe!).
18
+ # Wired once from OpenLoam::Engine. Idempotent — subscribing twice would run
19
+ # every rule twice (see OpenLoam::Webhooks.subscribe!).
20
20
  def subscribe!
21
- @subscription ||= Loam::Events.subscribe_all { |event_name, payload| on_event(event_name, payload) }
21
+ @subscription ||= OpenLoam::Events.subscribe_all { |event_name, payload| on_event(event_name, payload) }
22
22
  end
23
23
 
24
24
  def on_event(event_name, payload)
25
- tenant = Loam::Tenant.find_by(id: payload[:tenant_id])
25
+ tenant = OpenLoam::Tenant.find_by(id: payload[:tenant_id])
26
26
  return if tenant.nil?
27
27
 
28
- Loam.as_tenant(tenant) do
28
+ OpenLoam.as_tenant(tenant) do
29
29
  within_depth_guard do
30
- Loam::BusinessRule.active.by_priority.each do |rule|
30
+ OpenLoam::BusinessRule.active.by_priority.each do |rule|
31
31
  next unless rule.matches_trigger?(event_name)
32
32
 
33
33
  run_rule(rule, subject_for(rule, payload), event_name)
@@ -40,7 +40,7 @@ module Loam
40
40
  # active rules for the record's entity type (optionally filtered to a
41
41
  # trigger) in priority order.
42
42
  def evaluate(record, trigger: nil)
43
- Loam::BusinessRule.active.for_entity(record.class.base_class.name).by_priority.each do |rule|
43
+ OpenLoam::BusinessRule.active.for_entity(record.class.base_class.name).by_priority.each do |rule|
44
44
  next if trigger && !rule.matches_trigger?(trigger)
45
45
 
46
46
  run_rule(rule, record, trigger)
@@ -51,7 +51,7 @@ module Loam
51
51
  # holds, veto this transition? Consulted by a workflow BEFORE it moves (see
52
52
  # the demo's DamageReport). Evaluate-only — no actions run, nothing logged.
53
53
  def veto?(record, trigger)
54
- Loam::BusinessRule.active.for_entity(record.class.base_class.name).by_priority.any? do |rule|
54
+ OpenLoam::BusinessRule.active.for_entity(record.class.base_class.name).by_priority.any? do |rule|
55
55
  rule.matches_trigger?(trigger) &&
56
56
  rule.action_list.any? { |action| action["type"].to_s == "block_transition" } &&
57
57
  Condition.matches?(rule.condition_tree, record)
@@ -81,7 +81,7 @@ module Loam
81
81
  # deterministic "condition didn't match" ones (readable from the rule).
82
82
  return if !matched && error.nil?
83
83
 
84
- Loam::BusinessRuleRun.create!(
84
+ OpenLoam::BusinessRuleRun.create!(
85
85
  business_rule: rule,
86
86
  subject_type: record&.class&.base_class&.name,
87
87
  subject_id: record&.id,
@@ -102,21 +102,21 @@ module Loam
102
102
  # A rule may ONLY target a tenant-scoped model. This is the security
103
103
  # boundary: a global model like `User` has no tenant default scope, so a
104
104
  # find_by(id:) would reach across tenants — refusing anything that is not
105
- # a Loam::TenantRecord closes that, and a TenantRecord.find_by can only
105
+ # a OpenLoam::TenantRecord closes that, and a TenantRecord.find_by can only
106
106
  # ever return a record in the CURRENT tenant even if an id is injected.
107
- return nil unless klass.is_a?(Class) && klass < Loam::TenantRecord
107
+ return nil unless klass.is_a?(Class) && klass < OpenLoam::TenantRecord
108
108
 
109
109
  klass.find_by(id: payload[:id])
110
110
  end
111
111
 
112
112
  def within_depth_guard
113
- depth = Thread.current[:loam_rule_depth] ||= 0
113
+ depth = Thread.current[:open_loam_rule_depth] ||= 0
114
114
  return if depth >= MAX_DEPTH
115
115
 
116
- Thread.current[:loam_rule_depth] = depth + 1
116
+ Thread.current[:open_loam_rule_depth] = depth + 1
117
117
  yield
118
118
  ensure
119
- Thread.current[:loam_rule_depth] = depth if depth
119
+ Thread.current[:open_loam_rule_depth] = depth if depth
120
120
  end
121
121
  end
122
122
  end
@@ -1,8 +1,8 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # Discussion on a record:
3
3
  #
4
- # equipment.comment!("Hydraulics serviced") # as Loam.actor
5
- # equipment.loam_comments.oldest_first
4
+ # equipment.comment!("Hydraulics serviced") # as OpenLoam.actor
5
+ # equipment.open_loam_comments.oldest_first
6
6
  #
7
7
  # Included by every generated entity. Comments are tenant-scoped like their
8
8
  # subject, so a discussion can never be read from another tenant.
@@ -10,13 +10,13 @@ module Loam
10
10
  extend ActiveSupport::Concern
11
11
 
12
12
  included do
13
- has_many :loam_comments, as: :commentable, class_name: "Loam::Comment", dependent: :destroy
13
+ has_many :open_loam_comments, as: :commentable, class_name: "OpenLoam::Comment", dependent: :destroy
14
14
  end
15
15
 
16
16
  # Author defaults to whoever is acting — the same person the audit trail
17
17
  # and the event payload would name.
18
- def comment!(body, author: Loam::Current.actor)
19
- loam_comments.create!(body: body, author: author)
18
+ def comment!(body, author: OpenLoam::Current.actor)
19
+ open_loam_comments.create!(body: body, author: author)
20
20
  end
21
21
  end
22
22
  end