open-loam 0.1.0 → 0.2.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 (273) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +48 -8
  3. data/LICENSE +1 -1
  4. data/README.md +24 -24
  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/{loam → open_loam}/event_redelivery_sweep_job.rb +4 -4
  8. data/app/jobs/{loam → open_loam}/webhook_delivery_job.rb +7 -7
  9. data/app/models/{loam → open_loam}/api_token.rb +7 -7
  10. data/app/models/{loam → open_loam}/audit_record.rb +3 -3
  11. data/app/models/{loam → open_loam}/auth_attempt.rb +5 -5
  12. data/app/models/{loam → open_loam}/business_rule.rb +10 -10
  13. data/app/models/{loam → open_loam}/business_rule_run.rb +5 -5
  14. data/app/models/{loam → open_loam}/comment.rb +9 -9
  15. data/app/models/{loam → open_loam}/config.rb +7 -7
  16. data/app/models/{loam → open_loam}/custom_field_value.rb +5 -5
  17. data/app/models/{loam → open_loam}/dashboard_widget.rb +5 -5
  18. data/app/models/open_loam/dictionary.rb +20 -0
  19. data/app/models/{loam → open_loam}/dictionary_entry.rb +7 -7
  20. data/app/models/{loam → open_loam}/event_delivery.rb +4 -4
  21. data/app/models/{loam → open_loam}/field_definition.rb +8 -8
  22. data/app/models/{loam → open_loam}/inbound_webhook_delivery.rb +4 -4
  23. data/app/models/{loam → open_loam}/inbound_webhook_source.rb +13 -13
  24. data/app/models/{loam → open_loam}/membership.rb +6 -6
  25. data/app/models/{loam → open_loam}/mfa_credential.rb +8 -8
  26. data/app/models/{loam → open_loam}/notification.rb +10 -10
  27. data/app/models/{loam → open_loam}/pending_action.rb +23 -23
  28. data/app/models/{loam → open_loam}/perspective.rb +5 -5
  29. data/app/models/{loam → open_loam}/progress_job.rb +7 -7
  30. data/app/models/{loam → open_loam}/record_lock.rb +5 -5
  31. data/app/models/{loam → open_loam}/scheduled_job.rb +7 -7
  32. data/app/models/open_loam/search_token.rb +9 -0
  33. data/app/models/{loam → open_loam}/sso_identity.rb +4 -4
  34. data/app/models/{loam → open_loam}/sso_provider.rb +8 -8
  35. data/app/models/open_loam/tenant.rb +20 -0
  36. data/app/models/{loam → open_loam}/translation.rb +5 -5
  37. data/app/models/{loam → open_loam}/webhook_endpoint.rb +6 -6
  38. data/app/views/{loam → open_loam}/attachments/_attachments.html.erb +1 -1
  39. data/app/views/{loam → open_loam}/comments/_comments.html.erb +1 -1
  40. data/app/views/{loam → open_loam}/custom_fields/_fields.html.erb +2 -2
  41. data/lib/generators/{loam → open_loam}/entity/entity_generator.rb +9 -9
  42. data/lib/generators/{loam → open_loam}/entity/templates/api_controller.rb +1 -1
  43. data/lib/generators/{loam → open_loam}/entity/templates/controller.rb +21 -21
  44. data/lib/generators/{loam → open_loam}/entity/templates/entity_test.rb +12 -12
  45. data/lib/generators/{loam → open_loam}/entity/templates/migration.rb +5 -5
  46. data/lib/generators/{loam → open_loam}/entity/templates/model.rb +10 -10
  47. data/lib/generators/{loam → open_loam}/entity/templates/policy.rb +1 -1
  48. data/lib/generators/{loam → open_loam}/entity/templates/views/_form.html.erb +4 -4
  49. data/lib/generators/{loam → open_loam}/entity/templates/views/deleted.html.erb +7 -7
  50. data/lib/generators/open_loam/entity/templates/views/edit.html.erb +3 -0
  51. data/lib/generators/open_loam/entity/templates/views/index.html.erb +90 -0
  52. data/lib/generators/open_loam/entity/templates/views/new.html.erb +3 -0
  53. data/lib/generators/open_loam/entity/templates/views/show.html.erb +36 -0
  54. data/lib/generators/{loam → open_loam}/install/install_generator.rb +47 -47
  55. data/lib/generators/open_loam/install/templates/AGENTS.md +341 -0
  56. data/lib/generators/{loam → open_loam}/install/templates/admin/api_docs_controller.rb +2 -2
  57. data/lib/generators/{loam → open_loam}/install/templates/admin/api_docs_index.html.erb +1 -1
  58. data/lib/generators/{loam → open_loam}/install/templates/admin/api_tokens_controller.rb +1 -1
  59. data/lib/generators/{loam → open_loam}/install/templates/admin/base_controller.rb +38 -38
  60. data/lib/generators/{loam → open_loam}/install/templates/admin/business_rules_controller.rb +7 -7
  61. data/lib/generators/{loam → open_loam}/install/templates/admin/business_rules_form.html.erb +2 -2
  62. data/lib/generators/{loam → open_loam}/install/templates/admin/comments_controller.rb +4 -4
  63. data/lib/generators/{loam → open_loam}/install/templates/admin/configs_controller.rb +9 -9
  64. data/lib/generators/open_loam/install/templates/admin/dashboard_controller.rb +10 -0
  65. data/lib/generators/{loam → open_loam}/install/templates/admin/dashboard_widgets_controller.rb +5 -5
  66. data/lib/generators/{loam → open_loam}/install/templates/admin/dictionaries_controller.rb +5 -5
  67. data/lib/generators/{loam → open_loam}/install/templates/admin/dictionaries_form.html.erb +1 -1
  68. data/lib/generators/{loam → open_loam}/install/templates/admin/dictionary_entries_controller.rb +2 -2
  69. data/lib/generators/{loam → open_loam}/install/templates/admin/event_deliveries_controller.rb +5 -5
  70. data/lib/generators/{loam → open_loam}/install/templates/admin/event_deliveries_index.html.erb +1 -1
  71. data/lib/generators/{loam → open_loam}/install/templates/admin/events_controller.rb +5 -5
  72. data/lib/generators/{loam → open_loam}/install/templates/admin/features_controller.rb +9 -9
  73. data/lib/generators/{loam → open_loam}/install/templates/admin/field_definitions_controller.rb +5 -5
  74. data/lib/generators/{loam → open_loam}/install/templates/admin/field_definitions_new.html.erb +2 -2
  75. data/lib/generators/{loam → open_loam}/install/templates/admin/history_controller.rb +7 -7
  76. data/lib/generators/{loam → open_loam}/install/templates/admin/history_index.html.erb +1 -1
  77. data/lib/generators/{loam → open_loam}/install/templates/admin/imports_controller.rb +12 -12
  78. data/lib/generators/{loam → open_loam}/install/templates/admin/inbound_webhook_sources_controller.rb +6 -6
  79. data/lib/generators/{loam → open_loam}/install/templates/admin/inbound_webhook_sources_new.html.erb +2 -2
  80. data/lib/generators/open_loam/install/templates/admin/layout.html.erb +76 -0
  81. data/lib/generators/{loam → open_loam}/install/templates/admin/mfa_controller.rb +6 -6
  82. data/lib/generators/{loam → open_loam}/install/templates/admin/notifications_controller.rb +3 -3
  83. data/lib/generators/{loam → open_loam}/install/templates/admin/overrides_controller.rb +3 -3
  84. data/lib/generators/{loam → open_loam}/install/templates/admin/overrides_index.html.erb +2 -2
  85. data/lib/generators/{loam → open_loam}/install/templates/admin/pending_actions_controller.rb +3 -3
  86. data/lib/generators/{loam → open_loam}/install/templates/admin/perspectives_controller.rb +7 -7
  87. data/lib/generators/{loam → open_loam}/install/templates/admin/perspectives_index.html.erb +1 -1
  88. data/lib/generators/open_loam/install/templates/admin/progress_jobs_controller.rb +19 -0
  89. data/lib/generators/{loam → open_loam}/install/templates/admin/progress_jobs_index.html.erb +1 -1
  90. data/lib/generators/{loam → open_loam}/install/templates/admin/record_locks_controller.rb +4 -4
  91. data/lib/generators/{loam → open_loam}/install/templates/admin/scheduled_jobs_controller.rb +7 -7
  92. data/lib/generators/{loam → open_loam}/install/templates/admin/scheduled_jobs_form.html.erb +1 -1
  93. data/lib/generators/{loam → open_loam}/install/templates/admin/scheduled_jobs_index.html.erb +1 -1
  94. data/lib/generators/{loam → open_loam}/install/templates/admin/search_controller.rb +5 -5
  95. data/lib/generators/{loam → open_loam}/install/templates/admin/sessions_controller.rb +32 -32
  96. data/lib/generators/{loam → open_loam}/install/templates/admin/sso_providers_controller.rb +6 -6
  97. data/lib/generators/{loam → open_loam}/install/templates/admin/sso_providers_form.html.erb +2 -2
  98. data/lib/generators/{loam → open_loam}/install/templates/admin/sudo_controller.rb +5 -5
  99. data/lib/generators/{loam → open_loam}/install/templates/admin/sudo_new.html.erb +1 -1
  100. data/lib/generators/{loam → open_loam}/install/templates/admin/translations_controller.rb +7 -7
  101. data/lib/generators/{loam → open_loam}/install/templates/admin/translations_index.html.erb +3 -3
  102. data/lib/generators/{loam → open_loam}/install/templates/admin/webhook_endpoints_controller.rb +4 -4
  103. data/lib/generators/{loam → open_loam}/install/templates/admin/webhook_endpoints_index.html.erb +1 -1
  104. data/lib/generators/{loam → open_loam}/install/templates/admin.css +1 -1
  105. data/lib/generators/{loam → open_loam}/install/templates/api_base_controller.rb +15 -15
  106. data/lib/generators/{loam → open_loam}/install/templates/guardrails_test.rb +17 -17
  107. data/lib/generators/{loam → open_loam}/install/templates/import_job.rb +7 -7
  108. data/lib/generators/{loam → open_loam}/install/templates/inbound_webhooks_controller.rb +4 -4
  109. data/lib/generators/{loam → open_loam}/install/templates/initializer.rb +74 -74
  110. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_api_tokens.rb +15 -0
  111. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_audit_records.rb +14 -0
  112. 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
  113. 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
  114. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_comments.rb +13 -0
  115. data/lib/generators/{loam/install/templates/migrations/create_loam_configs.rb → open_loam/install/templates/migrations/create_open_loam_configs.rb} +7 -7
  116. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_custom_field_values.rb +19 -0
  117. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_dashboard_widgets.rb +12 -0
  118. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_dictionaries.rb +11 -0
  119. 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
  120. 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
  121. 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
  122. data/lib/generators/{loam/install/templates/migrations/create_loam_inbound_webhooks.rb → open_loam/install/templates/migrations/create_open_loam_inbound_webhooks.rb} +9 -9
  123. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_memberships.rb +11 -0
  124. 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
  125. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_notifications.rb +15 -0
  126. 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
  127. data/lib/generators/{loam/install/templates/migrations/create_loam_perspectives.rb → open_loam/install/templates/migrations/create_open_loam_perspectives.rb} +5 -5
  128. 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
  129. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_record_locks.rb +16 -0
  130. 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
  131. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_search_tokens.rb +18 -0
  132. data/lib/generators/{loam/install/templates/migrations/create_loam_sso_providers.rb → open_loam/install/templates/migrations/create_open_loam_sso_providers.rb} +12 -12
  133. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_tenants.rb +9 -0
  134. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_translations.rb +15 -0
  135. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_webhook_endpoints.rb +13 -0
  136. data/lib/generators/{loam → open_loam}/install/templates/migrations/create_users.rb +1 -1
  137. data/lib/generators/{loam → open_loam}/install/templates/user.rb +5 -5
  138. data/lib/generators/{loam → open_loam}/primary_key_options.rb +23 -23
  139. data/lib/open-loam.rb +2 -2
  140. data/lib/{loam → open_loam}/attachable.rb +4 -4
  141. data/lib/{loam → open_loam}/auditable.rb +23 -23
  142. data/lib/{loam → open_loam}/auth_throttle.rb +10 -10
  143. data/lib/{loam → open_loam}/base32.rb +1 -1
  144. data/lib/{loam → open_loam}/bulk.rb +7 -7
  145. data/lib/{loam → open_loam}/business_rules/actions.rb +7 -7
  146. data/lib/{loam → open_loam}/business_rules/condition.rb +5 -5
  147. data/lib/{loam → open_loam}/business_rules.rb +19 -19
  148. data/lib/{loam → open_loam}/commentable.rb +6 -6
  149. data/lib/{loam → open_loam}/configs.rb +19 -19
  150. data/lib/{loam → open_loam}/cron.rb +2 -2
  151. data/lib/{loam → open_loam}/csv.rb +2 -2
  152. data/lib/{loam → open_loam}/current.rb +5 -5
  153. data/lib/{loam → open_loam}/custom_field_index.rb +23 -23
  154. data/lib/{loam → open_loam}/custom_fields.rb +17 -17
  155. data/lib/{loam → open_loam}/dashboard.rb +5 -5
  156. data/lib/{loam → open_loam}/dictionaries.rb +12 -12
  157. data/lib/{loam → open_loam}/durable_events.rb +19 -19
  158. data/lib/{loam → open_loam}/encryptable.rb +33 -33
  159. data/lib/{loam → open_loam}/encryption/cipher.rb +3 -3
  160. data/lib/{loam → open_loam}/encryption/key_provider.rb +3 -3
  161. data/lib/{loam → open_loam}/encryption.rb +11 -11
  162. data/lib/open_loam/engine.rb +57 -0
  163. data/lib/{loam → open_loam}/enrichers.rb +5 -5
  164. data/lib/{loam → open_loam}/errors.rb +10 -10
  165. data/lib/{loam → open_loam}/eval.rb +2 -2
  166. data/lib/{loam → open_loam}/event_stream.rb +14 -14
  167. data/lib/{loam → open_loam}/eventful.rb +12 -12
  168. data/lib/{loam → open_loam}/events.rb +5 -5
  169. data/lib/{loam → open_loam}/export.rb +8 -8
  170. data/lib/{loam → open_loam}/features.rb +15 -15
  171. data/lib/{loam → open_loam}/generated_key.rb +6 -6
  172. data/lib/{loam → open_loam}/import.rb +12 -12
  173. data/lib/{loam → open_loam}/inbound_webhooks.rb +7 -7
  174. data/lib/{loam → open_loam}/lifecycle.rb +25 -25
  175. data/lib/{loam/locales/loam.en.yml → open_loam/locales/open_loam.en.yml} +5 -5
  176. data/lib/{loam → open_loam}/mcp/server.rb +9 -9
  177. data/lib/{loam → open_loam}/mcp.rb +21 -21
  178. data/lib/{loam → open_loam}/notifications.rb +5 -5
  179. data/lib/{loam → open_loam}/open_api.rb +16 -16
  180. data/lib/{loam → open_loam}/overrides.rb +10 -10
  181. data/lib/{loam → open_loam}/pending_actions.rb +9 -9
  182. data/lib/{loam → open_loam}/permissions.rb +8 -8
  183. data/lib/{loam → open_loam}/perspectives.rb +9 -9
  184. data/lib/{loam → open_loam}/policy.rb +6 -6
  185. data/lib/{loam → open_loam}/progress.rb +6 -6
  186. data/lib/{loam → open_loam}/record_locks.rb +9 -9
  187. data/lib/{loam → open_loam}/scheduler.rb +20 -20
  188. data/lib/{loam → open_loam}/search/driver.rb +3 -3
  189. data/lib/{loam → open_loam}/search/like_driver.rb +5 -5
  190. data/lib/{loam → open_loam}/search/token_driver.rb +15 -15
  191. data/lib/{loam → open_loam}/search.rb +5 -5
  192. data/lib/{loam → open_loam}/searchable.rb +22 -22
  193. data/lib/{loam → open_loam}/soft_deletable.rb +10 -10
  194. data/lib/{loam → open_loam}/sso/claims.rb +1 -1
  195. data/lib/{loam → open_loam}/sso/fake_provider.rb +1 -1
  196. data/lib/{loam → open_loam}/sso/http_client.rb +3 -3
  197. data/lib/{loam → open_loam}/sso/oidc_provider.rb +2 -2
  198. data/lib/{loam → open_loam}/sso.rb +17 -17
  199. data/lib/{loam → open_loam}/telemetry.rb +6 -6
  200. data/lib/{loam → open_loam}/tenant_record.rb +10 -10
  201. data/lib/{loam → open_loam}/test_helpers.rb +4 -4
  202. data/lib/{loam → open_loam}/totp.rb +3 -3
  203. data/lib/{loam → open_loam}/translatable.rb +24 -24
  204. data/lib/{loam → open_loam}/undo.rb +11 -11
  205. data/lib/open_loam/version.rb +3 -0
  206. data/lib/{loam → open_loam}/webhooks.rb +4 -4
  207. data/lib/{loam → open_loam}/widgets.rb +9 -9
  208. data/lib/{loam → open_loam}/workflow.rb +45 -45
  209. data/lib/open_loam.rb +122 -0
  210. data/lib/tasks/{loam.rake → open_loam.rake} +43 -43
  211. data/lib/tasks/{loam_eval.rake → open_loam_eval.rake} +7 -7
  212. data/lib/tasks/open_loam_mcp.rake +20 -0
  213. metadata +242 -242
  214. data/app/models/loam/dictionary.rb +0 -20
  215. data/app/models/loam/search_token.rb +0 -9
  216. data/app/models/loam/tenant.rb +0 -20
  217. data/lib/generators/loam/entity/templates/views/edit.html.erb +0 -3
  218. data/lib/generators/loam/entity/templates/views/index.html.erb +0 -90
  219. data/lib/generators/loam/entity/templates/views/new.html.erb +0 -3
  220. data/lib/generators/loam/entity/templates/views/show.html.erb +0 -36
  221. data/lib/generators/loam/install/templates/AGENTS.md +0 -341
  222. data/lib/generators/loam/install/templates/admin/dashboard_controller.rb +0 -10
  223. data/lib/generators/loam/install/templates/admin/layout.html.erb +0 -76
  224. data/lib/generators/loam/install/templates/admin/progress_jobs_controller.rb +0 -19
  225. data/lib/generators/loam/install/templates/migrations/create_loam_api_tokens.rb +0 -15
  226. data/lib/generators/loam/install/templates/migrations/create_loam_audit_records.rb +0 -14
  227. data/lib/generators/loam/install/templates/migrations/create_loam_comments.rb +0 -13
  228. data/lib/generators/loam/install/templates/migrations/create_loam_custom_field_values.rb +0 -19
  229. data/lib/generators/loam/install/templates/migrations/create_loam_dashboard_widgets.rb +0 -12
  230. data/lib/generators/loam/install/templates/migrations/create_loam_dictionaries.rb +0 -11
  231. data/lib/generators/loam/install/templates/migrations/create_loam_memberships.rb +0 -11
  232. data/lib/generators/loam/install/templates/migrations/create_loam_notifications.rb +0 -15
  233. data/lib/generators/loam/install/templates/migrations/create_loam_record_locks.rb +0 -16
  234. data/lib/generators/loam/install/templates/migrations/create_loam_search_tokens.rb +0 -18
  235. data/lib/generators/loam/install/templates/migrations/create_loam_tenants.rb +0 -9
  236. data/lib/generators/loam/install/templates/migrations/create_loam_translations.rb +0 -15
  237. data/lib/generators/loam/install/templates/migrations/create_loam_webhook_endpoints.rb +0 -13
  238. data/lib/loam/engine.rb +0 -57
  239. data/lib/loam/version.rb +0 -3
  240. data/lib/loam.rb +0 -122
  241. data/lib/tasks/loam_mcp.rake +0 -20
  242. /data/lib/generators/{loam → open_loam}/install/templates/admin/api_tokens_index.html.erb +0 -0
  243. /data/lib/generators/{loam → open_loam}/install/templates/admin/business_rules_edit.html.erb +0 -0
  244. /data/lib/generators/{loam → open_loam}/install/templates/admin/business_rules_index.html.erb +0 -0
  245. /data/lib/generators/{loam → open_loam}/install/templates/admin/business_rules_new.html.erb +0 -0
  246. /data/lib/generators/{loam → open_loam}/install/templates/admin/configs_edit.html.erb +0 -0
  247. /data/lib/generators/{loam → open_loam}/install/templates/admin/configs_index.html.erb +0 -0
  248. /data/lib/generators/{loam → open_loam}/install/templates/admin/dashboard_index.html.erb +0 -0
  249. /data/lib/generators/{loam → open_loam}/install/templates/admin/dashboard_widgets_index.html.erb +0 -0
  250. /data/lib/generators/{loam → open_loam}/install/templates/admin/dictionaries_edit.html.erb +0 -0
  251. /data/lib/generators/{loam → open_loam}/install/templates/admin/dictionaries_index.html.erb +0 -0
  252. /data/lib/generators/{loam → open_loam}/install/templates/admin/dictionaries_new.html.erb +0 -0
  253. /data/lib/generators/{loam → open_loam}/install/templates/admin/features_index.html.erb +0 -0
  254. /data/lib/generators/{loam → open_loam}/install/templates/admin/field_definitions_index.html.erb +0 -0
  255. /data/lib/generators/{loam → open_loam}/install/templates/admin/imports_new.html.erb +0 -0
  256. /data/lib/generators/{loam → open_loam}/install/templates/admin/imports_preview.html.erb +0 -0
  257. /data/lib/generators/{loam → open_loam}/install/templates/admin/imports_summary.html.erb +0 -0
  258. /data/lib/generators/{loam → open_loam}/install/templates/admin/inbound_webhook_sources_index.html.erb +0 -0
  259. /data/lib/generators/{loam → open_loam}/install/templates/admin/mfa_activated.html.erb +0 -0
  260. /data/lib/generators/{loam → open_loam}/install/templates/admin/mfa_new.html.erb +0 -0
  261. /data/lib/generators/{loam → open_loam}/install/templates/admin/mfa_show.html.erb +0 -0
  262. /data/lib/generators/{loam → open_loam}/install/templates/admin/notifications_index.html.erb +0 -0
  263. /data/lib/generators/{loam → open_loam}/install/templates/admin/pagination.rb +0 -0
  264. /data/lib/generators/{loam → open_loam}/install/templates/admin/pending_actions_index.html.erb +0 -0
  265. /data/lib/generators/{loam → open_loam}/install/templates/admin/scheduled_jobs_edit.html.erb +0 -0
  266. /data/lib/generators/{loam → open_loam}/install/templates/admin/scheduled_jobs_new.html.erb +0 -0
  267. /data/lib/generators/{loam → open_loam}/install/templates/admin/search_index.html.erb +0 -0
  268. /data/lib/generators/{loam → open_loam}/install/templates/admin/sessions_mfa_challenge.html.erb +0 -0
  269. /data/lib/generators/{loam → open_loam}/install/templates/admin/sessions_new.html.erb +0 -0
  270. /data/lib/generators/{loam → open_loam}/install/templates/admin/sso_providers_edit.html.erb +0 -0
  271. /data/lib/generators/{loam → open_loam}/install/templates/admin/sso_providers_index.html.erb +0 -0
  272. /data/lib/generators/{loam → open_loam}/install/templates/admin/sso_providers_new.html.erb +0 -0
  273. /data/lib/generators/{loam → open_loam}/install/templates/admin/webhook_endpoints_new.html.erb +0 -0
