has_helpers 5.0.2

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 (748) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +38 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +10 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.tekton/Chart.yaml +14 -0
  8. data/.tekton/templates/pipelinerun.yaml +44 -0
  9. data/.tekton/values-main.yaml +53 -0
  10. data/.tekton/values-pr.yaml +40 -0
  11. data/.tekton/values.yaml +17 -0
  12. data/.tool-versions +1 -0
  13. data/CHANGELOG.md +204 -0
  14. data/Gemfile +25 -0
  15. data/README.md +76 -0
  16. data/Rakefile +46 -0
  17. data/app/api/has_helpers/rack_api.rb +116 -0
  18. data/app/api/has_helpers/search_api.rb +173 -0
  19. data/app/assets/stylesheets/has_helpers/base.scss +5 -0
  20. data/app/assets/stylesheets/has_helpers/color_palette.scss +33 -0
  21. data/app/assets/stylesheets/has_helpers/color_palettes/default.scss +11 -0
  22. data/app/assets/stylesheets/has_helpers/components/dataset.scss +43 -0
  23. data/app/assets/stylesheets/has_helpers/mixins.scss +149 -0
  24. data/app/commands/has_helpers/cards/available_cards_command.rb +98 -0
  25. data/app/commands/has_helpers/cards/card_data_command.rb +1062 -0
  26. data/app/commands/reports/base_options.rb +79 -0
  27. data/app/commands/reports/belongs_to_options.rb +178 -0
  28. data/app/commands/reports/constant_options.rb +66 -0
  29. data/app/commands/reports/external_graphql_client.rb +116 -0
  30. data/app/commands/reports/fill_dates.rb +65 -0
  31. data/app/commands/reports/filter_option_generator.rb +186 -0
  32. data/app/commands/reports/generator.rb +310 -0
  33. data/app/commands/reports/has_many_options.rb +105 -0
  34. data/app/commands/reports/importer.rb +132 -0
  35. data/app/commands/reports/nested_attribute_options.rb +134 -0
  36. data/app/constants/color.rb +9 -0
  37. data/app/constants/file_format.rb +12 -0
  38. data/app/constants/has_helpers/cards/card_custom_field.rb +12 -0
  39. data/app/constants/has_helpers/cards/card_data_type.rb +11 -0
  40. data/app/constants/has_helpers/cards/card_identifier.rb +10 -0
  41. data/app/constants/has_helpers/cards/card_layout_type.rb +11 -0
  42. data/app/constants/has_helpers/cards/card_variant_type.rb +12 -0
  43. data/app/constants/has_helpers/link_type.rb +10 -0
  44. data/app/constants/has_helpers/notification_alert_status.rb +14 -0
  45. data/app/constants/has_helpers/notification_alert_type.rb +10 -0
  46. data/app/constants/has_helpers/operation.rb +11 -0
  47. data/app/constants/has_helpers/resource_type.rb +25 -0
  48. data/app/constants/icon.rb +22 -0
  49. data/app/constants/logical_operator.rb +7 -0
  50. data/app/constants/option.rb +10 -0
  51. data/app/constants/time_zone.rb +12 -0
  52. data/app/constructors/has_helpers/base_constructor.rb +62 -0
  53. data/app/controllers/concerns/has_helpers/service_trust/authenticated_as_service.rb +51 -0
  54. data/app/controllers/concerns/has_helpers/service_trust/graphql_authentication.rb +57 -0
  55. data/app/controllers/has_helpers/controller.rb +63 -0
  56. data/app/controllers/has_helpers/factory_controller.rb +261 -0
  57. data/app/controllers/has_helpers/graphql_extensions.rb +83 -0
  58. data/app/controllers/has_helpers/health_check_controller.rb +46 -0
  59. data/app/controllers/has_helpers/search_extensions.rb +92 -0
  60. data/app/controllers/has_helpers/service_trust/jwks_controller.rb +20 -0
  61. data/app/controllers/has_helpers/xlsx_export.rb +115 -0
  62. data/app/controllers/search_extensions.rb +5 -0
  63. data/app/graphql/has_helpers/base_type.rb +13 -0
  64. data/app/graphql/has_helpers/base_union.rb +9 -0
  65. data/app/graphql/has_helpers/enums/resource_enum.rb +24 -0
  66. data/app/graphql/has_helpers/middleware/application_access.rb +26 -0
  67. data/app/graphql/has_helpers/mutations/base.rb +27 -0
  68. data/app/graphql/has_helpers/mutations/external_data_sync.rb +72 -0
  69. data/app/graphql/has_helpers/mutations/mark_individual_alert_read.rb +31 -0
  70. data/app/graphql/has_helpers/mutations/mark_notifications_read.rb +50 -0
  71. data/app/graphql/has_helpers/mutations/toggle_favorite.rb +39 -0
  72. data/app/graphql/has_helpers/resolvers/app_nav.rb +107 -0
  73. data/app/graphql/has_helpers/resolvers/base.rb +27 -0
  74. data/app/graphql/has_helpers/resolvers/cards/available_cards.rb +31 -0
  75. data/app/graphql/has_helpers/resolvers/cards/card_data.rb +74 -0
  76. data/app/graphql/has_helpers/resolvers/cards/hover_card_data.rb +153 -0
  77. data/app/graphql/has_helpers/resolvers/favorite.rb +62 -0
  78. data/app/graphql/has_helpers/resolvers/internal/roles.rb +54 -0
  79. data/app/graphql/has_helpers/resolvers/is_favorite.rb +24 -0
  80. data/app/graphql/has_helpers/resolvers/list_notification_alerts.rb +47 -0
  81. data/app/graphql/has_helpers/resolvers/lookup.rb +36 -0
  82. data/app/graphql/has_helpers/resolvers/recently_viewed.rb +54 -0
  83. data/app/graphql/has_helpers/resolvers/resource_tab.rb +22 -0
  84. data/app/graphql/has_helpers/resolvers/search.rb +18 -0
  85. data/app/graphql/has_helpers/resolvers/search_query.rb +38 -0
  86. data/app/graphql/has_helpers/resolvers/user_application_order.rb +35 -0
  87. data/app/graphql/has_helpers/resources/organization_option_resource.rb +28 -0
  88. data/app/graphql/has_helpers/resources/organization_resource_resource.rb +18 -0
  89. data/app/graphql/has_helpers/resources/resource_group_resource.rb +10 -0
  90. data/app/graphql/has_helpers/resources/resource_resource.rb +21 -0
  91. data/app/graphql/has_helpers/resources/user_role_resource.rb +9 -0
  92. data/app/graphql/has_helpers/types/application_order_type.rb +18 -0
  93. data/app/graphql/has_helpers/types/cards/available_card_type.rb +20 -0
  94. data/app/graphql/has_helpers/types/cards/card_badge_type.rb +12 -0
  95. data/app/graphql/has_helpers/types/cards/card_config_type.rb +18 -0
  96. data/app/graphql/has_helpers/types/cards/card_data_type.rb +13 -0
  97. data/app/graphql/has_helpers/types/cards/card_entry_field_type.rb +21 -0
  98. data/app/graphql/has_helpers/types/cards/card_entry_type.rb +12 -0
  99. data/app/graphql/has_helpers/types/cards/card_field_config_type.rb +17 -0
  100. data/app/graphql/has_helpers/types/layouts/app_nav/link_section_type.rb +17 -0
  101. data/app/graphql/has_helpers/types/layouts/app_nav/link_type.rb +20 -0
  102. data/app/graphql/has_helpers/types/layouts/app_nav/sub_link_type.rb +17 -0
  103. data/app/graphql/has_helpers/types/layouts/app_nav/type.rb +16 -0
  104. data/app/graphql/has_helpers/types/lookup.rb +16 -0
  105. data/app/graphql/has_helpers/types/resource_tab/type.rb +15 -0
  106. data/app/graphql/has_helpers/types/role_type.rb +13 -0
  107. data/app/graphql/has_helpers/types/search/detail_badge_type.rb +15 -0
  108. data/app/graphql/has_helpers/types/search/detail_item_type.rb +14 -0
  109. data/app/graphql/has_helpers/types/search/detail_type.rb +16 -0
  110. data/app/graphql/has_helpers/types/search/filter_operation_enum.rb +19 -0
  111. data/app/graphql/has_helpers/types/search/input_type.rb +16 -0
  112. data/app/graphql/has_helpers/types/search/result_type.rb +72 -0
  113. data/app/graphql/has_helpers/types/search/search_filter_input_type.rb +17 -0
  114. data/app/graphql/has_helpers/types/search/type.rb +17 -0
  115. data/app/importers/has_helpers/cards/importer.rb +1223 -0
  116. data/app/lib/has_helpers/application_helper.rb +21 -0
  117. data/app/lib/has_helpers/dn/processor.rb +55 -0
  118. data/app/lib/has_helpers/dn/refresh.rb +13 -0
  119. data/app/lib/has_helpers/dn/utils/backfill_utils.rb +80 -0
  120. data/app/lib/has_helpers/dn/utils/query_utils.rb +216 -0
  121. data/app/lib/has_helpers/feature.rb +88 -0
  122. data/app/lib/has_helpers/hq_exception.rb +18 -0
  123. data/app/lib/has_helpers/index.rb +890 -0
  124. data/app/lib/has_helpers/organization_merge.rb +128 -0
  125. data/app/lib/has_helpers/query/elasticsearch.rb +20 -0
  126. data/app/lib/has_helpers/query/provider.rb +105 -0
  127. data/app/lib/has_helpers/query/sql.rb +133 -0
  128. data/app/lib/has_helpers/query.rb +14 -0
  129. data/app/lib/has_helpers/search/elasticsearch.rb +41 -0
  130. data/app/lib/has_helpers/search/elasticsearch_shared.rb +70 -0
  131. data/app/lib/has_helpers/search/provider.rb +149 -0
  132. data/app/lib/has_helpers/search/search_utils.rb +74 -0
  133. data/app/lib/has_helpers/search/sql_search.rb +219 -0
  134. data/app/lib/has_helpers/search.rb +46 -0
  135. data/app/models/ability.rb +233 -0
  136. data/app/models/has_helpers/application.rb +47 -0
  137. data/app/models/has_helpers/application_record.rb +7 -0
  138. data/app/models/has_helpers/cards/card_builder/base.rb +74 -0
  139. data/app/models/has_helpers/cards/card_builder/card_definition_builder.rb +486 -0
  140. data/app/models/has_helpers/cards/card_builder/definition_context.rb +82 -0
  141. data/app/models/has_helpers/cards/card_builder/field_builder.rb +142 -0
  142. data/app/models/has_helpers/cards/card_builder/resource_builder.rb +34 -0
  143. data/app/models/has_helpers/cards/card_definition.rb +41 -0
  144. data/app/models/has_helpers/cards/card_definition_resource.rb +19 -0
  145. data/app/models/has_helpers/cards/card_field.rb +39 -0
  146. data/app/models/has_helpers/cards/registry.rb +115 -0
  147. data/app/models/has_helpers/cards/role_card_field.rb +28 -0
  148. data/app/models/has_helpers/cards/role_resource_card.rb +41 -0
  149. data/app/models/has_helpers/dn_utils.rb +61 -0
  150. data/app/models/has_helpers/favorite.rb +20 -0
  151. data/app/models/has_helpers/master_record.rb +18 -0
  152. data/app/models/has_helpers/notification_alert.rb +52 -0
  153. data/app/models/has_helpers/organization.rb +16 -0
  154. data/app/models/has_helpers/organization_link.rb +92 -0
  155. data/app/models/has_helpers/organization_methods.rb +62 -0
  156. data/app/models/has_helpers/organization_option.rb +31 -0
  157. data/app/models/has_helpers/organization_resource.rb +45 -0
  158. data/app/models/has_helpers/pending_db_operation.rb +31 -0
  159. data/app/models/has_helpers/recently_viewed.rb +34 -0
  160. data/app/models/has_helpers/reindex_queue.rb +43 -0
  161. data/app/models/has_helpers/resource.rb +38 -0
  162. data/app/models/has_helpers/resource_group.rb +15 -0
  163. data/app/models/has_helpers/restriction.rb +15 -0
  164. data/app/models/has_helpers/restriction_operation.rb +13 -0
  165. data/app/models/has_helpers/role.rb +20 -0
  166. data/app/models/has_helpers/service_trust/audit_log.rb +20 -0
  167. data/app/models/has_helpers/service_trust/key.rb +56 -0
  168. data/app/models/has_helpers/stat.rb +13 -0
  169. data/app/models/has_helpers/user.rb +18 -0
  170. data/app/models/has_helpers/user_methods.rb +184 -0
  171. data/app/models/has_helpers/user_organization.rb +33 -0
  172. data/app/models/has_helpers/user_role.rb +27 -0
  173. data/app/models/has_helpers/user_session.rb +19 -0
  174. data/app/presenters/has_helpers/barcode_base_presenter.rb +113 -0
  175. data/app/presenters/has_helpers/base_presenter.rb +100 -0
  176. data/app/presenters/has_helpers/cacheable_item.rb +28 -0
  177. data/app/presenters/has_helpers/item_presenter.rb +52 -0
  178. data/app/presenters/has_helpers/items_presenter.rb +33 -0
  179. data/app/presenters/has_helpers/resource/action_presenter.rb +8 -0
  180. data/app/presenters/has_helpers/resource/cell_presenter.rb +52 -0
  181. data/app/presenters/has_helpers/resource/email_presenter.rb +34 -0
  182. data/app/presenters/has_helpers/resource/form_presenter.rb +76 -0
  183. data/app/presenters/has_helpers/resource/info_collection_presenter.rb +33 -0
  184. data/app/presenters/has_helpers/resource/info_presenter.rb +34 -0
  185. data/app/presenters/has_helpers/resource/list_presenter.rb +120 -0
  186. data/app/presenters/has_helpers/resource/note_presenter.rb +14 -0
  187. data/app/presenters/has_helpers/resource/row_presenter.rb +25 -0
  188. data/app/presenters/has_helpers/resource/tag_presenter.rb +31 -0
  189. data/app/presenters/has_helpers/search_presenter.rb +230 -0
  190. data/app/triggers/has_helpers/erase_denormalized_belongs_to_column_trigger.rb +85 -0
  191. data/app/triggers/has_helpers/limit_favorites.rb +97 -0
  192. data/app/utils/has_helpers/redis_configurable.rb +79 -0
  193. data/app/utils/has_helpers/sync_utils.rb +234 -0
  194. data/app/views/has_helpers/actions/_action.html.erb +31 -0
  195. data/app/views/has_helpers/alerts/_alert.html.erb +3 -0
  196. data/app/views/has_helpers/barcodes.html.erb +36 -0
  197. data/app/views/has_helpers/containers/_container.html.erb +3 -0
  198. data/app/views/has_helpers/email_notifier/system.html.erb +8 -0
  199. data/app/views/has_helpers/email_notifier/system.text.erb +7 -0
  200. data/app/views/has_helpers/enumerables/_enumerable.html.erb +1 -0
  201. data/app/views/has_helpers/icons/_icon.html.erb +1 -0
  202. data/app/views/has_helpers/render_objects/_render_object.html.erb +1 -0
  203. data/app/views/has_helpers/services/_service.html.erb +1 -0
  204. data/app/views/has_helpers/text_nodes/_text_node.html.erb +4 -0
  205. data/app/views/has_helpers/ui_attributes/_html_attrs.html.erb +1 -0
  206. data/app/workers/elasticsearch_process_queue.rb +11 -0
  207. data/app/workers/elasticsearch_reindex.rb +11 -0
  208. data/app/workers/elasticsearch_reindex_scheduler.rb +35 -0
  209. data/app/workers/good_job/configurable_backfill.rb +64 -0
  210. data/app/workers/good_job/cron_denormalization_stats_manager.rb +39 -0
  211. data/app/workers/good_job/denormalization_stats_worker.rb +57 -0
  212. data/app/workers/good_job/dn_update_worker.rb +30 -0
  213. data/app/workers/good_job/dn_worker.rb +47 -0
  214. data/app/workers/good_job/elasticsearch_old_index_cleaner_worker.rb +113 -0
  215. data/app/workers/good_job/elasticsearch_on_finish_reindex.rb +26 -0
  216. data/app/workers/good_job/elasticsearch_process_batch.rb +78 -0
  217. data/app/workers/good_job/elasticsearch_process_change_logs_master_worker.rb +27 -0
  218. data/app/workers/good_job/elasticsearch_process_change_logs_worker.rb +155 -0
  219. data/app/workers/good_job/elasticsearch_process_queue.rb +50 -0
  220. data/app/workers/good_job/elasticsearch_reindex.rb +31 -0
  221. data/app/workers/has_helpers/cards/orphan_card_sweeper.rb +28 -0
  222. data/app/workers/has_helpers/db_operations/clean_stats_worker.rb +42 -0
  223. data/app/workers/has_helpers/db_operations/helper_worker.rb +131 -0
  224. data/app/workers/has_helpers/db_operations/main_worker.rb +164 -0
  225. data/app/workers/has_helpers/db_operations/operations.rb +134 -0
  226. data/app/workers/has_helpers/db_operations/update_operations_stats_resume_worker.rb +35 -0
  227. data/app/workers/has_helpers/default_worker.rb +17 -0
  228. data/app/workers/has_helpers/good_job/notification_alert_cleanup_worker.rb +43 -0
  229. data/app/workers/has_helpers/service_trust/audit_log_cleanup_worker.rb +19 -0
  230. data/app/workers/sidekiq/elasticsearch_old_index_cleaner_worker.rb +112 -0
  231. data/app/workers/sidekiq/elasticsearch_process_batch.rb +87 -0
  232. data/app/workers/sidekiq/elasticsearch_process_queue.rb +40 -0
  233. data/app/workers/sidekiq/elasticsearch_reindex.rb +53 -0
  234. data/config/initializers/action_view.rb +7 -0
  235. data/config/initializers/inflections.rb +24 -0
  236. data/config/initializers/marginalia.rb +8 -0
  237. data/config/initializers/mime_types.rb +6 -0
  238. data/config/initializers/opentelemetry.rb +12 -0
  239. data/config/initializers/redis_conf.rb +40 -0
  240. data/config/initializers/searchkick.rb +9 -0
  241. data/config/initializers/will_paginate.rb +39 -0
  242. data/config/redis.yml +12 -0
  243. data/config/routes.rb +27 -0
  244. data/config/service_trust_routes.rb +5 -0
  245. data/config.ru +9 -0
  246. data/db/migrate/20230127194725_create_organization_table.rb +25 -0
  247. data/db/migrate/20230127194726_validate_organization_foreign_keys.rb +7 -0
  248. data/db/migrate/20230206141413_create_security_models.rb +42 -0
  249. data/db/migrate/20230206141513_create_restriction_model.rb +20 -0
  250. data/db/migrate/20230206142613_add_security_indexes.rb +14 -0
  251. data/db/migrate/20230206143513_security_model_constants_seed.rb +8 -0
  252. data/db/migrate/20230206161513_add_organization_to_security_models.rb +13 -0
  253. data/db/migrate/20230209121019_create_search_group_models.rb +36 -0
  254. data/db/migrate/20230209122919_add_search_group_indexes.rb +21 -0
  255. data/db/migrate/20230209181832_add_search_group_resources_type_constraint_trigger.rb +61 -0
  256. data/db/migrate/20230313220337_create_table_user_sessions.rb +31 -0
  257. data/db/migrate/20230517141106_update_resource_types.rb +12 -0
  258. data/db/migrate/20230518194611_add_resources_contraints.rb +17 -0
  259. data/db/migrate/20230518194642_create_nested_resource_model.rb +21 -0
  260. data/db/migrate/20230518194645_validate_security_models_constraints.rb +23 -0
  261. data/db/migrate/20230608125037_add_inactive_field_to_user_sessions.rb +7 -0
  262. data/db/migrate/20230919191525_add_parent_id_to_resources.rb +8 -0
  263. data/db/migrate/20230919191535_validate_parent_id_fk_resources.rb +7 -0
  264. data/db/migrate/20230919210456_drop_nested_resources.rb +7 -0
  265. data/db/migrate/20231222135620_add_user_role.rb +19 -0
  266. data/db/migrate/20240108172021_create_favorites_table.rb +20 -0
  267. data/db/migrate/20240108190209_create_organization_link_table_if_doesnt_exists.rb +31 -0
  268. data/db/migrate/20240109211445_validate_fks_org_links.rb +9 -0
  269. data/db/migrate/20240110135440_create_recently_vieweds.rb +20 -0
  270. data/db/migrate/20240112185312_link_type_options_seed.rb +7 -0
  271. data/db/migrate/20240116173201_organization_link_resource_tab_constraints.rb +19 -0
  272. data/db/migrate/20240131160949_add_user_organizations_table.rb +32 -0
  273. data/db/migrate/20240212132519_add_organization_in_user_sessions.rb +13 -0
  274. data/db/migrate/20240227135052_add_user_organization_composite_fks.rb +32 -0
  275. data/db/migrate/20240312193501_update_organization_links.rb +11 -0
  276. data/db/migrate/20240426185902_add_user_organizations_current_organization_column.rb +12 -0
  277. data/db/migrate/20240429214601_add_primary_trigger.rb +10 -0
  278. data/db/migrate/20241112131819_add_json_file_format_seed.rb +7 -0
  279. data/db/migrate/20250520133417_create_restriction_operations.rb +18 -0
  280. data/db/migrate/20250520133420_validate_create_restriction_operations.rb +8 -0
  281. data/db/migrate/20250520140642_add_indexes_to_restrictions.rb +9 -0
  282. data/db/migrate/20250529130741_create_notification_alerts_table.rb +28 -0
  283. data/db/migrate/20250530223332_create_data_migrations_table.rb +19 -0
  284. data/db/migrate/20250605121622_seed_notification_alert_status.rb +7 -0
  285. data/db/migrate/20250605131439_seed_notification_alert_types.rb +7 -0
  286. data/db/migrate/20250606173128_update_notification_alert_length_constraint.rb +18 -0
  287. data/db/migrate/20250606174043_validate_notification_alert_length_constraints.rb +8 -0
  288. data/db/migrate/20250624183000_add_data_import_alert_type.rb +11 -0
  289. data/db/migrate/20250722183935_create_organization_options_table.rb +38 -0
  290. data/db/migrate/20250722192724_validate_organization_options_fks_and_constraints.rb +32 -0
  291. data/db/migrate/20250722192936_add_indexes_to_organization_options_table.rb +15 -0
  292. data/db/migrate/20250915132448_add_column_restrictions_hash.rb +7 -0
  293. data/db/migrate/20250917143211_add_column_permissions_hash.rb +7 -0
  294. data/db/migrate/20251001145950_add_trust_this_device.rb +7 -0
  295. data/db/migrate/20251021130835_add_backend_urls_to_applications_table.rb +13 -0
  296. data/db/migrate/20251021172715_validate_backend_urls_constraints_in_applications.rb +9 -0
  297. data/db/migrate/20251030200020_create_card_definitions.rb +23 -0
  298. data/db/migrate/20251030201012_create_card_definition_resources.rb +24 -0
  299. data/db/migrate/20251030201236_create_card_fields.rb +34 -0
  300. data/db/migrate/20251103194317_create_role_resource_cards.rb +37 -0
  301. data/db/migrate/20251103194426_create_role_card_fields.rb +34 -0
  302. data/db/migrate/20251107173218_validate_columns_from_cards.rb +17 -0
  303. data/db/migrate/20251111173212_add_column_application_array.rb +7 -0
  304. data/db/migrate/20251112203326_add_card_types.rb +10 -0
  305. data/db/migrate/20251115120000_add_virtual_cards_support.rb +28 -0
  306. data/db/migrate/20251202231256_add_org_scoped_fk_role_resource_cards_to_roles.rb +14 -0
  307. data/db/migrate/20260105134504_add_accessor_field_resource_type.rb +7 -0
  308. data/db/migrate/20260115123000_add_default_visibility_to_card_definitions.rb +9 -0
  309. data/db/migrate/20260120175037_is_new_admin.rb +9 -0
  310. data/db/migrate/20260122392008_add_stats_table.rb +17 -0
  311. data/db/migrate/20260414204718_add_index_to_stats_table.rb +10 -0
  312. data/db/migrate/20260509120000_create_service_trust_keys.rb +25 -0
  313. data/db/migrate/20260509120001_create_service_trust_audit_logs.rb +26 -0
  314. data/db/migrate/20260511120000_add_direction_to_service_trust_audit_logs.rb +17 -0
  315. data/db/migrate/20260511121000_remove_role_fk_from_role_resource_cards.rb +19 -0
  316. data/db/migrate/20260511121001_add_disabled_at_to_role_resource_cards.rb +10 -0
  317. data/db/migrate/20260605120100_seed_hover_card_options.rb +8 -0
  318. data/db/migrate/20260608120000_seed_badge_list_card_data_type.rb +7 -0
  319. data/db/migrate/20260727000000_add_two_factor_trust_id_to_user_sessions.rb +11 -0
  320. data/db/seeds.rb +14 -0
  321. data/docs/Elasticsearch.md +23 -0
  322. data/docs/Search_API.md +126 -0
  323. data/docs/cards_role_proxy.md +107 -0
  324. data/docs/factory_api.md +135 -0
  325. data/has-helpers-pr-289-pipeline.yaml +16 -0
  326. data/has-helpers-pr-298-pipeline.yaml +15 -0
  327. data/has_helpers.gemspec +68 -0
  328. data/lib/active_record/migration/active_migration_patch.rb +123 -0
  329. data/lib/generators/has_helpers/install/USAGE +8 -0
  330. data/lib/generators/has_helpers/install/install_generator.rb +22 -0
  331. data/lib/generators/has_helpers/install/templates/migrate/has_helpers_initial_setup.rb +26 -0
  332. data/lib/has_helpers/active_record/base.rb +50 -0
  333. data/lib/has_helpers/active_record/date_range.rb +228 -0
  334. data/lib/has_helpers/active_record/nested_resource.rb +58 -0
  335. data/lib/has_helpers/active_record/validation.rb +63 -0
  336. data/lib/has_helpers/active_record.rb +520 -0
  337. data/lib/has_helpers/app/command.rb +107 -0
  338. data/lib/has_helpers/app.rb +10 -0
  339. data/lib/has_helpers/assets_precompile_helper.rb +20 -0
  340. data/lib/has_helpers/attributes.rb +196 -0
  341. data/lib/has_helpers/aws.rb +67 -0
  342. data/lib/has_helpers/cards/cache.rb +49 -0
  343. data/lib/has_helpers/cards/config.rb +42 -0
  344. data/lib/has_helpers/cards/remote_role.rb +27 -0
  345. data/lib/has_helpers/cards/remote_roles.rb +75 -0
  346. data/lib/has_helpers/cards/roles.rb +55 -0
  347. data/lib/has_helpers/cards.rb +34 -0
  348. data/lib/has_helpers/concerns/hydrate_concern.rb +76 -0
  349. data/lib/has_helpers/concerns/importable_accessors.rb +25 -0
  350. data/lib/has_helpers/concerns/restrictions_parseable.rb +51 -0
  351. data/lib/has_helpers/config.rb +11 -0
  352. data/lib/has_helpers/constant.rb +578 -0
  353. data/lib/has_helpers/constraints.rb +10 -0
  354. data/lib/has_helpers/core_ext/array.rb +28 -0
  355. data/lib/has_helpers/core_ext/date/infinity.rb +25 -0
  356. data/lib/has_helpers/core_ext/date.rb +39 -0
  357. data/lib/has_helpers/core_ext/enumerable.rb +20 -0
  358. data/lib/has_helpers/core_ext/false_class.rb +14 -0
  359. data/lib/has_helpers/core_ext/float.rb +18 -0
  360. data/lib/has_helpers/core_ext/hash.rb +71 -0
  361. data/lib/has_helpers/core_ext/integer.rb +18 -0
  362. data/lib/has_helpers/core_ext/nil_class.rb +30 -0
  363. data/lib/has_helpers/core_ext/object.rb +60 -0
  364. data/lib/has_helpers/core_ext/proc.rb +16 -0
  365. data/lib/has_helpers/core_ext/string.rb +81 -0
  366. data/lib/has_helpers/core_ext/symbol.rb +17 -0
  367. data/lib/has_helpers/core_ext/true_class.rb +14 -0
  368. data/lib/has_helpers/cursor.rb +46 -0
  369. data/lib/has_helpers/data_migration/base.rb +293 -0
  370. data/lib/has_helpers/data_migration/large_worker.rb +20 -0
  371. data/lib/has_helpers/data_migration/master_worker.rb +33 -0
  372. data/lib/has_helpers/data_migration/migrator.rb +83 -0
  373. data/lib/has_helpers/data_migration/model.rb +18 -0
  374. data/lib/has_helpers/data_migration/small_worker.rb +20 -0
  375. data/lib/has_helpers/data_migration.rb +8 -0
  376. data/lib/has_helpers/engine.rb +47 -0
  377. data/lib/has_helpers/extensions.rb +17 -0
  378. data/lib/has_helpers/factory_api.rb +47 -0
  379. data/lib/has_helpers/factory_scenario/definition.rb +33 -0
  380. data/lib/has_helpers/factory_scenario/dsl.rb +80 -0
  381. data/lib/has_helpers/factory_scenario/factory_bot_override.rb +53 -0
  382. data/lib/has_helpers/factory_scenario/registry.rb +31 -0
  383. data/lib/has_helpers/factory_scenario/runner.rb +285 -0
  384. data/lib/has_helpers/factory_scenario.rb +27 -0
  385. data/lib/has_helpers/fixed_expiration_time_cache_session_store.rb +20 -0
  386. data/lib/has_helpers/flattener/association.rb +140 -0
  387. data/lib/has_helpers/flattener/attribute.rb +42 -0
  388. data/lib/has_helpers/flattener/base.rb +428 -0
  389. data/lib/has_helpers/flattener/column/types.rb +71 -0
  390. data/lib/has_helpers/flattener/column.rb +97 -0
  391. data/lib/has_helpers/flattener/pivot.rb +49 -0
  392. data/lib/has_helpers/flattener/reset.rb +33 -0
  393. data/lib/has_helpers/flattener/resource.rb +114 -0
  394. data/lib/has_helpers/flattener/resource_generator.rb +21 -0
  395. data/lib/has_helpers/flattener/utils.rb +14 -0
  396. data/lib/has_helpers/flattener.rb +53 -0
  397. data/lib/has_helpers/hash_accessors.rb +62 -0
  398. data/lib/has_helpers/helpers/application.rb +291 -0
  399. data/lib/has_helpers/index/config.rb +15 -0
  400. data/lib/has_helpers/lazy_config.rb +61 -0
  401. data/lib/has_helpers/resource_importer.rb +260 -0
  402. data/lib/has_helpers/resources.rb +127 -0
  403. data/lib/has_helpers/rspec/constants.rb +61 -0
  404. data/lib/has_helpers/rspec/factories.rb +11 -0
  405. data/lib/has_helpers/rspec.rb +22 -0
  406. data/lib/has_helpers/sentry/graphql_trace.rb +37 -0
  407. data/lib/has_helpers/serializer.rb +41 -0
  408. data/lib/has_helpers/service_trust/audit.rb +45 -0
  409. data/lib/has_helpers/service_trust/authorization.rb +34 -0
  410. data/lib/has_helpers/service_trust/bearer_token.rb +23 -0
  411. data/lib/has_helpers/service_trust/capability.rb +32 -0
  412. data/lib/has_helpers/service_trust/client/response.rb +13 -0
  413. data/lib/has_helpers/service_trust/client/retry.rb +35 -0
  414. data/lib/has_helpers/service_trust/client.rb +128 -0
  415. data/lib/has_helpers/service_trust/configuration.rb +76 -0
  416. data/lib/has_helpers/service_trust/engine.rb +10 -0
  417. data/lib/has_helpers/service_trust/errors.rb +32 -0
  418. data/lib/has_helpers/service_trust/graphql/authorizable.rb +72 -0
  419. data/lib/has_helpers/service_trust/graphql_client.rb +54 -0
  420. data/lib/has_helpers/service_trust/jwks_client.rb +133 -0
  421. data/lib/has_helpers/service_trust/jwt_codec.rb +45 -0
  422. data/lib/has_helpers/service_trust/key_manager.rb +160 -0
  423. data/lib/has_helpers/service_trust/rate_limiter.rb +27 -0
  424. data/lib/has_helpers/service_trust/replay_cache.rb +26 -0
  425. data/lib/has_helpers/service_trust/signer.rb +47 -0
  426. data/lib/has_helpers/service_trust/test.rb +60 -0
  427. data/lib/has_helpers/service_trust/verifier.rb +85 -0
  428. data/lib/has_helpers/service_trust/verify_middleware.rb +55 -0
  429. data/lib/has_helpers/service_trust.rb +51 -0
  430. data/lib/has_helpers/test_helpers/factory_data/unit_addresses.rb +42 -0
  431. data/lib/has_helpers/test_helpers/factory_data/unit_advisors.rb +272 -0
  432. data/lib/has_helpers/test_helpers/factory_data/unit_emails.rb +37 -0
  433. data/lib/has_helpers/test_helpers/factory_data/unit_organizations.rb +48 -0
  434. data/lib/has_helpers/test_helpers/factory_data/unit_people.rb +34 -0
  435. data/lib/has_helpers/test_helpers/factory_data/unit_phones.rb +41 -0
  436. data/lib/has_helpers/test_helpers/factory_data/unit_states.rb +26 -0
  437. data/lib/has_helpers/test_helpers/factory_data/unit_webs.rb +31 -0
  438. data/lib/has_helpers/test_helpers/shoulda_helpers.rb +249 -0
  439. data/lib/has_helpers/ui_attributes.rb +39 -0
  440. data/lib/has_helpers/utils/action.rb +87 -0
  441. data/lib/has_helpers/utils/active_record_utils.rb +40 -0
  442. data/lib/has_helpers/utils/alert.rb +42 -0
  443. data/lib/has_helpers/utils/container.rb +46 -0
  444. data/lib/has_helpers/utils/dataset.rb +34 -0
  445. data/lib/has_helpers/utils/email_notifier.rb +15 -0
  446. data/lib/has_helpers/utils/icon.rb +66 -0
  447. data/lib/has_helpers/utils/input.rb +45 -0
  448. data/lib/has_helpers/utils/log_notifier.rb +79 -0
  449. data/lib/has_helpers/utils/render_object.rb +32 -0
  450. data/lib/has_helpers/utils/s3_utils.rb +37 -0
  451. data/lib/has_helpers/utils/service.rb +87 -0
  452. data/lib/has_helpers/utils/slack_conf.rb +31 -0
  453. data/lib/has_helpers/utils/slack_notifier.rb +42 -0
  454. data/lib/has_helpers/utils/text_node.rb +34 -0
  455. data/lib/has_helpers/utils/user_restrictions.rb +266 -0
  456. data/lib/has_helpers/utils.rb +19 -0
  457. data/lib/has_helpers/version.rb +6 -0
  458. data/lib/has_helpers/web_socket_publisher.rb +20 -0
  459. data/lib/has_helpers/webhook.rb +17 -0
  460. data/lib/has_helpers.rb +63 -0
  461. data/lib/hq/resource.rb +55 -0
  462. data/lib/hq/structure/inserts.rb +176 -0
  463. data/lib/hq/structure.rb +115 -0
  464. data/lib/tasks/has_helpers/service_trust.rake +39 -0
  465. data/script/rails +10 -0
  466. data/sorbet/config +5 -0
  467. data/sorbet/rbi/gems/actionpack.rbi +1361 -0
  468. data/sorbet/rbi/gems/activemodel.rbi +755 -0
  469. data/sorbet/rbi/gems/activerecord.rbi +4713 -0
  470. data/sorbet/rbi/gems/activesupport.rbi +1970 -0
  471. data/sorbet/rbi/gems/barby.rbi +129 -0
  472. data/sorbet/rbi/gems/better_errors.rbi +155 -0
  473. data/sorbet/rbi/gems/binding_of_caller.rbi +22 -0
  474. data/sorbet/rbi/gems/byebug.rbi +19 -0
  475. data/sorbet/rbi/gems/chronic.rbi +480 -0
  476. data/sorbet/rbi/gems/chunky_png.rbi +510 -0
  477. data/sorbet/rbi/gems/climate_control.rbi +45 -0
  478. data/sorbet/rbi/gems/coderay.rbi +91 -0
  479. data/sorbet/rbi/gems/concurrent-ruby.rbi +1587 -0
  480. data/sorbet/rbi/gems/debug_inspector.rbi +18 -0
  481. data/sorbet/rbi/gems/erubi.rbi +26 -0
  482. data/sorbet/rbi/gems/holidays.rbi +362 -0
  483. data/sorbet/rbi/gems/i18n.rbi +132 -0
  484. data/sorbet/rbi/gems/method_source.rbi +63 -0
  485. data/sorbet/rbi/gems/mime-types-data.rbi +16 -0
  486. data/sorbet/rbi/gems/mime-types.rbi +217 -0
  487. data/sorbet/rbi/gems/mimemagic.rbi +36 -0
  488. data/sorbet/rbi/gems/minitest.rbi +282 -0
  489. data/sorbet/rbi/gems/mustache.rbi +151 -0
  490. data/sorbet/rbi/gems/paperclip.rbi +644 -0
  491. data/sorbet/rbi/gems/parallel.rbi +81 -0
  492. data/sorbet/rbi/gems/parallel_tests.rbi +27 -0
  493. data/sorbet/rbi/gems/pry-rails.rbi +71 -0
  494. data/sorbet/rbi/gems/pry.rbi +1964 -0
  495. data/sorbet/rbi/gems/rack.rbi +228 -0
  496. data/sorbet/rbi/gems/railties.rbi +364 -0
  497. data/sorbet/rbi/gems/rspec-core.rbi +1704 -0
  498. data/sorbet/rbi/gems/rspec-expectations.rbi +389 -0
  499. data/sorbet/rbi/gems/rspec-support.rbi +206 -0
  500. data/sorbet/rbi/gems/rspec.rbi +14 -0
  501. data/sorbet/rbi/gems/spreadsheet_architect.rbi +10 -0
  502. data/sorbet/rbi/gems/sprockets.rbi +659 -0
  503. data/sorbet/rbi/gems/terrapin.rbi +115 -0
  504. data/sorbet/rbi/gems/testhq.rbi +14 -0
  505. data/sorbet/rbi/gems/thor.rbi +482 -0
  506. data/sorbet/rbi/gems/thread_safe.rbi +81 -0
  507. data/sorbet/rbi/gems/tzinfo.rbi +407 -0
  508. data/sorbet/rbi/gems/zeitwerk.rbi +131 -0
  509. data/sorbet/rbi/hidden-definitions/errors.txt +35111 -0
  510. data/sorbet/rbi/hidden-definitions/hidden.rbi +59974 -0
  511. data/sorbet/rbi/sorbet-typed/lib/actionmailer/all/actionmailer.rbi +13 -0
  512. data/sorbet/rbi/sorbet-typed/lib/actionpack/all/actionpack.rbi +855 -0
  513. data/sorbet/rbi/sorbet-typed/lib/actionview/all/actionview.rbi +314 -0
  514. data/sorbet/rbi/sorbet-typed/lib/activemodel/all/activemodel.rbi +423 -0
  515. data/sorbet/rbi/sorbet-typed/lib/activerecord/>=5.2/activerecord.rbi +17 -0
  516. data/sorbet/rbi/sorbet-typed/lib/activerecord/all/activerecord.rbi +1415 -0
  517. data/sorbet/rbi/sorbet-typed/lib/activerecord/~>6.0.0/activerecord.rbi +475 -0
  518. data/sorbet/rbi/sorbet-typed/lib/activestorage/>=6.0.0/activestorage.rbi +19 -0
  519. data/sorbet/rbi/sorbet-typed/lib/activestorage/all/activestorage.rbi +173 -0
  520. data/sorbet/rbi/sorbet-typed/lib/activesupport/>=6.0.0.rc1/activesupport.rbi +23 -0
  521. data/sorbet/rbi/sorbet-typed/lib/activesupport/all/activesupport.rbi +625 -0
  522. data/sorbet/rbi/sorbet-typed/lib/bundler/all/bundler.rbi +8684 -0
  523. data/sorbet/rbi/sorbet-typed/lib/minitest/all/minitest.rbi +99 -0
  524. data/sorbet/rbi/sorbet-typed/lib/railties/all/railties.rbi +25 -0
  525. data/sorbet/rbi/sorbet-typed/lib/rainbow/all/rainbow.rbi +254 -0
  526. data/sorbet/rbi/sorbet-typed/lib/ruby/all/gem.rbi +4222 -0
  527. data/sorbet/rbi/sorbet-typed/lib/ruby/all/open3.rbi +111 -0
  528. data/sorbet/rbi/sorbet-typed/lib/ruby/all/resolv.rbi +543 -0
  529. data/sorbet/rbi/todo.rbi +244 -0
  530. data/spec/commands/cards/available_cards_command_spec.rb +90 -0
  531. data/spec/commands/cards/card_data_command_spec.rb +1048 -0
  532. data/spec/constructors/has_helpers/base_constructor_spec.rb +49 -0
  533. data/spec/controllers/has_helpers/controller_spec.rb +71 -0
  534. data/spec/controllers/has_helpers/xlsx_export_spec.rb +96 -0
  535. data/spec/factories/advisors.rb +20 -0
  536. data/spec/factories/anonymous_cowards.rb +5 -0
  537. data/spec/factories/applications.rb +5 -0
  538. data/spec/factories/appointments.rb +6 -0
  539. data/spec/factories/attachment.rb +15 -0
  540. data/spec/factories/audits.rb +5 -0
  541. data/spec/factories/basic_schedules.rb +12 -0
  542. data/spec/factories/cards/card_definition_resources.rb +7 -0
  543. data/spec/factories/cards/card_definitions.rb +11 -0
  544. data/spec/factories/cards/card_fields.rb +22 -0
  545. data/spec/factories/cards/role_card_fields.rb +17 -0
  546. data/spec/factories/cards/role_resource_cards.rb +12 -0
  547. data/spec/factories/constant_definition.rb +7 -0
  548. data/spec/factories/contracts.rb +10 -0
  549. data/spec/factories/data_migration_models.rb +9 -0
  550. data/spec/factories/demographics.rb +22 -0
  551. data/spec/factories/events.rb +21 -0
  552. data/spec/factories/firms.rb +21 -0
  553. data/spec/factories/imports.rb +9 -0
  554. data/spec/factories/meetups.rb +13 -0
  555. data/spec/factories/organizations.rb +13 -0
  556. data/spec/factories/policies.rb +7 -0
  557. data/spec/factories/policy_notes.rb +9 -0
  558. data/spec/factories/read_only_values.rb +7 -0
  559. data/spec/factories/resources.rb +7 -0
  560. data/spec/factories/roles.rb +11 -0
  561. data/spec/factories/schedules.rb +13 -0
  562. data/spec/factories/shared/flattener/attributes.rb +10 -0
  563. data/spec/factories/shared/flattener/columns.rb +6 -0
  564. data/spec/factories/shared/flattener/resources.rb +5 -0
  565. data/spec/factories/templates.rb +12 -0
  566. data/spec/factories/user_organizations.rb +23 -0
  567. data/spec/factories/user_session.rb +11 -0
  568. data/spec/factories/users.rb +55 -0
  569. data/spec/fixtures/sample_config.yml +3 -0
  570. data/spec/graphql/has_helpers/mutations/external_data_sync_spec.rb +101 -0
  571. data/spec/graphql/has_helpers/resolvers/cards/available_cards_spec.rb +36 -0
  572. data/spec/graphql/has_helpers/resolvers/cards/card_data_spec.rb +42 -0
  573. data/spec/graphql/has_helpers/resolvers/cards/hover_card_data_spec.rb +59 -0
  574. data/spec/has_helpers/cards/cache_spec.rb +58 -0
  575. data/spec/has_helpers/cards/config_spec.rb +68 -0
  576. data/spec/has_helpers/cards/orphan_card_sweeper_spec.rb +97 -0
  577. data/spec/has_helpers/cards/remote_role_spec.rb +17 -0
  578. data/spec/has_helpers/cards/remote_roles_spec.rb +97 -0
  579. data/spec/has_helpers/cards/roles_spec.rb +52 -0
  580. data/spec/has_helpers/cards/smoke_spec.rb +168 -0
  581. data/spec/has_helpers/cards/support.rb +52 -0
  582. data/spec/has_helpers/resolvers/internal/roles_spec.rb +73 -0
  583. data/spec/has_helpers/service_trust/audit_log_cleanup_worker_spec.rb +33 -0
  584. data/spec/has_helpers/service_trust/audit_spec.rb +70 -0
  585. data/spec/has_helpers/service_trust/authenticated_as_service_spec.rb +65 -0
  586. data/spec/has_helpers/service_trust/authorization_spec.rb +37 -0
  587. data/spec/has_helpers/service_trust/capability_spec.rb +35 -0
  588. data/spec/has_helpers/service_trust/client_spec.rb +129 -0
  589. data/spec/has_helpers/service_trust/configuration_spec.rb +35 -0
  590. data/spec/has_helpers/service_trust/engine_spec.rb +13 -0
  591. data/spec/has_helpers/service_trust/errors_spec.rb +23 -0
  592. data/spec/has_helpers/service_trust/graphql_authentication_spec.rb +59 -0
  593. data/spec/has_helpers/service_trust/graphql_authorizable_spec.rb +91 -0
  594. data/spec/has_helpers/service_trust/graphql_client_spec.rb +47 -0
  595. data/spec/has_helpers/service_trust/jwks_client_spec.rb +85 -0
  596. data/spec/has_helpers/service_trust/jwks_controller_spec.rb +47 -0
  597. data/spec/has_helpers/service_trust/jwt_codec_spec.rb +30 -0
  598. data/spec/has_helpers/service_trust/key_manager_spec.rb +80 -0
  599. data/spec/has_helpers/service_trust/key_spec.rb +52 -0
  600. data/spec/has_helpers/service_trust/rake_tasks_spec.rb +58 -0
  601. data/spec/has_helpers/service_trust/rate_limiter_spec.rb +31 -0
  602. data/spec/has_helpers/service_trust/replay_cache_spec.rb +29 -0
  603. data/spec/has_helpers/service_trust/signer_verifier_spec.rb +159 -0
  604. data/spec/has_helpers/service_trust/smoke_spec.rb +216 -0
  605. data/spec/has_helpers/service_trust/support.rb +121 -0
  606. data/spec/has_helpers/service_trust/test_module_spec.rb +39 -0
  607. data/spec/has_helpers/service_trust/verify_middleware_spec.rb +82 -0
  608. data/spec/helpers/application_spec.rb +155 -0
  609. data/spec/importers/cards/importer_spec.rb +861 -0
  610. data/spec/internal/app/assets/config/manifest.js +0 -0
  611. data/spec/internal/app/constants/cat_egory.rb +33 -0
  612. data/spec/internal/app/constants/color.rb +19 -0
  613. data/spec/internal/app/constants/exclude_group_nil_constant.rb +5 -0
  614. data/spec/internal/app/constants/foo.rb +6 -0
  615. data/spec/internal/app/constants/fruit.rb +3 -0
  616. data/spec/internal/app/constants/generic_group_example.rb +12 -0
  617. data/spec/internal/app/constants/my_constant.rb +4 -0
  618. data/spec/internal/app/constants/product.rb +3 -0
  619. data/spec/internal/app/constants/shirt_size.rb +18 -0
  620. data/spec/internal/app/constants/simple_option.rb +14 -0
  621. data/spec/internal/app/constants/status.rb +5 -0
  622. data/spec/internal/app/constructors/organization_constructor.rb +4 -0
  623. data/spec/internal/app/controllers/application_controller.rb +27 -0
  624. data/spec/internal/app/controllers/search_extensions.rb +3 -0
  625. data/spec/internal/app/indices/advisor_index.rb +35 -0
  626. data/spec/internal/app/indices/contract_index.rb +52 -0
  627. data/spec/internal/app/indices/firm_index.rb +16 -0
  628. data/spec/internal/app/indices/policy_index.rb +20 -0
  629. data/spec/internal/app/models/advisor.rb +24 -0
  630. data/spec/internal/app/models/agency.rb +3 -0
  631. data/spec/internal/app/models/anonymous_coward.rb +5 -0
  632. data/spec/internal/app/models/another_event.rb +2 -0
  633. data/spec/internal/app/models/appointment.rb +3 -0
  634. data/spec/internal/app/models/attachment.rb +10 -0
  635. data/spec/internal/app/models/basic_schedule.rb +6 -0
  636. data/spec/internal/app/models/beneficiary.rb +6 -0
  637. data/spec/internal/app/models/client.rb +5 -0
  638. data/spec/internal/app/models/contract.rb +9 -0
  639. data/spec/internal/app/models/demographic.rb +4 -0
  640. data/spec/internal/app/models/division.rb +3 -0
  641. data/spec/internal/app/models/event.rb +10 -0
  642. data/spec/internal/app/models/event_template.rb +1 -0
  643. data/spec/internal/app/models/firm.rb +18 -0
  644. data/spec/internal/app/models/has_audits/audit.rb +15 -0
  645. data/spec/internal/app/models/import.rb +7 -0
  646. data/spec/internal/app/models/meetup.rb +8 -0
  647. data/spec/internal/app/models/organization.rb +17 -0
  648. data/spec/internal/app/models/organization_event.rb +5 -0
  649. data/spec/internal/app/models/policy.rb +16 -0
  650. data/spec/internal/app/models/policy_holder.rb +7 -0
  651. data/spec/internal/app/models/policy_note.rb +8 -0
  652. data/spec/internal/app/models/project.rb +3 -0
  653. data/spec/internal/app/models/read_only_value.rb +8 -0
  654. data/spec/internal/app/models/schedule.rb +12 -0
  655. data/spec/internal/app/models/template.rb +9 -0
  656. data/spec/internal/app/models/user.rb +31 -0
  657. data/spec/internal/app/models/user_organization.rb +17 -0
  658. data/spec/internal/app/views/has_lists/lists/_table.html.erb +1 -0
  659. data/spec/internal/config/database-standard.yml +24 -0
  660. data/spec/internal/config/initializers/resources.rb +19 -0
  661. data/spec/internal/config/routes.rb +19 -0
  662. data/spec/internal/config/storage.yml +3 -0
  663. data/spec/internal/db/good_job_schema.rb +141 -0
  664. data/spec/internal/db/schema.rb +334 -0
  665. data/spec/internal/log/.gitignore +1 -0
  666. data/spec/internal/public/favicon.ico +0 -0
  667. data/spec/lib/flattener/base_spec.rb +93 -0
  668. data/spec/lib/flattener/column/types_spec.rb +65 -0
  669. data/spec/lib/has_helpers/active_record/base_spec.rb +53 -0
  670. data/spec/lib/has_helpers/active_record/date_range_spec.rb +498 -0
  671. data/spec/lib/has_helpers/active_record/lookup_model_spec.rb +408 -0
  672. data/spec/lib/has_helpers/active_record/nested_resource_spec.rb +43 -0
  673. data/spec/lib/has_helpers/app/command/reports/generator_specs.rb +887 -0
  674. data/spec/lib/has_helpers/app/command_spec.rb +117 -0
  675. data/spec/lib/has_helpers/app/support/mocks/graphql_reports_helpers.rb +96 -0
  676. data/spec/lib/has_helpers/app/support/mocks/reports_api_mock.rb +47 -0
  677. data/spec/lib/has_helpers/attributes_spec.rb +220 -0
  678. data/spec/lib/has_helpers/aws_spec.rb +126 -0
  679. data/spec/lib/has_helpers/concerns/hydrate_concern_spec.rb +473 -0
  680. data/spec/lib/has_helpers/constant/definition_spec.rb +128 -0
  681. data/spec/lib/has_helpers/constant_spec.rb +432 -0
  682. data/spec/lib/has_helpers/core_ext/array_spec.rb +22 -0
  683. data/spec/lib/has_helpers/core_ext/date/infinity_spec.rb +67 -0
  684. data/spec/lib/has_helpers/core_ext/date_spec.rb +27 -0
  685. data/spec/lib/has_helpers/core_ext/false_class_spec.rb +7 -0
  686. data/spec/lib/has_helpers/core_ext/float_spec.rb +9 -0
  687. data/spec/lib/has_helpers/core_ext/hash_spec.rb +48 -0
  688. data/spec/lib/has_helpers/core_ext/integer_spec.rb +9 -0
  689. data/spec/lib/has_helpers/core_ext/nil_class_spec.rb +17 -0
  690. data/spec/lib/has_helpers/core_ext/object_spec.rb +54 -0
  691. data/spec/lib/has_helpers/core_ext/proc_spec.rb +24 -0
  692. data/spec/lib/has_helpers/core_ext/string_spec.rb +154 -0
  693. data/spec/lib/has_helpers/core_ext/true_class_spec.rb +7 -0
  694. data/spec/lib/has_helpers/data_migration/base_spec.rb +470 -0
  695. data/spec/lib/has_helpers/data_migration/large_worker_spec.rb +34 -0
  696. data/spec/lib/has_helpers/data_migration/master_worker_spec.rb +51 -0
  697. data/spec/lib/has_helpers/data_migration/migrator_spec.rb +113 -0
  698. data/spec/lib/has_helpers/data_migration/model_spec.rb +52 -0
  699. data/spec/lib/has_helpers/data_migration/small_worker_spec.rb +34 -0
  700. data/spec/lib/has_helpers/good_job/notification_alert_cleanup_worker_spec.rb +89 -0
  701. data/spec/lib/has_helpers/hash_accessors_spec.rb +66 -0
  702. data/spec/lib/has_helpers/index_spec.rb +442 -0
  703. data/spec/lib/has_helpers/lazy_config_spec.rb +31 -0
  704. data/spec/lib/has_helpers/organization_merge_spec.rb +88 -0
  705. data/spec/lib/has_helpers/resource_importer_spec.rb +119 -0
  706. data/spec/lib/has_helpers/rspec_spec.rb +16 -0
  707. data/spec/lib/has_helpers/search/provider_spec.rb +26 -0
  708. data/spec/lib/has_helpers/search/search_utils_spec.rb +134 -0
  709. data/spec/lib/has_helpers/sentry/graphql_trace_spec.rb +140 -0
  710. data/spec/lib/has_helpers/ui_attributes_spec.rb +42 -0
  711. data/spec/lib/has_helpers/utils/action_spec.rb +49 -0
  712. data/spec/lib/has_helpers/utils/alert_spec.rb +33 -0
  713. data/spec/lib/has_helpers/utils/dataset_spec.rb +10 -0
  714. data/spec/lib/has_helpers/utils/icon_spec.rb +23 -0
  715. data/spec/lib/has_helpers/utils/input_spec.rb +40 -0
  716. data/spec/lib/has_helpers/utils/log_notifier_spec.rb +25 -0
  717. data/spec/lib/has_helpers/utils/render_object_spec.rb +17 -0
  718. data/spec/lib/has_helpers/utils/s3_utils_spec.rb +78 -0
  719. data/spec/lib/has_helpers/utils/service_spec.rb +79 -0
  720. data/spec/lib/has_helpers/utils/slack_notifier_spec.rb +70 -0
  721. data/spec/lib/has_helpers/utils/user_restrictions_spec.rb +343 -0
  722. data/spec/lib/hq/structure/inserts_spec.rb +152 -0
  723. data/spec/lib/hq/structure_spec.rb +12 -0
  724. data/spec/models/cards/base_spec.rb +149 -0
  725. data/spec/models/cards/card_definition_resource_spec.rb +103 -0
  726. data/spec/models/cards/card_definition_spec.rb +50 -0
  727. data/spec/models/cards/card_field_spec.rb +46 -0
  728. data/spec/models/cards/field_builder_spec.rb +318 -0
  729. data/spec/models/cards/registry_spec.rb +147 -0
  730. data/spec/models/cards/role_card_field_spec.rb +111 -0
  731. data/spec/models/cards/role_resource_card_spec.rb +112 -0
  732. data/spec/models/has_helpers/master_record_spec.rb +20 -0
  733. data/spec/models/has_helpers/user_organization_spec.rb +28 -0
  734. data/spec/models/has_helpers/user_session_spec.rb +16 -0
  735. data/spec/presenters/base_presenter_spec.rb +166 -0
  736. data/spec/rails_helper.rb +119 -0
  737. data/spec/requests/has_helpers/factory_controller_spec.rb +159 -0
  738. data/spec/requests/has_helpers/health_check_controller_spec.rb +37 -0
  739. data/spec/scenarios/empty_setup.rb +6 -0
  740. data/spec/spec_helper.rb +22 -0
  741. data/spec/support/has_helpers/database_helpers.rb +12 -0
  742. data/spec/utils/sync_utils_spec.rb +233 -0
  743. data/spec/workers/goodjob/elasticsearch_process_batch_spec.rb +94 -0
  744. data/spec/workers/goodjob/elasticsearch_process_change_logs_master_worker_spec.rb +40 -0
  745. data/spec/workers/goodjob/elasticsearch_process_change_logs_worker_spec.rb +82 -0
  746. data/spec/workers/goodjob/elasticsearch_process_queue_spec.rb +89 -0
  747. data/spec/workers/goodjob/elasticsearch_reindex_spec.rb +49 -0
  748. metadata +1593 -0
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HasHelpers
4
+ module Concerns
5
+ # Shared parsing logic for the restrictions_hash stored on user_organizations.
6
+ #
7
+ # The field arrives as a single-encoded JSON string: hqadmin stores it via
8
+ # `RestrictionsAndPermissions#restrictions` which does `.to_json`, and that
9
+ # string is what the data sync delivers (hqadmin's own `serialize` unwraps the
10
+ # extra layer before sending). So a single `JSON.parse` is enough — this mirrors
11
+ # how readygop consumes it (`user_resource.rb`: `JSON.parse(restrictions_hash || "{}")`).
12
+ # We also accept a Hash as-is in case the column is jsonb.
13
+ #
14
+ # The `.to_json` at the source is the real defect (the field should hold an
15
+ # object); fixing it there is tracked in a separate ticket. Until then we just
16
+ # parse the string once.
17
+ #
18
+ # Canonical format is a JSON object keyed by resource name:
19
+ # {"Client" => {...}, "Team" => {...}}
20
+ # (The Array form handled by `normalize_restrictions` is a deprecated/bad-data
21
+ # shape — confirmed not a supported format — kept only as fail-closed defense.)
22
+ module RestrictionsParseable
23
+ private
24
+
25
+ def parse_raw_restrictions(raw, user_id: nil)
26
+ return {} if raw.blank?
27
+ return raw if raw.is_a?(Hash)
28
+
29
+ parsed = JSON.parse(raw)
30
+ parsed.is_a?(Hash) ? parsed : {}
31
+ rescue JSON::ParserError => e
32
+ Rails.logger.warn("[HasHelpers][Restrictions] Invalid restrictions_hash for user #{user_id}: #{e.message}")
33
+ {}
34
+ end
35
+
36
+ def normalize_restrictions(value)
37
+ case value
38
+ when Hash
39
+ value
40
+ when Array
41
+ value.each_with_object({}) do |item, acc|
42
+ next unless item.is_a?(Hash)
43
+ item.each { |k, v| acc[k] = v }
44
+ end
45
+ else
46
+ {}
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,11 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "has_helpers/index/config"
5
+
6
+ module HasHelpers
7
+ class Config < T::Struct
8
+ prop :index, ::HasHelpers::Index::Config, default: ::HasHelpers::Index::Config.new
9
+ prop :sync_custom_mapping, Hash, default: {}
10
+ end
11
+ end
@@ -0,0 +1,578 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ require "delegate"
5
+
6
+ module HasHelpers
7
+ # This module can be included into any standard ActiveRecord model to make its records
8
+ # available as databased-backed "constants". The definition of these records is contained
9
+ # in the individual model classes.
10
+ #
11
+ #=== Seeding Constants
12
+ # Each Constant class contains its own constant definitions and the appropriate database
13
+ # records can be seeded by calling the `.seed!` method. This can easily be done in a migration
14
+ # For example:
15
+ #
16
+ # # app/constants/my_const.rb
17
+ # class MyConst < ActiveRecord::Base
18
+ # include HasHelpers::Constant
19
+ # define_constants "Foo", "Bar"
20
+ # end
21
+ #
22
+ # # db/migrate/20190226170740_seed_my_const.rb
23
+ # ::MyConst.seed!
24
+ #
25
+ # However, care must be given to avoid referencing Constants in code which is executed
26
+ # during application load. Referencing a Constant for which there is no matching record
27
+ # in the database will cause an exception. In a Rails application such a situation would
28
+ # make it impossible to run the `db:seed` Rake task.
29
+ #
30
+ # class Foo
31
+ # validates :attr, inclusion: MyConst::ALL.map(&:name) # <- This will cause an exception unless all MyConst records have been seeded.
32
+ #
33
+ # To avoid that problem you should structure your code to only use constants _after_
34
+ # the applications has been loaded. See also the notes below regarding [Lazy Attributes](#lazy-attributes).
35
+ #
36
+ #==== Constant Names Without Referencing Constants
37
+ # It is possible to access the names of the constant records using just the configuration.
38
+ # A list of constant names is return by calling `.constant_names` on a Constant class.
39
+ # This does not rely on having database records seeded and can thus be used in cases
40
+ # where records may not have been seeded. While this approach is currently limited
41
+ # it does provide a working alternative to the broken example above.
42
+ #
43
+ # class Foo
44
+ # validates :attr, inclusion: MyConst.constant_names # <- This will work regardless of whether the MyConst records have been seeded.
45
+ #
46
+ #=== Examples
47
+ # class FujitaScale
48
+ # include HasHelpers::Constant
49
+ # define_constants "F1", "F2", "F3", "F4", "F5"
50
+ # end
51
+ #
52
+ # FujitaScale::F1 # => <FujitaScale name="F1">
53
+ # FujitaScale::ALL # => [<FujitaScale name="F1">, <FujitaScale name="F2">, <FujitaScale name="F3">, <FujitaScale name="F4">, <FujitaScale name="F5">]
54
+ #
55
+ # class Alignment
56
+ # include HasHelpers::Constant
57
+ #
58
+ # # See also the section on Shared Attributes below.
59
+ # define_constants(
60
+ # "Top".to_constant(type: "Vertical"),
61
+ # "Middle".to_constant(type: "Vertical"),
62
+ # "Bottom".to_constant(type: "Vertical"),
63
+ #
64
+ # "Left".to_constant(type: "Horizontal"),
65
+ # "Center".to_constant(type: "Horizontal"),
66
+ # "Right".to_constant(type: "Horizontal")
67
+ # )
68
+ #
69
+ # define_constant_group "VERTICAL" do
70
+ # [TOP, MIDDLE, BOTTOM]
71
+ # end
72
+ #
73
+ # define_constant_group("HORIZONTAL") { [LEFT, CENTER, RIGHT] } # Terser syntax
74
+ # end
75
+ #
76
+ # Alignment::HORIZONTAL # => [<Alignment name="Left" type="Horizontal">, <Alignment name="Center" type="Horizontal">, <Alignment name="Right" type="Horizontal">]
77
+ # Alignment::TOP # => <Alignment name="Top" type="Vertical">
78
+ #
79
+ #=== Key Attribute
80
+ # When defining a constant a value for the `key` attribute is automatically generated
81
+ # from the given name. From the examples above the `Alignment` constant with the name
82
+ # "Top" is assigned the key `TOP`. That means that `Alignment::TOP` will be Ruby constant
83
+ # through which users should reference.
84
+ #
85
+ # In some cases the given name of the defined constant is not convertible to a valid
86
+ # key (Ruby constant name), or the resulting key is not desirable. In these cases it
87
+ # is possible to explicitly set the key value.
88
+ #
89
+ # class AgeBand
90
+ # include HasHelpers::Constant
91
+ # define_constant "0-17".to_constant(key: "ZERO_TO_SEVENTEEN") # Explicit key
92
+ #
93
+ #=== Shared Attributes
94
+ # Sometimes many constants will have attributes in common. Such common attributes may
95
+ # be assigned by passing them into a `define_constants` call. In this case each constant
96
+ # definition will be assigned the common attributes.
97
+ #
98
+ # The constants from the previous example can be rewritten as the following:
99
+ #
100
+ # define_constants "Top", "Middle", "Bottom", type: "Vertical"
101
+ # define_constants "Left", "Center", "Right", type: "Horizontal"
102
+ #
103
+ #=== Lazy Attributes
104
+ # If attributes need to be dynamically resolved, then wrap them in a block. This allows
105
+ # a constant definition to reference another constant without needing to load the other
106
+ # constant immediately.
107
+ #
108
+ # class Registration
109
+ # define_constants(
110
+ # # Do not force the Exam::S4 constant to be loaded immediatily.
111
+ # "Registered Options Principal".to_constant(abbreviation: "OP", exam: proc { ::Exam::S4 })
112
+ #
113
+ # # The +:key+ option must _not_ be lazy! If +:name+ is given without +:key+ then +:name+ must likewise _not_ be lazy.
114
+ # "Registered Options Principal".to_constant(key: "OP", abbreviation: "OP", exam: proc { ::Exam::S4} }
115
+ module Constant
116
+ extend T::Helpers
117
+
118
+ # Container class for a constant definition. Attributes are accessible via reader methods.
119
+ #=== Examples
120
+ # definition = Definition.new(name: "Kansas", abbreviation: "KS")
121
+ # definition.name # => "Kansas"
122
+ # definition.abbreviation # => "KS"
123
+ # definition.attributes # => { name: "Kansas", abbreviation: "KS" }
124
+ #
125
+ # # Lazy block -- this is useful for creating groups or other constants which
126
+ # # we do not want to load immediately.
127
+ # Definition.new(name: "Annuity") { [ADDITIONAL, INITIAL, RENEWAL, TRIAL, BONUS] }
128
+ #
129
+ class Definition
130
+ ERROR_MSG_MISSING_KEY = "At least one option from :name and :key is required"
131
+
132
+ attr_writer :attrs, :key
133
+ attr_accessor :block, :group_foreign_key, :persisted_parent, :include_parent_group_id
134
+
135
+ #=== Options
136
+ # +__attrs__+: Data attributes. These will be used to look up the constant.
137
+ def initialize(key: nil, name: nil, group_foreign_key: nil, **attrs, &block)
138
+ raise ArgumentError, ERROR_MSG_MISSING_KEY unless name || key
139
+ self.attrs = attrs
140
+ self.key = key
141
+ self.group_foreign_key = group_foreign_key
142
+ self.persisted_parent = !!group_foreign_key
143
+ attrs[:name] = name if name
144
+ self.block = block
145
+ end
146
+
147
+ # Returns a hash of attributes which define the constant. Some attributes
148
+ # may be "lazy", meaning they are wrapped inside a Proc. To get the attributes
149
+ # with their final evaluated values pass use the force_eval option, e.g.
150
+ # `attr(force_eval: true)`
151
+ def attrs(force_eval: false)
152
+ if force_eval
153
+ attrs.each do |k, v|
154
+ # Evaluate each lazy attribute by calling its proc and storing the result
155
+ if v.respond_to?(:call)
156
+ attrs[k] = v.call
157
+ end
158
+ end
159
+ attrs
160
+ else
161
+ @attrs ||= {}
162
+ end
163
+ end
164
+
165
+ def key # Note that this may hide the original key value specified in the attributes.
166
+ (@key || name.to_s.constant_case).to_sym # Return a symbol for easy comparison with the argument to const_missing, which is a symbol
167
+ end
168
+
169
+ def name
170
+ @attrs[:name]
171
+ end
172
+
173
+ def lazy?
174
+ @block.present?
175
+ end
176
+
177
+ def evaluate
178
+ @block.call
179
+ end
180
+
181
+ def persisted_parent?
182
+ @persisted_parent
183
+ end
184
+
185
+ def to_s
186
+ name
187
+ end
188
+
189
+ def to_constant
190
+ self
191
+ end
192
+
193
+ def load(klass = nil)
194
+ lazy? && !persisted_parent? ? evaluate : load_record(klass)
195
+ end
196
+
197
+ def seed!(klass)
198
+ find_by_attrs = attrs(force_eval: true)
199
+ find_by_attrs[:id] = default_id(klass) if klass.simple
200
+ find_by_attrs[:option_type] = default_option_type(klass) if klass.has_attribute? :option_type
201
+ # Leaving out the group_foreign_key attr from the query for the cases where we're seeding after modifying it.
202
+ # Including it would cause those queries to fail and it would try to initialize a new constant without it being necessary.
203
+ returned = klass.where(find_by_attrs.with_indifferent_access.except(self.group_foreign_key)).first_or_initialize.tap do |constant|
204
+ constant.assign_attributes(find_by_attrs.with_indifferent_access.slice(self.group_foreign_key)) if find_by_attrs.with_indifferent_access.key?(self.group_foreign_key)
205
+ end
206
+ returned.save!
207
+ returned
208
+ end
209
+
210
+ def meta
211
+ @meta ||= OpenStruct.new
212
+ end
213
+
214
+ private
215
+
216
+ def default_id(klass)
217
+ "#{klass.name}::::#{name}"
218
+ end
219
+
220
+ def default_option_type(klass)
221
+ "#{klass}"
222
+ end
223
+
224
+ def load_record(klass)
225
+ attributes = attrs(force_eval: true).symbolize_keys
226
+ record = if klass.simple
227
+ rec = klass.new(id: default_id(klass), **attributes)
228
+ rec.option_type = default_option_type(klass) if klass.has_attribute? :option_type
229
+ # We can assume the record is persisted here, but because it was
230
+ # not loaded from the database, the object returns `false` from
231
+ # `#persisted?`. So rails does not try to save this record again,
232
+ # we need to set "@new_record" to `false`.
233
+ rec.instance_variable_set("@new_record", false)
234
+ rec
235
+ else
236
+ klass.find_by(attributes)
237
+ end
238
+
239
+ if record
240
+ record.definition = self
241
+ record # return the record
242
+ else
243
+ raise(NameError, "the constant #{ klass.name }::#{ key } does not exist\n- Make sure the corresponding record is in the database. You may need to run `rake db:seed`")
244
+ end
245
+ end
246
+ end
247
+
248
+ class Proxy < BasicObject
249
+ instance_methods.select { |m| m !~ /__.*__/ }.each { |m| undef_method(m) }
250
+
251
+ def initialize(klass, constant_name)
252
+ @klass = klass
253
+ @constant_name = constant_name
254
+ @mutex = ::Mutex.new
255
+ end
256
+
257
+ def __getobj__
258
+ @mutex.synchronize do # Make sure this does not get evaluated simultaneously across threads.
259
+ @__getobj__ ||= begin
260
+ @klass.send(:remove_const, @constant_name) if @klass.const_defined?(@constant_name, false) # Remove the Proxy object (self) which has served as a constant place-holder.
261
+ @klass.const_get(@constant_name, false)
262
+ end
263
+ end
264
+ end
265
+
266
+ # Delegate all methods to the underlying object.
267
+ def method_missing(method_name, *args, **kwargs, &block)
268
+ __getobj__.send(method_name, *args, **kwargs, &block)
269
+ end
270
+ end
271
+
272
+ extend ActiveSupport::Concern
273
+
274
+ def self.included(klass)
275
+ klass.include(InstanceMethods) # Use include rather than prepend so to not override user-defined instance methods on the class.
276
+ end
277
+
278
+ module InstanceMethods
279
+ def to_renderable
280
+ name.to_s.to_text_node
281
+ end
282
+
283
+ def definition=(definition)
284
+ @definition = definition
285
+ end
286
+
287
+ def definition
288
+ @definition
289
+ end
290
+
291
+ def is_parent?
292
+ !!definition&.persisted_parent || self.class.is_parent?(self)
293
+ end
294
+
295
+ def meta
296
+ definition && definition.meta
297
+ end
298
+ end
299
+
300
+ module ClassMethods
301
+ # An abstract constant class is a class that constant classes inherit from
302
+ # but does not have any constants of its own. This prevents the `ALL` constant
303
+ # from being defined on an ancestor, which can cause problems for its descendants.
304
+ attr_accessor :abstract_constant_class
305
+
306
+ # Set `self.simple = true` if a constant is unique by type and name only.
307
+ # This enables a few optimizations:
308
+ # 1) The constant never has to be loaded from the db
309
+ # 2) The constant can be set just by the name
310
+ # Ex: `advisor.status = "Active"` is enough information to determine which constant to use.
311
+ attr_writer :simple, :grouped
312
+
313
+ # grouped it is used to filter constants with group_id = nil
314
+ # as default scope.
315
+ def grouped
316
+ @grouped || false
317
+ end
318
+
319
+ def simple
320
+ @simple || (superclass.respond_to?(:simple) && superclass.simple)
321
+ end
322
+
323
+ def supports_grouping?
324
+ !!reflect_on_association(:group_children)
325
+ end
326
+
327
+ def is_parent?(klass)
328
+ __parent_constants__.any? { |c| c.name === klass.name }
329
+ end
330
+
331
+ # Method to be able to reference a constant via Active Record
332
+ # Handles a GET request
333
+ # @param constant_name [CONSTANT] the constant being requested
334
+ # @return [Array, ActiveRecord] the active record model looked
335
+ def const_missing(constant_name)
336
+ constant_name = constant_name.to_sym
337
+ if constant_name == :ALL && !self.abstract_constant_class # Special case for ::ALL
338
+ const_set(:ALL, __regular_constant_definitions__.map { |definition| __fetch_const_from_definition__(definition) })
339
+ elsif constant_name == :ALL_GROUPS && !self.abstract_constant_class # Special case for :ALL_GROUPS
340
+ const_set(:ALL_GROUPS, __parent_constants__.map { |definition| __fetch_const_from_definition__(definition) })
341
+ elsif __constants__.has_key?(constant_name)
342
+ const_set(constant_name, __constants__[constant_name].load(self))
343
+ else
344
+ super
345
+ end
346
+ end
347
+
348
+ # Returns constant definitions
349
+ def define_constants(*constant_definitions, key_prefix: nil, **attrs)
350
+ constant_definitions.map(&:to_constant).tap do |definitions|
351
+ definitions.each do |constant_definition|
352
+ constant_definition.attrs.merge!(attrs)
353
+ constant_definition.key = "#{ key_prefix.constant_case }_#{ constant_definition.key }" if key_prefix
354
+
355
+ existing_attrs = __constants__[constant_definition.key]&.attrs || {}
356
+ constant_definition.attrs = existing_attrs.merge!(constant_definition.attrs)
357
+ __constants__[constant_definition.key] = constant_definition # Register the definition in the look-up hash
358
+
359
+ # This gets a bit complicated, but in short we need a way to prevent Ruby from returning
360
+ # a constant which is defined in an ancestor module. E.g. if `IMO` is a top-level class
361
+ # then we don't want `MyConstant::IMO` to return that top-level class.
362
+ # To stop this undesired behavior the code below sets the constant on the current class
363
+ # to be a proxy. When that Proxy is evaluated it will set load the constant as expected.
364
+ remove_const(constant_definition.key) if const_defined?(constant_definition.key, false)
365
+ const_set(constant_definition.key, Proxy.new(self, constant_definition.key))
366
+ end
367
+ end
368
+ end
369
+
370
+ def define_constant(constant_definition, **attrs)
371
+ define_constants(constant_definition, **attrs).first
372
+ end
373
+
374
+ # Defines a single constant group
375
+ #
376
+ # Constants in the group can be either actual ruby constants, constant definitions, or strings.
377
+ #
378
+ # If they're actual ruby constants, then they should go in a block that's
379
+ # passed into either the to_constant method of the parent or the
380
+ # define_constant_group method.
381
+ #
382
+ # If they're strings or constant definitions, then they should go in the constants parameter.
383
+ #
384
+ # The group_name parameter is used to set a custom group name. This is only
385
+ # used when the group is persisted in the database. If the group is
386
+ # persisted and no group_name is given, then it defaults to #{parent.key.upcase}_GROUP
387
+ #
388
+ # The group_foreign_key parameter determines whether or not the group association is
389
+ # persisted into the database. If it's nil, then the group exists only in
390
+ # memory, otherwise both the children and the parent will have the
391
+ # column specified by the group_foreign_key set to the parent's id.
392
+ #
393
+ # The include_parent_group_id parameter is used to set the group_id of the parent (false by default)
394
+ # This behavior sometimes is needed.
395
+ #
396
+ #=== Examples
397
+ #
398
+ # # This shows the different ways to put constants in a group
399
+ # define_constants('Lapsed', 'Reversed', 'Returned', 'Replaced')
400
+ # define_constant_group(
401
+ # 'Closed'.to_constant { [LAPSED, REVERSED] }, # Closed is the parent, LAPSED and REVERSE are in the group
402
+ # group_name: "CLOSED_STATUSES", # CLOSED evaluates to the parent closed status, while CLOSED_STATUSES gives the constants in the group.
403
+ # group_foreign_key: :group_id, # All constants in the group will have their group_id set to CLOSED.id
404
+ # constants: ['DECLINED', 'SURRENDERED']
405
+ # ) { [RETURNED, REPLACED] } # RETURNED and REPLACED are in the group
406
+ #
407
+ # define_constant_group "POLICIES" do
408
+ # [AMENDMENT_FORM, APPLICATIONS, AWAIT_FUNDING, TENTHIRTYFIVE_FORM]
409
+ # end
410
+ def define_constant_group(parent, group_name: nil, group_foreign_key: nil, constants: nil, include_parent_group_id: true, &block)
411
+ child_defs = constants ? Array(constants).map(&:to_constant).each { |child| child.group_foreign_key = group_foreign_key } : nil
412
+ child_defs = define_constants(*child_defs.map(&:to_constant)) if child_defs&.any?
413
+ group_block = group_proc(child_defs, parent, block)
414
+ parent_definition = parent.to_constant.tap do |parent_def|
415
+ parent_def.group_foreign_key = group_foreign_key
416
+ parent_def.persisted_parent = !!group_foreign_key
417
+ parent_def.block = group_block if group_block
418
+ parent_def.include_parent_group_id = include_parent_group_id
419
+ end
420
+
421
+ if group_foreign_key
422
+ # When the parent is an actual constant in the database, we assign a separate name for the group
423
+ # so that parent constant still evaluates to its record.
424
+ group_name ||= "#{parent.to_s.constant_case}_GROUP"
425
+ group_definition = group_name.to_constant.tap do |group|
426
+ group.block = proc do
427
+ child_constants = group_block.call
428
+ [__fetch_const_from_definition__(parent_definition), *child_constants].uniq
429
+ end
430
+ end
431
+
432
+ if self.reflect_on_association(:children).nil?
433
+ self.has_many :group_children, class_name: self.name, foreign_key: "#{group_foreign_key}"
434
+ self.belongs_to :group_parent, class_name: self.name, foreign_key: "#{group_foreign_key}", optional: true
435
+ end
436
+
437
+ define_constant(group_definition)
438
+ end
439
+ define_constant(parent_definition)
440
+ end
441
+
442
+ def group_proc(const_defs, parent, block)
443
+ existing_parent_block = parent.to_constant.block
444
+ proc do
445
+ grouped_constants = []
446
+ grouped_constants << existing_parent_block.call if existing_parent_block
447
+ grouped_constants << __defs_to_consts__(const_defs) if const_defs
448
+ grouped_constants << block.call if block
449
+ grouped_constants.flatten!
450
+ grouped_constants = grouped_constants.first if grouped_constants.one? && grouped_constants.first.is_a?(::Hash)
451
+ grouped_constants
452
+ end
453
+ end
454
+
455
+ def constant_names
456
+ __regular_constant_definitions__.map(&:name)
457
+ end
458
+
459
+ # Load the seeds for this constant.
460
+ # Note, if the current constant depends on other constants, then those should be loaded first.
461
+ # Do so by overriding the `.seed!` method, loading the dependencies, and then calling super.
462
+ #
463
+ # class Registration
464
+ # def self.seed!
465
+ # ::Exam.seed! # Load Exams before Registrations.
466
+ # super
467
+ # end
468
+ def seed!
469
+ self.grouped = false
470
+ __regular_constant_definitions__.each { |definition| definition.seed!(self) }
471
+ set_parent_child_associations
472
+ nil # Returns this rather than the enumerator from the previous line.
473
+ end
474
+
475
+ def set_parent_child_associations
476
+ __parent_constants__.each do |parent_definition|
477
+ parent_instance = parent_definition.load(self)
478
+ parent_instance.reload if parent_definition.include_parent_group_id
479
+ parent_definition.attrs["#{parent_definition.group_foreign_key}"] = parent_instance.id
480
+ parent_instance.send("#{parent_definition.group_foreign_key}=", parent_instance.id)
481
+ parent_instance.save!
482
+ children = parent_definition.evaluate
483
+ children.each do |child_instance|
484
+ foreign_key = parent_definition.group_foreign_key
485
+ # Simple constants are created w/o fetching from the db.
486
+ # Since we're saving the constant, we need to load from the database so attributes are up-to-date
487
+ child_instance.reload if simple
488
+ child_instance.public_send("#{foreign_key}=", parent_instance.id)
489
+ child_instance.save!
490
+ child_def = child_instance.definition
491
+ child_def.attrs["#{foreign_key}"] = parent_instance.id
492
+ __constants__[child_def.key] = child_def
493
+ end
494
+ end
495
+ end
496
+
497
+ # Accepts a query string and returns the matching record, if any.
498
+ # If there is an ambiguity which causes more than one record to be
499
+ # match then an error will be raised. For such cases it may be
500
+ # necessary to override this method and provide a way to disambiguate lookups.
501
+ #
502
+ # === Examples
503
+ # # Lookup by name
504
+ # FujitaScale.lookup("F1") # => <FujitaScale name="F1">
505
+ # FujitaScale.lookup(name: "F1") # => <FujitaScale name="F1">
506
+ #
507
+ # # Lookup by an attribute other than name
508
+ # FujitaScale.lookup(key: :F1) # => <FujitaScale name="F1">
509
+ # State.lookup(abbreviation: "KS") # => <State name="Kansas" abbreviation="KS">
510
+ #
511
+ # # Lookup by incorrect name (does not exist)
512
+ # FujitaScale.lookup("F14") # => nil
513
+ #
514
+ # # Ambiguous lookup (multiple matches)
515
+ # Alignment.lookup(type: "Horizontal") # => Error!
516
+ def lookup(query = nil, name: query, id: nil, key: nil, case_sensitive: false, **attrs)
517
+ if name || id || key || attrs.any? # Perform lookup if any criterion is present. Otherwise nil is returned.
518
+ matches = Enumerator.new do |yielder|
519
+ already_yielded = false
520
+ case_converter = case_sensitive ? :itself : :downcase
521
+ __constants__.
522
+ lazy.
523
+ select do |constant_key, definition|
524
+ (!name || definition.name.send(case_converter) == name.send(case_converter)) &&
525
+ (!key || key.to_s == constant_key.to_s) &&
526
+ attrs.all? { |k, v| definition.attrs(force_eval: true)[k] == v }
527
+ end.
528
+ each do |_, definition|
529
+ raise "Lookup matched more than one record" if already_yielded
530
+
531
+ const = __fetch_const_from_definition__(definition)
532
+
533
+ # We can't lookup by id until after loading constants
534
+ if !id || (!const.is_a?(Array) && const.id.to_s == id.to_s)
535
+ yielder << const
536
+ already_yielded = true
537
+
538
+ # Break early if we find a matching record with an id.
539
+ # An id will be unique so we don't care about raising
540
+ # an error when there are more than one matched record.
541
+ break if id
542
+ end
543
+ end
544
+ end
545
+ matches.to_a.first
546
+ end
547
+ end
548
+
549
+ private
550
+
551
+ def __constants__
552
+ @__constants__ ||= {}
553
+ end
554
+
555
+ def __parent_constants__
556
+ __regular_constant_definitions__.select(&:persisted_parent)
557
+ end
558
+
559
+ def __regular_constant_definitions__
560
+ __constants__.map { |_, definition| definition }.reject do |definition|
561
+ definition.lazy? && !definition.persisted_parent?
562
+ end.sort_by do |definition|
563
+ definition.persisted_parent? ? 0 : 1
564
+ end
565
+ end
566
+
567
+ def __fetch_const_from_definition__(definition)
568
+ const_get(definition.to_constant.key)
569
+ end
570
+
571
+ def __defs_to_consts__(definitions)
572
+ definitions.map { |definition| __fetch_const_from_definition__(definition) }
573
+ end
574
+ end
575
+
576
+ mixes_in_class_methods(ClassMethods)
577
+ end
578
+ end
@@ -0,0 +1,10 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ module HasHelpers::Constraints
5
+ ARRAY_LIMIT = 100
6
+ JSON_LIMIT = 10_000
7
+ TEXT_LIMIT = 10_000
8
+ NOTE_LIMIT = 20_000
9
+ LIMIT_ERROR_MSG = "Exceeds the storage limit"
10
+ end
@@ -0,0 +1,28 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module HasHelpers
5
+ module CoreExt
6
+ module Array
7
+ #=== Examples
8
+ # ["Initial", "Renewal", "Trial"].to_constant
9
+ # ["Initial", "Renewal", "Trial", :type => "Annuity"].to_constant
10
+ def to_constant
11
+ _to_constant(*self)
12
+ end
13
+
14
+ def to_renderable
15
+ to_enum
16
+ end
17
+
18
+ private
19
+
20
+ def _to_constant(*constant_names)
21
+ options = constant_names.extract_options!
22
+ constant_names.map { |name| name.to_constant(**options) }
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ ::Array.include(::HasHelpers::CoreExt::Array)