@@ -1,20 +1,20 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # A tenant's connection to an external identity provider (OIDC). Per-tenant
3
3
  # config: each tenant/org wires its OWN IdP, so the client_secret is encrypted
4
4
  # under the tenant key (the default Encryptable scope) — unlike MFA, which is
5
5
  # user-keyed. Home-realm discovery matches an email `domain` to the owning
6
- # provider at the sign-in page (see Loam::Sso.provider_for).
7
- class SsoProvider < Loam::TenantRecord
8
- self.table_name = "loam_sso_providers"
6
+ # provider at the sign-in page (see OpenLoam::Sso.provider_for).
7
+ class SsoProvider < OpenLoam::TenantRecord
8
+ self.table_name = "open_loam_sso_providers"
9
9
 
10
- include Loam::Auditable # config changes are audited; the secret is redacted
11
- include Loam::Encryptable
10
+ include OpenLoam::Auditable # config changes are audited; the secret is redacted
11
+ include OpenLoam::Encryptable
12
12
 
13
13
  # Tenant-scoped key (default): SSO config belongs to the tenant, and the
14
14
  # secret is only ever read inside that tenant's context on callback.
15
15
  encrypts :client_secret
16
16
 
17
- has_many :identities, class_name: "Loam::SsoIdentity",
17
+ has_many :identities, class_name: "OpenLoam::SsoIdentity",
18
18
  foreign_key: :sso_provider_id, dependent: :delete_all
19
19
 
20
20
  validates :name, :protocol, :domain, :jit_role, presence: true
@@ -27,7 +27,7 @@ module Loam
27
27
 
28
28
  scope :active, -> { where(active: true) }
29
29
 
30
- # IdP group -> Loam role, first match wins; falls back to jit_role.
30
+ # IdP group -> OpenLoam role, first match wins; falls back to jit_role.
31
31
  def group_roles
32
32
  group_role_map.is_a?(Hash) ? group_role_map : {}
33
33
  end
@@ -0,0 +1,20 @@
1
+ module OpenLoam
2
+ # The tenant itself is deliberately NOT tenant-scoped — it is the axis the
3
+ # rest of the system is scoped by.
4
+ class Tenant < ActiveRecord::Base
5
+ include OpenLoam::GeneratedKey
6
+ self.table_name = "open_loam_tenants"
7
+
8
+ validates :name, presence: true
9
+ validates :slug, presence: true, uniqueness: true
10
+
11
+ has_many :memberships, class_name: "OpenLoam::Membership", dependent: :delete_all
12
+
13
+ # A new tenant is never empty: whatever the app registered via
14
+ # OpenLoam.on_tenant_created runs here, inside this tenant's context. After
15
+ # commit, so callbacks see a persisted tenant and may enqueue jobs.
16
+ # `bin/rails open_loam:sync` re-runs the same callbacks for existing tenants —
17
+ # which is why they must be idempotent (see OpenLoam::Lifecycle).
18
+ after_create_commit { OpenLoam::Lifecycle.run_tenant_created(self) }
19
+ end
20
+ end
@@ -1,13 +1,13 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # A per-locale override for ONE field of ONE record — the storage behind
3
- # Loam::Translatable. The record's own column holds the base (default-locale)
3
+ # OpenLoam::Translatable. The record's own column holds the base (default-locale)
4
4
  # value; a translation row overrides it for a specific locale. Additive:
5
5
  # writing a translation never touches the base column, so the base value is
6
6
  # never lost. Tenant-scoped and audited.
7
- class Translation < Loam::TenantRecord
8
- self.table_name = "loam_translations"
7
+ class Translation < OpenLoam::TenantRecord
8
+ self.table_name = "open_loam_translations"
9
9
 
10
- include Loam::Auditable
10
+ include OpenLoam::Auditable
11
11
 
12
12
  belongs_to :translatable, polymorphic: true
13
13
 
@@ -1,12 +1,12 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # Where one tenant wants its domain events delivered. `event_pattern` uses
3
- # the same rule as Loam::Events.subscribe — a trailing dot is a domain prefix
3
+ # the same rule as OpenLoam::Events.subscribe — a trailing dot is a domain prefix
4
4
  # ("rental."), anything else is an exact event name.
5
5
  #
6
- # The secret signs every delivery (X-Loam-Signature), so a receiver can tell
6
+ # The secret signs every delivery (X-OpenLoam-Signature), so a receiver can tell
7
7
  # a real call from a forged one.
8
- class WebhookEndpoint < Loam::TenantRecord
9
- self.table_name = "loam_webhook_endpoints"
8
+ class WebhookEndpoint < OpenLoam::TenantRecord
9
+ self.table_name = "open_loam_webhook_endpoints"
10
10
 
11
11
  validates :url, presence: true, format: { with: %r{\Ahttps?://}, message: "must start with http:// or https://" }
12
12
  validates :event_pattern, presence: true
@@ -20,7 +20,7 @@ module Loam
20
20
  # One matcher, shared with the event bus, so "rental." means the same thing
21
21
  # to a subscriber and to a webhook.
22
22
  def matches?(event_name)
23
- Loam::Events.pattern_matches?(event_pattern, event_name)
23
+ OpenLoam::Events.pattern_matches?(event_pattern, event_name)
24
24
  end
25
25
  end
26
26
  end
@@ -2,7 +2,7 @@
2
2
  Uploading happens through the entity's own form (it is an update, and the
3
3
  entity's policy decides who may do it); this partial only lists what is
4
4
  already attached. Note that these links are signed URLs, not tenant-checked
5
- addresses — see Loam::Attachable. %>
5
+ addresses — see OpenLoam::Attachable. %>
6
6
  <h2>Files</h2>
7
7
 
8
8
  <% if record.files.attached? %>
@@ -8,7 +8,7 @@
8
8
  <p style="color:#b00"><%= flash[:alert] %></p>
9
9
  <% end %>
10
10
 
11
- <% record.loam_comments.oldest_first.each do |comment| %>
11
+ <% record.open_loam_comments.oldest_first.each do |comment| %>
12
12
  <p>
13
13
  <strong><%= User.find_by(id: comment.author_id)&.name || "—" %></strong>
14
14
  <em><%= comment.created_at.to_fs(:short) %></em><br>
@@ -20,14 +20,14 @@
20
20
  <% when "integer", "decimal" %>
21
21
  <%= number_field_tag param_name, value %>
22
22
  <% when "dictionary" %>
23
- <% options = Loam::Dictionaries.entries(definition.dictionary_key).map { |e| [e.label, e.value] } %>
23
+ <% options = OpenLoam::Dictionaries.entries(definition.dictionary_key).map { |e| [e.label, e.value] } %>
24
24
  <%= select_tag param_name, options_for_select(options, value), include_blank: true, id: param_name %>
25
25
  <% else %>
26
26
  <%= text_field_tag param_name, value %>
27
27
  <% end %>
28
28
  <% else %>
29
29
  <%# A dictionary field shows its entry label, not the raw stored code. %>
30
- <% display = definition.field_type == "dictionary" ? Loam::Dictionaries.label_for(definition.dictionary_key, value) : value %>
30
+ <% display = definition.field_type == "dictionary" ? OpenLoam::Dictionaries.label_for(definition.dictionary_key, value) : value %>
31
31
  <%= definition.name.humanize %>: <%= display %>
32
32
  <% if mode == :edit %><em>(read-only for your role)</em><% end %>
33
33
  <% end %>
@@ -1,19 +1,19 @@
1
1
  require "rails/generators"
2
2
  require "rails/generators/active_record"
3
- require "generators/loam/primary_key_options"
3
+ require "generators/open_loam/primary_key_options"
4
4
 
5
- module Loam
5
+ module OpenLoam
6
6
  module Generators
7
- # `rails g loam:entity Equipment name:string daily_rate:decimal --domain rental`
7
+ # `rails g open_loam:entity Equipment name:string daily_rate:decimal --domain rental`
8
8
  #
9
9
  # THE interface for adding a business entity — for humans and AI agents
10
10
  # alike. One command produces a tenant-scoped, audited, evented model, its
11
11
  # policy, an admin screen, and the isolation tests that prove the
12
- # guardrails hold. No decisions about how tenancy/permissions work: Loam
12
+ # guardrails hold. No decisions about how tenancy/permissions work: OpenLoam
13
13
  # already decided.
14
14
  class EntityGenerator < Rails::Generators::NamedBase
15
15
  include ActiveRecord::Generators::Migration
16
- include Loam::Generators::PrimaryKeyOptions
16
+ include OpenLoam::Generators::PrimaryKeyOptions
17
17
 
18
18
  source_root File.expand_path("templates", __dir__)
19
19
 
@@ -21,7 +21,7 @@ module Loam
21
21
  class_option :domain, type: :string, default: "app",
22
22
  desc: "Event domain prefix (-> domain.entity.created)"
23
23
  class_option :encrypt, type: :array, default: [], banner: "field field",
24
- desc: "Encrypt these fields at rest, per tenant (Loam::Encryptable)"
24
+ desc: "Encrypt these fields at rest, per tenant (OpenLoam::Encryptable)"
25
25
  class_option :encrypt_searchable, type: :array, default: [], banner: "field field",
26
26
  desc: "Encrypt + add a blind index for exact-match lookup"
27
27
 
@@ -59,7 +59,7 @@ module Loam
59
59
  end
60
60
 
61
61
  # `namespace:` makes Rails inject the route INTO the existing
62
- # `namespace :admin do` block (the one loam:install wrote) instead of
62
+ # `namespace :admin do` block (the one open_loam:install wrote) instead of
63
63
  # stacking one admin block per entity, and falls back to creating the
64
64
  # block when there is none. Re-running the generator is a no-op: the
65
65
  # injection is `force: false`, so identical routing code is skipped.
@@ -70,7 +70,7 @@ module Loam
70
70
  # identical `resources :gadgets` lines would leave the second one out.
71
71
  #
72
72
  # The admin resource carries the soft-delete recycle bin: `deleted` lists
73
- # `only_deleted`, `restore` brings one back (see Loam::SoftDeletable).
73
+ # `only_deleted`, `restore` brings one back (see OpenLoam::SoftDeletable).
74
74
  def add_route
75
75
  route <<~RUBY.strip, namespace: :admin
76
76
  resources :#{plural_file_name} do
@@ -101,7 +101,7 @@ module Loam
101
101
  attributes.map(&:name)
102
102
  end
103
103
 
104
- # Only text-ish columns are worth a LIKE (see Loam::Searchable), so an
104
+ # Only text-ish columns are worth a LIKE (see OpenLoam::Searchable), so an
105
105
  # entity with none gets no `searchable_by` declaration and stays out of
106
106
  # the global search. Encrypted columns are excluded: ciphertext is
107
107
  # meaningless to LIKE, and declaring both would raise at class load.
@@ -9,7 +9,7 @@ module Api
9
9
 
10
10
  def index
11
11
  records = <%= class_name %>.order(created_at: :desc).to_a
12
- enriched = Loam::Enrichers.enrich_many(records) # one batched pass, no N+1
12
+ enriched = OpenLoam::Enrichers.enrich_many(records) # one batched pass, no N+1
13
13
  render json: records.map { |record| entity_json(record, enrichments: enriched[record.id]) }
14
14
  end
15
15
 
@@ -15,34 +15,34 @@ module Admin
15
15
  end
16
16
 
17
17
  def index
18
- @perspective = Loam::Perspectives.resolve("<%= class_name %>", user: current_actor, id: params[:perspective_id])
19
- @perspectives = Loam::Perspectives.visible_to("<%= class_name %>", user: current_actor)
18
+ @perspective = OpenLoam::Perspectives.resolve("<%= class_name %>", user: current_actor, id: params[:perspective_id])
19
+ @perspectives = OpenLoam::Perspectives.visible_to("<%= class_name %>", user: current_actor)
20
20
  @records, @page, @has_next = paginate(index_scope)
21
- @index_partial = params[:cf_field].present? && Loam::CustomFieldIndex.partial? # incomplete-index warning (L-919)
21
+ @index_partial = params[:cf_field].present? && OpenLoam::CustomFieldIndex.partial? # incomplete-index warning (L-919)
22
22
  end
23
23
 
24
24
  # CSV of the CURRENT filtered/perspective view — manager-only, policy- and
25
- # encryption-aware (Loam::Export).
25
+ # encryption-aware (OpenLoam::Export).
26
26
  def export
27
27
  require_role!(:manager)
28
- send_data Loam::Export.csv(index_scope, actor: current_actor),
28
+ send_data OpenLoam::Export.csv(index_scope, actor: current_actor),
29
29
  filename: "<%= plural_name %>-#{Date.current}.csv", type: "text/csv"
30
30
  end
31
31
 
32
32
  # Datatable bulk actions on the selected ids — each is policy-checked per
33
- # record and tenant-scoped (Loam::Bulk). Zero selection is a no-op.
33
+ # record and tenant-scoped (OpenLoam::Bulk). Zero selection is a no-op.
34
34
  def bulk
35
35
  ids = Array(params[:ids])
36
36
  case params[:bulk_action]
37
37
  when "soft_delete"
38
- count = Loam::Bulk.soft_delete(<%= class_name %>, ids)
38
+ count = OpenLoam::Bulk.soft_delete(<%= class_name %>, ids)
39
39
  redirect_to polymorphic_path([:admin, <%= class_name %>]), notice: "Deleted #{count} record(s)."
40
40
  when "set_field"
41
- count = Loam::Bulk.set_field(<%= class_name %>, ids, field: params[:field], value: params[:value])
41
+ count = OpenLoam::Bulk.set_field(<%= class_name %>, ids, field: params[:field], value: params[:value])
42
42
  redirect_to polymorphic_path([:admin, <%= class_name %>]), notice: "Updated #{count} record(s)."
43
43
  when "export"
44
44
  require_role!(:manager) # same gate as the dedicated export action
45
- send_data Loam::Export.csv(Loam::Bulk.selected(<%= class_name %>, ids), actor: current_actor),
45
+ send_data OpenLoam::Export.csv(OpenLoam::Bulk.selected(<%= class_name %>, ids), actor: current_actor),
46
46
  filename: "<%= plural_name %>-selected.csv", type: "text/csv"
47
47
  else
48
48
  redirect_to polymorphic_path([:admin, <%= class_name %>]), alert: "Unknown bulk action."
@@ -75,7 +75,7 @@ module Admin
75
75
  attach_files!(@record, policy)
76
76
 
77
77
  if @record.save
78
- redirect_to [:admin, @record], notice: t("loam.flash.created", name: <%= class_name %>.model_name.human)
78
+ redirect_to [:admin, @record], notice: t("open_loam.flash.created", name: <%= class_name %>.model_name.human)
79
79
  else
80
80
  render :new, status: :unprocessable_entity
81
81
  end
@@ -84,7 +84,7 @@ module Admin
84
84
  def edit
85
85
  authorize!(policy_for(@record), :update?)
86
86
  # Take the advisory lock (courtesy) or learn who holds it, for the banner.
87
- @lock = Loam::RecordLocks.acquire(@record, by: current_actor) || Loam::RecordLocks.active_lock(@record)
87
+ @lock = OpenLoam::RecordLocks.acquire(@record, by: current_actor) || OpenLoam::RecordLocks.active_lock(@record)
88
88
  end
89
89
 
90
90
  def update
@@ -94,15 +94,15 @@ module Admin
94
94
  attach_files!(@record, policy)
95
95
 
96
96
  if @record.update(permitted_params(policy))
97
- Loam::RecordLocks.release(@record, by: current_actor)
98
- redirect_to [:admin, @record], notice: t("loam.flash.updated", name: <%= class_name %>.model_name.human)
97
+ OpenLoam::RecordLocks.release(@record, by: current_actor)
98
+ redirect_to [:admin, @record], notice: t("open_loam.flash.updated", name: <%= class_name %>.model_name.human)
99
99
  else
100
100
  render :edit, status: :unprocessable_entity
101
101
  end
102
102
  rescue ActiveRecord::StaleObjectError
103
103
  # Someone saved between open and submit — show the diff, reload fresh, retry.
104
104
  stale_conflict!(@record, FIELDS)
105
- @lock = Loam::RecordLocks.acquire(@record, by: current_actor) || Loam::RecordLocks.active_lock(@record)
105
+ @lock = OpenLoam::RecordLocks.acquire(@record, by: current_actor) || OpenLoam::RecordLocks.active_lock(@record)
106
106
  render :edit, status: :conflict
107
107
  end
108
108
 
@@ -111,8 +111,8 @@ module Admin
111
111
  def destroy
112
112
  authorize!(policy_for(@record), :destroy?)
113
113
  @record.soft_delete!
114
- Loam::RecordLocks.release(@record, by: current_actor)
115
- redirect_to [:admin, <%= class_name %>], notice: t("loam.flash.destroyed", name: <%= class_name %>.model_name.human)
114
+ OpenLoam::RecordLocks.release(@record, by: current_actor)
115
+ redirect_to [:admin, <%= class_name %>], notice: t("open_loam.flash.destroyed", name: <%= class_name %>.model_name.human)
116
116
  end
117
117
 
118
118
  # Restore looks through the deleted rows — the default scope hides them, so a
@@ -121,7 +121,7 @@ module Admin
121
121
  @record = <%= class_name %>.with_deleted.find(params[:id])
122
122
  authorize!(policy_for(@record), :update?)
123
123
  @record.restore!
124
- redirect_to [:deleted, :admin, <%= class_name %>], notice: t("loam.flash.restored", name: <%= class_name %>.model_name.human)
124
+ redirect_to [:deleted, :admin, <%= class_name %>], notice: t("open_loam.flash.restored", name: <%= class_name %>.model_name.human)
125
125
  end
126
126
 
127
127
  private
@@ -134,7 +134,7 @@ module Admin
134
134
  # export so the CSV matches what the manager is looking at.
135
135
  def index_scope
136
136
  scope = <%= class_name %>.all
137
- perspective = Loam::Perspectives.resolve("<%= class_name %>", user: current_actor, id: params[:perspective_id])
137
+ perspective = OpenLoam::Perspectives.resolve("<%= class_name %>", user: current_actor, id: params[:perspective_id])
138
138
  scope = perspective.apply(scope) if perspective
139
139
  <% if searchable_attributes.any? -%>
140
140
  scope = scope.search(params[:q])
@@ -160,13 +160,13 @@ module Admin
160
160
  end
161
161
 
162
162
  # A custom-field filter routed through the read-model index
163
- # (Loam::CustomFieldIndex) — index-backed, not a JSON scan. An unknown field
163
+ # (OpenLoam::CustomFieldIndex) — index-backed, not a JSON scan. An unknown field
164
164
  # is ignored rather than raising.
165
165
  def apply_custom_field_filter(scope)
166
166
  return scope if params[:cf_field].blank?
167
167
 
168
- scope.merge(Loam::CustomFieldIndex.filter(<%= class_name %>, params[:cf_field], params[:cf_op].presence || "eq", params[:cf_value]))
169
- rescue Loam::Error
168
+ scope.merge(OpenLoam::CustomFieldIndex.filter(<%= class_name %>, params[:cf_field], params[:cf_op].presence || "eq", params[:cf_value]))
169
+ rescue OpenLoam::Error
170
170
  scope
171
171
  end
172
172
 
@@ -1,19 +1,19 @@
1
1
  require "test_helper"
2
2
 
3
- # Generated guardrail tests for <%= class_name %>. They prove the Loam
3
+ # Generated guardrail tests for <%= class_name %>. They prove the OpenLoam
4
4
  # invariants hold for THIS entity: tenant isolation, loud failure without
5
5
  # context, audit-by-default, lifecycle events, membership-gated policies.
6
6
  # Extend freely; never delete.
7
- class <%= class_name %>LoamTest < ActiveSupport::TestCase
7
+ class <%= class_name %>OpenLoamTest < ActiveSupport::TestCase
8
8
  setup do
9
- @tenant_a = Loam::Tenant.create!(name: "Tenant A", slug: "a-<%= file_name %>")
10
- @tenant_b = Loam::Tenant.create!(name: "Tenant B", slug: "b-<%= file_name %>")
9
+ @tenant_a = OpenLoam::Tenant.create!(name: "Tenant A", slug: "a-<%= file_name %>")
10
+ @tenant_b = OpenLoam::Tenant.create!(name: "Tenant B", slug: "b-<%= file_name %>")
11
11
  @manager = User.create!(name: "Manager", email: "manager@example.test", password: "password")
12
12
  @employee = User.create!(name: "Employee", email: "employee@example.test", password: "password")
13
13
 
14
14
  with_tenant(@tenant_a) do
15
- Loam::Membership.create!(user: @manager, role: "manager")
16
- Loam::Membership.create!(user: @employee, role: "employee")
15
+ OpenLoam::Membership.create!(user: @manager, role: "manager")
16
+ OpenLoam::Membership.create!(user: @employee, role: "employee")
17
17
  end
18
18
  end
19
19
 
@@ -29,15 +29,15 @@ class <%= class_name %>LoamTest < ActiveSupport::TestCase
29
29
  end
30
30
 
31
31
  test "guardrail: touching the model with no tenant context raises" do
32
- assert_raises(Loam::MissingTenantError) { <%= class_name %>.count }
33
- assert_raises(Loam::MissingTenantError) { <%= class_name %>.new }
32
+ assert_raises(OpenLoam::MissingTenantError) { <%= class_name %>.count }
33
+ assert_raises(OpenLoam::MissingTenantError) { <%= class_name %>.new }
34
34
  end
35
35
 
36
36
  test "guardrail: a record cannot be written into a foreign tenant" do
37
37
  record = with_tenant(@tenant_a) { <%= class_name %>.create!(<%= sample_attributes %>) }
38
38
 
39
39
  with_tenant(@tenant_b) do
40
- assert_raises(Loam::MissingTenantError) { record.update!(<%= sample_attributes(1) %>) }
40
+ assert_raises(OpenLoam::MissingTenantError) { record.update!(<%= sample_attributes(1) %>) }
41
41
  end
42
42
  end
43
43
 
@@ -45,7 +45,7 @@ class <%= class_name %>LoamTest < ActiveSupport::TestCase
45
45
  with_tenant(@tenant_a, actor: @manager) do
46
46
  record = <%= class_name %>.create!(<%= sample_attributes %>)
47
47
 
48
- audit = Loam::AuditRecord.find_by(
48
+ audit = OpenLoam::AuditRecord.find_by(
49
49
  auditable_type: "<%= class_name %>", auditable_id: record.id, action: "create"
50
50
  )
51
51
  assert audit, "expected an audit record for the create"
@@ -56,7 +56,7 @@ class <%= class_name %>LoamTest < ActiveSupport::TestCase
56
56
 
57
57
  test "lifecycle events are published with the tenant stamped" do
58
58
  received = []
59
- subscription = Loam::Events.subscribe("<%= domain %>.<%= singular_name %>.created") do |_name, payload|
59
+ subscription = OpenLoam::Events.subscribe("<%= domain %>.<%= singular_name %>.created") do |_name, payload|
60
60
  received << payload
61
61
  end
62
62
 
@@ -78,7 +78,7 @@ class <%= class_name %>LoamTest < ActiveSupport::TestCase
78
78
  with_tenant(@tenant_a) do
79
79
  assert_equal 0, <%= class_name %>.count, "a soft-deleted record is gone from ordinary queries"
80
80
  assert_equal 1, <%= class_name %>.with_deleted.count, "but the row still exists"
81
- assert Loam::AuditRecord.exists?(auditable_type: "<%= class_name %>", auditable_id: record_id, action: "soft_delete")
81
+ assert OpenLoam::AuditRecord.exists?(auditable_type: "<%= class_name %>", auditable_id: record_id, action: "soft_delete")
82
82
  end
83
83
 
84
84
  # with_deleted lifts the deleted_at filter but NOT the tenant filter.
@@ -1,10 +1,10 @@
1
1
  class Create<%= table_name.camelize %> < ActiveRecord::Migration[<%= ActiveRecord::VERSION::STRING.to_f %>]
2
2
  def change
3
- create_table :<%= table_name %><%= loam_id_option %> do |t|
4
- t.references :tenant, null: false, foreign_key: { to_table: :loam_tenants }<%= loam_type_option %>
3
+ create_table :<%= table_name %><%= open_loam_id_option %> do |t|
4
+ t.references :tenant, null: false, foreign_key: { to_table: :open_loam_tenants }<%= open_loam_type_option %>
5
5
  <% attributes.each do |attribute| -%>
6
6
  <% if encrypted?(attribute) -%>
7
- t.text :<%= attribute.name %> # encrypted at rest (Loam::Encryptable) — text, since ciphertext outgrows varchar
7
+ t.text :<%= attribute.name %> # encrypted at rest (OpenLoam::Encryptable) — text, since ciphertext outgrows varchar
8
8
  <% else -%>
9
9
  t.<%= attribute.type %> :<%= attribute.name %>
10
10
  <% end -%>
@@ -14,10 +14,10 @@ class Create<%= table_name.camelize %> < ActiveRecord::Migration[<%= ActiveRecor
14
14
  <% end -%>
15
15
  t.json :custom_fields, null: false, default: {}
16
16
  t.datetime :deleted_at
17
- t.integer :lock_version, null: false, default: 0 # optimistic locking (Loam::RecordLock)
17
+ t.integer :lock_version, null: false, default: 0 # optimistic locking (OpenLoam::RecordLock)
18
18
  t.timestamps
19
19
  end
20
- # Loam::SoftDeletable filters `deleted_at IS NULL` on every query.
20
+ # OpenLoam::SoftDeletable filters `deleted_at IS NULL` on every query.
21
21
  add_index :<%= table_name %>, :deleted_at
22
22
  <% encrypt_searchable_names.each do |field| -%>
23
23
  add_index :<%= table_name %>, :<%= field %>_hash
@@ -1,14 +1,14 @@
1
1
  # Tenant-scoped, audited, evented — by inheritance, not by remembering.
2
- class <%= class_name %> < Loam::TenantRecord
3
- include Loam::Auditable
4
- include Loam::Eventful
5
- include Loam::CustomFields
6
- include Loam::Commentable
7
- include Loam::Attachable
8
- include Loam::Searchable
9
- include Loam::SoftDeletable
2
+ class <%= class_name %> < OpenLoam::TenantRecord
3
+ include OpenLoam::Auditable
4
+ include OpenLoam::Eventful
5
+ include OpenLoam::CustomFields
6
+ include OpenLoam::Commentable
7
+ include OpenLoam::Attachable
8
+ include OpenLoam::Searchable
9
+ include OpenLoam::SoftDeletable
10
10
  <% if encrypted_field_names.any? -%>
11
- include Loam::Encryptable
11
+ include OpenLoam::Encryptable
12
12
  <% end -%>
13
13
 
14
14
  event_domain :<%= domain %>
@@ -23,5 +23,5 @@ class <%= class_name %> < Loam::TenantRecord
23
23
  <% end -%>
24
24
 
25
25
  # Business logic goes here. Publish business events explicitly:
26
- # Loam::Events.publish("<%= domain %>.something.happened", id: id)
26
+ # OpenLoam::Events.publish("<%= domain %>.something.happened", id: id)
27
27
  end
@@ -1,4 +1,4 @@
1
- class <%= class_name %>Policy < Loam::Policy
1
+ class <%= class_name %>Policy < OpenLoam::Policy
2
2
  # Action defaults: any member of the current tenant may read/create/update/
3
3
  # destroy. Tighten by overriding, e.g.:
4
4
  #
@@ -1,4 +1,4 @@
1
- <%% policy = Loam::Policy.for(@record) %>
1
+ <%% policy = OpenLoam::Policy.for(@record) %>
2
2
 
3
3
  <%%# Optimistic-locking conflict: what the user typed vs what is now saved. %>
4
4
  <%% if @conflict.present? %>
@@ -45,16 +45,16 @@
45
45
  <%% else %>
46
46
  <p>
47
47
  <%%= f.label :<%= attribute.name %> %>: <%%= @record.<%= attribute.name %> %>
48
- <em><%%= t("loam.form.read_only") %></em>
48
+ <em><%%= t("open_loam.form.read_only") %></em>
49
49
  </p>
50
50
  <%% end %>
51
51
  <% end -%>
52
52
 
53
- <%%= render "loam/custom_fields/fields", record: @record, policy: policy, mode: :edit %>
53
+ <%%= render "open_loam/custom_fields/fields", record: @record, policy: policy, mode: :edit %>
54
54
 
55
55
  <%% if policy.update? %>
56
56
  <p>
57
- <%%= f.label :files, t("loam.form.attach_files") %><br>
57
+ <%%= f.label :files, t("open_loam.form.attach_files") %><br>
58
58
  <%%= f.file_field :files, multiple: true %>
59
59
  </p>
60
60
  <%% end %>
@@ -1,13 +1,13 @@
1
- <h1><%%= t("loam.actions.deleted_title", name: <%= class_name %>.model_name.human(count: 2)) %></h1>
1
+ <h1><%%= t("open_loam.actions.deleted_title", name: <%= class_name %>.model_name.human(count: 2)) %></h1>
2
2
 
3
- <p><%%= link_to t("loam.actions.back"), [:admin, <%= class_name %>] %></p>
3
+ <p><%%= link_to t("open_loam.actions.back"), [:admin, <%= class_name %>] %></p>
4
4
 
5
5
  <%%# No Show/Edit here: these rows are hidden by the default scope, so a plain
6
6
  find would 404. The one action a deleted record offers is Restore. %>
7
7
  <table>
8
8
  <thead>
9
9
  <tr>
10
- <th><%%= t("loam.labels.id") %></th>
10
+ <th><%%= t("open_loam.labels.id") %></th>
11
11
  <% attributes.each do |attribute| -%>
12
12
  <th><%%= <%= class_name %>.human_attribute_name("<%= attribute.name %>") %></th>
13
13
  <% end -%>
@@ -23,7 +23,7 @@
23
23
  <td><%%= record.<%= attribute.name %> %></td>
24
24
  <% end -%>
25
25
  <td><%%= record.deleted_at %></td>
26
- <td><%%= button_to t("loam.actions.restore"), [:restore, :admin, record], method: :patch %></td>
26
+ <td><%%= button_to t("open_loam.actions.restore"), [:restore, :admin, record], method: :patch %></td>
27
27
  </tr>
28
28
  <%% end %>
29
29
  </tbody>
@@ -32,10 +32,10 @@
32
32
  <%%# Page links carry nothing but the page: the recycle bin has no filter box. %>
33
33
  <p>
34
34
  <%% if @page > 1 %>
35
- <%%= link_to t("loam.pagination.previous"), polymorphic_path([:deleted, :admin, <%= class_name %>], page: @page - 1) %>
35
+ <%%= link_to t("open_loam.pagination.previous"), polymorphic_path([:deleted, :admin, <%= class_name %>], page: @page - 1) %>
36
36
  <%% end %>
37
- <%%= t("loam.pagination.page", page: @page) %>
37
+ <%%= t("open_loam.pagination.page", page: @page) %>
38
38
  <%% if @has_next %>
39
- <%%= link_to t("loam.pagination.next"), polymorphic_path([:deleted, :admin, <%= class_name %>], page: @page + 1) %>
39
+ <%%= link_to t("open_loam.pagination.next"), polymorphic_path([:deleted, :admin, <%= class_name %>], page: @page + 1) %>
40
40
  <%% end %>
41
41
  </p>
@@ -0,0 +1,3 @@
1
+ <h1><%%= t("open_loam.actions.edit_title", name: <%= class_name %>.model_name.human, id: @record.id) %></h1>
2
+
3
+ <%%= render "form" %>
@@ -0,0 +1,90 @@
1
+ <h1><%%= <%= class_name %>.model_name.human(count: 2) %></h1>
2
+
3
+ <%% if @index_partial %>
4
+ <p style="color:#b00"><%%= t("open_loam.index.incomplete") %></p>
5
+ <%% end %>
6
+
7
+ <p>
8
+ <%%= link_to t("open_loam.actions.new", name: <%= class_name %>.model_name.human), new_polymorphic_path([:admin, <%= class_name %>]) %>
9
+ <%%= link_to t("open_loam.actions.deleted", name: <%= class_name %>.model_name.human(count: 2)), polymorphic_path([:deleted, :admin, <%= class_name %>]) %>
10
+ <%% if current_role == :manager %>
11
+ <%%= link_to t("open_loam.actions.export_csv"), url_for(action: :export, **current_index_params) %>
12
+ <%%= link_to t("open_loam.actions.import_csv"), new_admin_import_path(entity_type: "<%= class_name %>") %>
13
+ <%% end %>
14
+ </p>
15
+
16
+ <%%# Saved views (OpenLoam::Perspectives): pick one to apply, or save the current one. %>
17
+ <%% if @perspectives.any? %>
18
+ <%%= form_with url: [:admin, <%= class_name %>], method: :get do %>
19
+ <%%= select_tag :perspective_id,
20
+ options_for_select([[t("open_loam.views.all", name: <%= class_name %>.model_name.human(count: 2)), "none"]] + @perspectives.map { |p| [p.name, p.id] }, @perspective&.id),
21
+ onchange: "this.form.submit()" %>
22
+ <%%= link_to t("open_loam.views.manage"), admin_perspectives_path(entity_type: "<%= class_name %>") %>
23
+ <%% end %>
24
+ <%% else %>
25
+ <p><%%= link_to t("open_loam.views.manage_saved"), admin_perspectives_path(entity_type: "<%= class_name %>") %></p>
26
+ <%% end %>
27
+
28
+ <%%= form_with url: admin_perspectives_path, method: :post do %>
29
+ <%%= hidden_field_tag :entity_type, "<%= class_name %>" %>
30
+ <%%= hidden_field_tag :q, params[:q] %>
31
+ <%%= text_field_tag :name, nil, placeholder: t("open_loam.views.name_placeholder") %>
32
+ <%%= submit_tag t("open_loam.views.save_current") %>
33
+ <%% end %>
34
+ <% if searchable_attributes.any? -%>
35
+
36
+ <%%= form_with url: [:admin, <%= class_name %>], method: :get do |f| %>
37
+ <%%= text_field_tag :q, params[:q], placeholder: t("open_loam.views.filter_placeholder", name: <%= class_name %>.model_name.human(count: 2)) %>
38
+ <%%= f.submit t("open_loam.actions.filter") %>
39
+ <%% end %>
40
+ <% end -%>
41
+
42
+ <%%# Bulk actions on selected rows (OpenLoam::Bulk). Zero selection = no-op; each
43
+ action is policy-checked per record server-side. %>
44
+ <%%= form_with url: url_for(action: :bulk), method: :post do %>
45
+ <p>
46
+ <%%= select_tag :bulk_action, options_for_select([[t("open_loam.bulk.delete_selected"), "soft_delete"], [t("open_loam.bulk.set_field"), "set_field"], [t("open_loam.bulk.export_selected"), "export"]]) %>
47
+ <%%= select_tag :field, options_for_select(%i[<%= field_names.join(" ") %>]) %>
48
+ <%%= text_field_tag :value, nil, placeholder: t("open_loam.bulk.new_value") %>
49
+ <%%= submit_tag t("open_loam.bulk.apply"), data: { turbo_confirm: t("open_loam.bulk.confirm") } %>
50
+ </p>
51
+
52
+ <table>
53
+ <thead>
54
+ <tr>
55
+ <th><input type="checkbox" onclick="document.querySelectorAll('.bulk-select').forEach(function(c){c.checked=this.checked}.bind(this))"></th>
56
+ <th><%%= t("open_loam.labels.id") %></th>
57
+ <% attributes.each do |attribute| -%>
58
+ <th><%%= sort_link("<%= attribute.name %>", <%= class_name %>.human_attribute_name("<%= attribute.name %>")) %></th>
59
+ <% end -%>
60
+ <th></th>
61
+ </tr>
62
+ </thead>
63
+ <tbody>
64
+ <%% @records.each do |record| %>
65
+ <tr>
66
+ <td><%%= check_box_tag "ids[]", record.id, false, class: "bulk-select" %></td>
67
+ <td><%%= record.id %></td>
68
+ <% attributes.each do |attribute| -%>
69
+ <td><%%= record.<%= attribute.name %> %></td>
70
+ <% end -%>
71
+ <td>
72
+ <%%= link_to t("open_loam.actions.show"), [:admin, record] %>
73
+ <%%= link_to t("open_loam.actions.edit"), edit_polymorphic_path([:admin, record]) %>
74
+ </td>
75
+ </tr>
76
+ <%% end %>
77
+ </tbody>
78
+ </table>
79
+ <%% end %>
80
+
81
+ <%%# Page links carry the full filter state, or page 2 would quietly drop it. %>
82
+ <p>
83
+ <%% if @page > 1 %>
84
+ <%%= link_to t("open_loam.pagination.previous"), polymorphic_path([:admin, <%= class_name %>], **current_index_params, page: @page - 1) %>
85
+ <%% end %>
86
+ <%%= t("open_loam.pagination.page", page: @page) %>
87
+ <%% if @has_next %>
88
+ <%%= link_to t("open_loam.pagination.next"), polymorphic_path([:admin, <%= class_name %>], **current_index_params, page: @page + 1) %>
89
+ <%% end %>
90
+ </p>