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.
- checksums.yaml +7 -0
- data/.gitignore +38 -0
- data/.rspec +3 -0
- data/.rubocop.yml +10 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.tekton/Chart.yaml +14 -0
- data/.tekton/templates/pipelinerun.yaml +44 -0
- data/.tekton/values-main.yaml +53 -0
- data/.tekton/values-pr.yaml +40 -0
- data/.tekton/values.yaml +17 -0
- data/.tool-versions +1 -0
- data/CHANGELOG.md +204 -0
- data/Gemfile +25 -0
- data/README.md +76 -0
- data/Rakefile +46 -0
- data/app/api/has_helpers/rack_api.rb +116 -0
- data/app/api/has_helpers/search_api.rb +173 -0
- data/app/assets/stylesheets/has_helpers/base.scss +5 -0
- data/app/assets/stylesheets/has_helpers/color_palette.scss +33 -0
- data/app/assets/stylesheets/has_helpers/color_palettes/default.scss +11 -0
- data/app/assets/stylesheets/has_helpers/components/dataset.scss +43 -0
- data/app/assets/stylesheets/has_helpers/mixins.scss +149 -0
- data/app/commands/has_helpers/cards/available_cards_command.rb +98 -0
- data/app/commands/has_helpers/cards/card_data_command.rb +1062 -0
- data/app/commands/reports/base_options.rb +79 -0
- data/app/commands/reports/belongs_to_options.rb +178 -0
- data/app/commands/reports/constant_options.rb +66 -0
- data/app/commands/reports/external_graphql_client.rb +116 -0
- data/app/commands/reports/fill_dates.rb +65 -0
- data/app/commands/reports/filter_option_generator.rb +186 -0
- data/app/commands/reports/generator.rb +310 -0
- data/app/commands/reports/has_many_options.rb +105 -0
- data/app/commands/reports/importer.rb +132 -0
- data/app/commands/reports/nested_attribute_options.rb +134 -0
- data/app/constants/color.rb +9 -0
- data/app/constants/file_format.rb +12 -0
- data/app/constants/has_helpers/cards/card_custom_field.rb +12 -0
- data/app/constants/has_helpers/cards/card_data_type.rb +11 -0
- data/app/constants/has_helpers/cards/card_identifier.rb +10 -0
- data/app/constants/has_helpers/cards/card_layout_type.rb +11 -0
- data/app/constants/has_helpers/cards/card_variant_type.rb +12 -0
- data/app/constants/has_helpers/link_type.rb +10 -0
- data/app/constants/has_helpers/notification_alert_status.rb +14 -0
- data/app/constants/has_helpers/notification_alert_type.rb +10 -0
- data/app/constants/has_helpers/operation.rb +11 -0
- data/app/constants/has_helpers/resource_type.rb +25 -0
- data/app/constants/icon.rb +22 -0
- data/app/constants/logical_operator.rb +7 -0
- data/app/constants/option.rb +10 -0
- data/app/constants/time_zone.rb +12 -0
- data/app/constructors/has_helpers/base_constructor.rb +62 -0
- data/app/controllers/concerns/has_helpers/service_trust/authenticated_as_service.rb +51 -0
- data/app/controllers/concerns/has_helpers/service_trust/graphql_authentication.rb +57 -0
- data/app/controllers/has_helpers/controller.rb +63 -0
- data/app/controllers/has_helpers/factory_controller.rb +261 -0
- data/app/controllers/has_helpers/graphql_extensions.rb +83 -0
- data/app/controllers/has_helpers/health_check_controller.rb +46 -0
- data/app/controllers/has_helpers/search_extensions.rb +92 -0
- data/app/controllers/has_helpers/service_trust/jwks_controller.rb +20 -0
- data/app/controllers/has_helpers/xlsx_export.rb +115 -0
- data/app/controllers/search_extensions.rb +5 -0
- data/app/graphql/has_helpers/base_type.rb +13 -0
- data/app/graphql/has_helpers/base_union.rb +9 -0
- data/app/graphql/has_helpers/enums/resource_enum.rb +24 -0
- data/app/graphql/has_helpers/middleware/application_access.rb +26 -0
- data/app/graphql/has_helpers/mutations/base.rb +27 -0
- data/app/graphql/has_helpers/mutations/external_data_sync.rb +72 -0
- data/app/graphql/has_helpers/mutations/mark_individual_alert_read.rb +31 -0
- data/app/graphql/has_helpers/mutations/mark_notifications_read.rb +50 -0
- data/app/graphql/has_helpers/mutations/toggle_favorite.rb +39 -0
- data/app/graphql/has_helpers/resolvers/app_nav.rb +107 -0
- data/app/graphql/has_helpers/resolvers/base.rb +27 -0
- data/app/graphql/has_helpers/resolvers/cards/available_cards.rb +31 -0
- data/app/graphql/has_helpers/resolvers/cards/card_data.rb +74 -0
- data/app/graphql/has_helpers/resolvers/cards/hover_card_data.rb +153 -0
- data/app/graphql/has_helpers/resolvers/favorite.rb +62 -0
- data/app/graphql/has_helpers/resolvers/internal/roles.rb +54 -0
- data/app/graphql/has_helpers/resolvers/is_favorite.rb +24 -0
- data/app/graphql/has_helpers/resolvers/list_notification_alerts.rb +47 -0
- data/app/graphql/has_helpers/resolvers/lookup.rb +36 -0
- data/app/graphql/has_helpers/resolvers/recently_viewed.rb +54 -0
- data/app/graphql/has_helpers/resolvers/resource_tab.rb +22 -0
- data/app/graphql/has_helpers/resolvers/search.rb +18 -0
- data/app/graphql/has_helpers/resolvers/search_query.rb +38 -0
- data/app/graphql/has_helpers/resolvers/user_application_order.rb +35 -0
- data/app/graphql/has_helpers/resources/organization_option_resource.rb +28 -0
- data/app/graphql/has_helpers/resources/organization_resource_resource.rb +18 -0
- data/app/graphql/has_helpers/resources/resource_group_resource.rb +10 -0
- data/app/graphql/has_helpers/resources/resource_resource.rb +21 -0
- data/app/graphql/has_helpers/resources/user_role_resource.rb +9 -0
- data/app/graphql/has_helpers/types/application_order_type.rb +18 -0
- data/app/graphql/has_helpers/types/cards/available_card_type.rb +20 -0
- data/app/graphql/has_helpers/types/cards/card_badge_type.rb +12 -0
- data/app/graphql/has_helpers/types/cards/card_config_type.rb +18 -0
- data/app/graphql/has_helpers/types/cards/card_data_type.rb +13 -0
- data/app/graphql/has_helpers/types/cards/card_entry_field_type.rb +21 -0
- data/app/graphql/has_helpers/types/cards/card_entry_type.rb +12 -0
- data/app/graphql/has_helpers/types/cards/card_field_config_type.rb +17 -0
- data/app/graphql/has_helpers/types/layouts/app_nav/link_section_type.rb +17 -0
- data/app/graphql/has_helpers/types/layouts/app_nav/link_type.rb +20 -0
- data/app/graphql/has_helpers/types/layouts/app_nav/sub_link_type.rb +17 -0
- data/app/graphql/has_helpers/types/layouts/app_nav/type.rb +16 -0
- data/app/graphql/has_helpers/types/lookup.rb +16 -0
- data/app/graphql/has_helpers/types/resource_tab/type.rb +15 -0
- data/app/graphql/has_helpers/types/role_type.rb +13 -0
- data/app/graphql/has_helpers/types/search/detail_badge_type.rb +15 -0
- data/app/graphql/has_helpers/types/search/detail_item_type.rb +14 -0
- data/app/graphql/has_helpers/types/search/detail_type.rb +16 -0
- data/app/graphql/has_helpers/types/search/filter_operation_enum.rb +19 -0
- data/app/graphql/has_helpers/types/search/input_type.rb +16 -0
- data/app/graphql/has_helpers/types/search/result_type.rb +72 -0
- data/app/graphql/has_helpers/types/search/search_filter_input_type.rb +17 -0
- data/app/graphql/has_helpers/types/search/type.rb +17 -0
- data/app/importers/has_helpers/cards/importer.rb +1223 -0
- data/app/lib/has_helpers/application_helper.rb +21 -0
- data/app/lib/has_helpers/dn/processor.rb +55 -0
- data/app/lib/has_helpers/dn/refresh.rb +13 -0
- data/app/lib/has_helpers/dn/utils/backfill_utils.rb +80 -0
- data/app/lib/has_helpers/dn/utils/query_utils.rb +216 -0
- data/app/lib/has_helpers/feature.rb +88 -0
- data/app/lib/has_helpers/hq_exception.rb +18 -0
- data/app/lib/has_helpers/index.rb +890 -0
- data/app/lib/has_helpers/organization_merge.rb +128 -0
- data/app/lib/has_helpers/query/elasticsearch.rb +20 -0
- data/app/lib/has_helpers/query/provider.rb +105 -0
- data/app/lib/has_helpers/query/sql.rb +133 -0
- data/app/lib/has_helpers/query.rb +14 -0
- data/app/lib/has_helpers/search/elasticsearch.rb +41 -0
- data/app/lib/has_helpers/search/elasticsearch_shared.rb +70 -0
- data/app/lib/has_helpers/search/provider.rb +149 -0
- data/app/lib/has_helpers/search/search_utils.rb +74 -0
- data/app/lib/has_helpers/search/sql_search.rb +219 -0
- data/app/lib/has_helpers/search.rb +46 -0
- data/app/models/ability.rb +233 -0
- data/app/models/has_helpers/application.rb +47 -0
- data/app/models/has_helpers/application_record.rb +7 -0
- data/app/models/has_helpers/cards/card_builder/base.rb +74 -0
- data/app/models/has_helpers/cards/card_builder/card_definition_builder.rb +486 -0
- data/app/models/has_helpers/cards/card_builder/definition_context.rb +82 -0
- data/app/models/has_helpers/cards/card_builder/field_builder.rb +142 -0
- data/app/models/has_helpers/cards/card_builder/resource_builder.rb +34 -0
- data/app/models/has_helpers/cards/card_definition.rb +41 -0
- data/app/models/has_helpers/cards/card_definition_resource.rb +19 -0
- data/app/models/has_helpers/cards/card_field.rb +39 -0
- data/app/models/has_helpers/cards/registry.rb +115 -0
- data/app/models/has_helpers/cards/role_card_field.rb +28 -0
- data/app/models/has_helpers/cards/role_resource_card.rb +41 -0
- data/app/models/has_helpers/dn_utils.rb +61 -0
- data/app/models/has_helpers/favorite.rb +20 -0
- data/app/models/has_helpers/master_record.rb +18 -0
- data/app/models/has_helpers/notification_alert.rb +52 -0
- data/app/models/has_helpers/organization.rb +16 -0
- data/app/models/has_helpers/organization_link.rb +92 -0
- data/app/models/has_helpers/organization_methods.rb +62 -0
- data/app/models/has_helpers/organization_option.rb +31 -0
- data/app/models/has_helpers/organization_resource.rb +45 -0
- data/app/models/has_helpers/pending_db_operation.rb +31 -0
- data/app/models/has_helpers/recently_viewed.rb +34 -0
- data/app/models/has_helpers/reindex_queue.rb +43 -0
- data/app/models/has_helpers/resource.rb +38 -0
- data/app/models/has_helpers/resource_group.rb +15 -0
- data/app/models/has_helpers/restriction.rb +15 -0
- data/app/models/has_helpers/restriction_operation.rb +13 -0
- data/app/models/has_helpers/role.rb +20 -0
- data/app/models/has_helpers/service_trust/audit_log.rb +20 -0
- data/app/models/has_helpers/service_trust/key.rb +56 -0
- data/app/models/has_helpers/stat.rb +13 -0
- data/app/models/has_helpers/user.rb +18 -0
- data/app/models/has_helpers/user_methods.rb +184 -0
- data/app/models/has_helpers/user_organization.rb +33 -0
- data/app/models/has_helpers/user_role.rb +27 -0
- data/app/models/has_helpers/user_session.rb +19 -0
- data/app/presenters/has_helpers/barcode_base_presenter.rb +113 -0
- data/app/presenters/has_helpers/base_presenter.rb +100 -0
- data/app/presenters/has_helpers/cacheable_item.rb +28 -0
- data/app/presenters/has_helpers/item_presenter.rb +52 -0
- data/app/presenters/has_helpers/items_presenter.rb +33 -0
- data/app/presenters/has_helpers/resource/action_presenter.rb +8 -0
- data/app/presenters/has_helpers/resource/cell_presenter.rb +52 -0
- data/app/presenters/has_helpers/resource/email_presenter.rb +34 -0
- data/app/presenters/has_helpers/resource/form_presenter.rb +76 -0
- data/app/presenters/has_helpers/resource/info_collection_presenter.rb +33 -0
- data/app/presenters/has_helpers/resource/info_presenter.rb +34 -0
- data/app/presenters/has_helpers/resource/list_presenter.rb +120 -0
- data/app/presenters/has_helpers/resource/note_presenter.rb +14 -0
- data/app/presenters/has_helpers/resource/row_presenter.rb +25 -0
- data/app/presenters/has_helpers/resource/tag_presenter.rb +31 -0
- data/app/presenters/has_helpers/search_presenter.rb +230 -0
- data/app/triggers/has_helpers/erase_denormalized_belongs_to_column_trigger.rb +85 -0
- data/app/triggers/has_helpers/limit_favorites.rb +97 -0
- data/app/utils/has_helpers/redis_configurable.rb +79 -0
- data/app/utils/has_helpers/sync_utils.rb +234 -0
- data/app/views/has_helpers/actions/_action.html.erb +31 -0
- data/app/views/has_helpers/alerts/_alert.html.erb +3 -0
- data/app/views/has_helpers/barcodes.html.erb +36 -0
- data/app/views/has_helpers/containers/_container.html.erb +3 -0
- data/app/views/has_helpers/email_notifier/system.html.erb +8 -0
- data/app/views/has_helpers/email_notifier/system.text.erb +7 -0
- data/app/views/has_helpers/enumerables/_enumerable.html.erb +1 -0
- data/app/views/has_helpers/icons/_icon.html.erb +1 -0
- data/app/views/has_helpers/render_objects/_render_object.html.erb +1 -0
- data/app/views/has_helpers/services/_service.html.erb +1 -0
- data/app/views/has_helpers/text_nodes/_text_node.html.erb +4 -0
- data/app/views/has_helpers/ui_attributes/_html_attrs.html.erb +1 -0
- data/app/workers/elasticsearch_process_queue.rb +11 -0
- data/app/workers/elasticsearch_reindex.rb +11 -0
- data/app/workers/elasticsearch_reindex_scheduler.rb +35 -0
- data/app/workers/good_job/configurable_backfill.rb +64 -0
- data/app/workers/good_job/cron_denormalization_stats_manager.rb +39 -0
- data/app/workers/good_job/denormalization_stats_worker.rb +57 -0
- data/app/workers/good_job/dn_update_worker.rb +30 -0
- data/app/workers/good_job/dn_worker.rb +47 -0
- data/app/workers/good_job/elasticsearch_old_index_cleaner_worker.rb +113 -0
- data/app/workers/good_job/elasticsearch_on_finish_reindex.rb +26 -0
- data/app/workers/good_job/elasticsearch_process_batch.rb +78 -0
- data/app/workers/good_job/elasticsearch_process_change_logs_master_worker.rb +27 -0
- data/app/workers/good_job/elasticsearch_process_change_logs_worker.rb +155 -0
- data/app/workers/good_job/elasticsearch_process_queue.rb +50 -0
- data/app/workers/good_job/elasticsearch_reindex.rb +31 -0
- data/app/workers/has_helpers/cards/orphan_card_sweeper.rb +28 -0
- data/app/workers/has_helpers/db_operations/clean_stats_worker.rb +42 -0
- data/app/workers/has_helpers/db_operations/helper_worker.rb +131 -0
- data/app/workers/has_helpers/db_operations/main_worker.rb +164 -0
- data/app/workers/has_helpers/db_operations/operations.rb +134 -0
- data/app/workers/has_helpers/db_operations/update_operations_stats_resume_worker.rb +35 -0
- data/app/workers/has_helpers/default_worker.rb +17 -0
- data/app/workers/has_helpers/good_job/notification_alert_cleanup_worker.rb +43 -0
- data/app/workers/has_helpers/service_trust/audit_log_cleanup_worker.rb +19 -0
- data/app/workers/sidekiq/elasticsearch_old_index_cleaner_worker.rb +112 -0
- data/app/workers/sidekiq/elasticsearch_process_batch.rb +87 -0
- data/app/workers/sidekiq/elasticsearch_process_queue.rb +40 -0
- data/app/workers/sidekiq/elasticsearch_reindex.rb +53 -0
- data/config/initializers/action_view.rb +7 -0
- data/config/initializers/inflections.rb +24 -0
- data/config/initializers/marginalia.rb +8 -0
- data/config/initializers/mime_types.rb +6 -0
- data/config/initializers/opentelemetry.rb +12 -0
- data/config/initializers/redis_conf.rb +40 -0
- data/config/initializers/searchkick.rb +9 -0
- data/config/initializers/will_paginate.rb +39 -0
- data/config/redis.yml +12 -0
- data/config/routes.rb +27 -0
- data/config/service_trust_routes.rb +5 -0
- data/config.ru +9 -0
- data/db/migrate/20230127194725_create_organization_table.rb +25 -0
- data/db/migrate/20230127194726_validate_organization_foreign_keys.rb +7 -0
- data/db/migrate/20230206141413_create_security_models.rb +42 -0
- data/db/migrate/20230206141513_create_restriction_model.rb +20 -0
- data/db/migrate/20230206142613_add_security_indexes.rb +14 -0
- data/db/migrate/20230206143513_security_model_constants_seed.rb +8 -0
- data/db/migrate/20230206161513_add_organization_to_security_models.rb +13 -0
- data/db/migrate/20230209121019_create_search_group_models.rb +36 -0
- data/db/migrate/20230209122919_add_search_group_indexes.rb +21 -0
- data/db/migrate/20230209181832_add_search_group_resources_type_constraint_trigger.rb +61 -0
- data/db/migrate/20230313220337_create_table_user_sessions.rb +31 -0
- data/db/migrate/20230517141106_update_resource_types.rb +12 -0
- data/db/migrate/20230518194611_add_resources_contraints.rb +17 -0
- data/db/migrate/20230518194642_create_nested_resource_model.rb +21 -0
- data/db/migrate/20230518194645_validate_security_models_constraints.rb +23 -0
- data/db/migrate/20230608125037_add_inactive_field_to_user_sessions.rb +7 -0
- data/db/migrate/20230919191525_add_parent_id_to_resources.rb +8 -0
- data/db/migrate/20230919191535_validate_parent_id_fk_resources.rb +7 -0
- data/db/migrate/20230919210456_drop_nested_resources.rb +7 -0
- data/db/migrate/20231222135620_add_user_role.rb +19 -0
- data/db/migrate/20240108172021_create_favorites_table.rb +20 -0
- data/db/migrate/20240108190209_create_organization_link_table_if_doesnt_exists.rb +31 -0
- data/db/migrate/20240109211445_validate_fks_org_links.rb +9 -0
- data/db/migrate/20240110135440_create_recently_vieweds.rb +20 -0
- data/db/migrate/20240112185312_link_type_options_seed.rb +7 -0
- data/db/migrate/20240116173201_organization_link_resource_tab_constraints.rb +19 -0
- data/db/migrate/20240131160949_add_user_organizations_table.rb +32 -0
- data/db/migrate/20240212132519_add_organization_in_user_sessions.rb +13 -0
- data/db/migrate/20240227135052_add_user_organization_composite_fks.rb +32 -0
- data/db/migrate/20240312193501_update_organization_links.rb +11 -0
- data/db/migrate/20240426185902_add_user_organizations_current_organization_column.rb +12 -0
- data/db/migrate/20240429214601_add_primary_trigger.rb +10 -0
- data/db/migrate/20241112131819_add_json_file_format_seed.rb +7 -0
- data/db/migrate/20250520133417_create_restriction_operations.rb +18 -0
- data/db/migrate/20250520133420_validate_create_restriction_operations.rb +8 -0
- data/db/migrate/20250520140642_add_indexes_to_restrictions.rb +9 -0
- data/db/migrate/20250529130741_create_notification_alerts_table.rb +28 -0
- data/db/migrate/20250530223332_create_data_migrations_table.rb +19 -0
- data/db/migrate/20250605121622_seed_notification_alert_status.rb +7 -0
- data/db/migrate/20250605131439_seed_notification_alert_types.rb +7 -0
- data/db/migrate/20250606173128_update_notification_alert_length_constraint.rb +18 -0
- data/db/migrate/20250606174043_validate_notification_alert_length_constraints.rb +8 -0
- data/db/migrate/20250624183000_add_data_import_alert_type.rb +11 -0
- data/db/migrate/20250722183935_create_organization_options_table.rb +38 -0
- data/db/migrate/20250722192724_validate_organization_options_fks_and_constraints.rb +32 -0
- data/db/migrate/20250722192936_add_indexes_to_organization_options_table.rb +15 -0
- data/db/migrate/20250915132448_add_column_restrictions_hash.rb +7 -0
- data/db/migrate/20250917143211_add_column_permissions_hash.rb +7 -0
- data/db/migrate/20251001145950_add_trust_this_device.rb +7 -0
- data/db/migrate/20251021130835_add_backend_urls_to_applications_table.rb +13 -0
- data/db/migrate/20251021172715_validate_backend_urls_constraints_in_applications.rb +9 -0
- data/db/migrate/20251030200020_create_card_definitions.rb +23 -0
- data/db/migrate/20251030201012_create_card_definition_resources.rb +24 -0
- data/db/migrate/20251030201236_create_card_fields.rb +34 -0
- data/db/migrate/20251103194317_create_role_resource_cards.rb +37 -0
- data/db/migrate/20251103194426_create_role_card_fields.rb +34 -0
- data/db/migrate/20251107173218_validate_columns_from_cards.rb +17 -0
- data/db/migrate/20251111173212_add_column_application_array.rb +7 -0
- data/db/migrate/20251112203326_add_card_types.rb +10 -0
- data/db/migrate/20251115120000_add_virtual_cards_support.rb +28 -0
- data/db/migrate/20251202231256_add_org_scoped_fk_role_resource_cards_to_roles.rb +14 -0
- data/db/migrate/20260105134504_add_accessor_field_resource_type.rb +7 -0
- data/db/migrate/20260115123000_add_default_visibility_to_card_definitions.rb +9 -0
- data/db/migrate/20260120175037_is_new_admin.rb +9 -0
- data/db/migrate/20260122392008_add_stats_table.rb +17 -0
- data/db/migrate/20260414204718_add_index_to_stats_table.rb +10 -0
- data/db/migrate/20260509120000_create_service_trust_keys.rb +25 -0
- data/db/migrate/20260509120001_create_service_trust_audit_logs.rb +26 -0
- data/db/migrate/20260511120000_add_direction_to_service_trust_audit_logs.rb +17 -0
- data/db/migrate/20260511121000_remove_role_fk_from_role_resource_cards.rb +19 -0
- data/db/migrate/20260511121001_add_disabled_at_to_role_resource_cards.rb +10 -0
- data/db/migrate/20260605120100_seed_hover_card_options.rb +8 -0
- data/db/migrate/20260608120000_seed_badge_list_card_data_type.rb +7 -0
- data/db/migrate/20260727000000_add_two_factor_trust_id_to_user_sessions.rb +11 -0
- data/db/seeds.rb +14 -0
- data/docs/Elasticsearch.md +23 -0
- data/docs/Search_API.md +126 -0
- data/docs/cards_role_proxy.md +107 -0
- data/docs/factory_api.md +135 -0
- data/has-helpers-pr-289-pipeline.yaml +16 -0
- data/has-helpers-pr-298-pipeline.yaml +15 -0
- data/has_helpers.gemspec +68 -0
- data/lib/active_record/migration/active_migration_patch.rb +123 -0
- data/lib/generators/has_helpers/install/USAGE +8 -0
- data/lib/generators/has_helpers/install/install_generator.rb +22 -0
- data/lib/generators/has_helpers/install/templates/migrate/has_helpers_initial_setup.rb +26 -0
- data/lib/has_helpers/active_record/base.rb +50 -0
- data/lib/has_helpers/active_record/date_range.rb +228 -0
- data/lib/has_helpers/active_record/nested_resource.rb +58 -0
- data/lib/has_helpers/active_record/validation.rb +63 -0
- data/lib/has_helpers/active_record.rb +520 -0
- data/lib/has_helpers/app/command.rb +107 -0
- data/lib/has_helpers/app.rb +10 -0
- data/lib/has_helpers/assets_precompile_helper.rb +20 -0
- data/lib/has_helpers/attributes.rb +196 -0
- data/lib/has_helpers/aws.rb +67 -0
- data/lib/has_helpers/cards/cache.rb +49 -0
- data/lib/has_helpers/cards/config.rb +42 -0
- data/lib/has_helpers/cards/remote_role.rb +27 -0
- data/lib/has_helpers/cards/remote_roles.rb +75 -0
- data/lib/has_helpers/cards/roles.rb +55 -0
- data/lib/has_helpers/cards.rb +34 -0
- data/lib/has_helpers/concerns/hydrate_concern.rb +76 -0
- data/lib/has_helpers/concerns/importable_accessors.rb +25 -0
- data/lib/has_helpers/concerns/restrictions_parseable.rb +51 -0
- data/lib/has_helpers/config.rb +11 -0
- data/lib/has_helpers/constant.rb +578 -0
- data/lib/has_helpers/constraints.rb +10 -0
- data/lib/has_helpers/core_ext/array.rb +28 -0
- data/lib/has_helpers/core_ext/date/infinity.rb +25 -0
- data/lib/has_helpers/core_ext/date.rb +39 -0
- data/lib/has_helpers/core_ext/enumerable.rb +20 -0
- data/lib/has_helpers/core_ext/false_class.rb +14 -0
- data/lib/has_helpers/core_ext/float.rb +18 -0
- data/lib/has_helpers/core_ext/hash.rb +71 -0
- data/lib/has_helpers/core_ext/integer.rb +18 -0
- data/lib/has_helpers/core_ext/nil_class.rb +30 -0
- data/lib/has_helpers/core_ext/object.rb +60 -0
- data/lib/has_helpers/core_ext/proc.rb +16 -0
- data/lib/has_helpers/core_ext/string.rb +81 -0
- data/lib/has_helpers/core_ext/symbol.rb +17 -0
- data/lib/has_helpers/core_ext/true_class.rb +14 -0
- data/lib/has_helpers/cursor.rb +46 -0
- data/lib/has_helpers/data_migration/base.rb +293 -0
- data/lib/has_helpers/data_migration/large_worker.rb +20 -0
- data/lib/has_helpers/data_migration/master_worker.rb +33 -0
- data/lib/has_helpers/data_migration/migrator.rb +83 -0
- data/lib/has_helpers/data_migration/model.rb +18 -0
- data/lib/has_helpers/data_migration/small_worker.rb +20 -0
- data/lib/has_helpers/data_migration.rb +8 -0
- data/lib/has_helpers/engine.rb +47 -0
- data/lib/has_helpers/extensions.rb +17 -0
- data/lib/has_helpers/factory_api.rb +47 -0
- data/lib/has_helpers/factory_scenario/definition.rb +33 -0
- data/lib/has_helpers/factory_scenario/dsl.rb +80 -0
- data/lib/has_helpers/factory_scenario/factory_bot_override.rb +53 -0
- data/lib/has_helpers/factory_scenario/registry.rb +31 -0
- data/lib/has_helpers/factory_scenario/runner.rb +285 -0
- data/lib/has_helpers/factory_scenario.rb +27 -0
- data/lib/has_helpers/fixed_expiration_time_cache_session_store.rb +20 -0
- data/lib/has_helpers/flattener/association.rb +140 -0
- data/lib/has_helpers/flattener/attribute.rb +42 -0
- data/lib/has_helpers/flattener/base.rb +428 -0
- data/lib/has_helpers/flattener/column/types.rb +71 -0
- data/lib/has_helpers/flattener/column.rb +97 -0
- data/lib/has_helpers/flattener/pivot.rb +49 -0
- data/lib/has_helpers/flattener/reset.rb +33 -0
- data/lib/has_helpers/flattener/resource.rb +114 -0
- data/lib/has_helpers/flattener/resource_generator.rb +21 -0
- data/lib/has_helpers/flattener/utils.rb +14 -0
- data/lib/has_helpers/flattener.rb +53 -0
- data/lib/has_helpers/hash_accessors.rb +62 -0
- data/lib/has_helpers/helpers/application.rb +291 -0
- data/lib/has_helpers/index/config.rb +15 -0
- data/lib/has_helpers/lazy_config.rb +61 -0
- data/lib/has_helpers/resource_importer.rb +260 -0
- data/lib/has_helpers/resources.rb +127 -0
- data/lib/has_helpers/rspec/constants.rb +61 -0
- data/lib/has_helpers/rspec/factories.rb +11 -0
- data/lib/has_helpers/rspec.rb +22 -0
- data/lib/has_helpers/sentry/graphql_trace.rb +37 -0
- data/lib/has_helpers/serializer.rb +41 -0
- data/lib/has_helpers/service_trust/audit.rb +45 -0
- data/lib/has_helpers/service_trust/authorization.rb +34 -0
- data/lib/has_helpers/service_trust/bearer_token.rb +23 -0
- data/lib/has_helpers/service_trust/capability.rb +32 -0
- data/lib/has_helpers/service_trust/client/response.rb +13 -0
- data/lib/has_helpers/service_trust/client/retry.rb +35 -0
- data/lib/has_helpers/service_trust/client.rb +128 -0
- data/lib/has_helpers/service_trust/configuration.rb +76 -0
- data/lib/has_helpers/service_trust/engine.rb +10 -0
- data/lib/has_helpers/service_trust/errors.rb +32 -0
- data/lib/has_helpers/service_trust/graphql/authorizable.rb +72 -0
- data/lib/has_helpers/service_trust/graphql_client.rb +54 -0
- data/lib/has_helpers/service_trust/jwks_client.rb +133 -0
- data/lib/has_helpers/service_trust/jwt_codec.rb +45 -0
- data/lib/has_helpers/service_trust/key_manager.rb +160 -0
- data/lib/has_helpers/service_trust/rate_limiter.rb +27 -0
- data/lib/has_helpers/service_trust/replay_cache.rb +26 -0
- data/lib/has_helpers/service_trust/signer.rb +47 -0
- data/lib/has_helpers/service_trust/test.rb +60 -0
- data/lib/has_helpers/service_trust/verifier.rb +85 -0
- data/lib/has_helpers/service_trust/verify_middleware.rb +55 -0
- data/lib/has_helpers/service_trust.rb +51 -0
- data/lib/has_helpers/test_helpers/factory_data/unit_addresses.rb +42 -0
- data/lib/has_helpers/test_helpers/factory_data/unit_advisors.rb +272 -0
- data/lib/has_helpers/test_helpers/factory_data/unit_emails.rb +37 -0
- data/lib/has_helpers/test_helpers/factory_data/unit_organizations.rb +48 -0
- data/lib/has_helpers/test_helpers/factory_data/unit_people.rb +34 -0
- data/lib/has_helpers/test_helpers/factory_data/unit_phones.rb +41 -0
- data/lib/has_helpers/test_helpers/factory_data/unit_states.rb +26 -0
- data/lib/has_helpers/test_helpers/factory_data/unit_webs.rb +31 -0
- data/lib/has_helpers/test_helpers/shoulda_helpers.rb +249 -0
- data/lib/has_helpers/ui_attributes.rb +39 -0
- data/lib/has_helpers/utils/action.rb +87 -0
- data/lib/has_helpers/utils/active_record_utils.rb +40 -0
- data/lib/has_helpers/utils/alert.rb +42 -0
- data/lib/has_helpers/utils/container.rb +46 -0
- data/lib/has_helpers/utils/dataset.rb +34 -0
- data/lib/has_helpers/utils/email_notifier.rb +15 -0
- data/lib/has_helpers/utils/icon.rb +66 -0
- data/lib/has_helpers/utils/input.rb +45 -0
- data/lib/has_helpers/utils/log_notifier.rb +79 -0
- data/lib/has_helpers/utils/render_object.rb +32 -0
- data/lib/has_helpers/utils/s3_utils.rb +37 -0
- data/lib/has_helpers/utils/service.rb +87 -0
- data/lib/has_helpers/utils/slack_conf.rb +31 -0
- data/lib/has_helpers/utils/slack_notifier.rb +42 -0
- data/lib/has_helpers/utils/text_node.rb +34 -0
- data/lib/has_helpers/utils/user_restrictions.rb +266 -0
- data/lib/has_helpers/utils.rb +19 -0
- data/lib/has_helpers/version.rb +6 -0
- data/lib/has_helpers/web_socket_publisher.rb +20 -0
- data/lib/has_helpers/webhook.rb +17 -0
- data/lib/has_helpers.rb +63 -0
- data/lib/hq/resource.rb +55 -0
- data/lib/hq/structure/inserts.rb +176 -0
- data/lib/hq/structure.rb +115 -0
- data/lib/tasks/has_helpers/service_trust.rake +39 -0
- data/script/rails +10 -0
- data/sorbet/config +5 -0
- data/sorbet/rbi/gems/actionpack.rbi +1361 -0
- data/sorbet/rbi/gems/activemodel.rbi +755 -0
- data/sorbet/rbi/gems/activerecord.rbi +4713 -0
- data/sorbet/rbi/gems/activesupport.rbi +1970 -0
- data/sorbet/rbi/gems/barby.rbi +129 -0
- data/sorbet/rbi/gems/better_errors.rbi +155 -0
- data/sorbet/rbi/gems/binding_of_caller.rbi +22 -0
- data/sorbet/rbi/gems/byebug.rbi +19 -0
- data/sorbet/rbi/gems/chronic.rbi +480 -0
- data/sorbet/rbi/gems/chunky_png.rbi +510 -0
- data/sorbet/rbi/gems/climate_control.rbi +45 -0
- data/sorbet/rbi/gems/coderay.rbi +91 -0
- data/sorbet/rbi/gems/concurrent-ruby.rbi +1587 -0
- data/sorbet/rbi/gems/debug_inspector.rbi +18 -0
- data/sorbet/rbi/gems/erubi.rbi +26 -0
- data/sorbet/rbi/gems/holidays.rbi +362 -0
- data/sorbet/rbi/gems/i18n.rbi +132 -0
- data/sorbet/rbi/gems/method_source.rbi +63 -0
- data/sorbet/rbi/gems/mime-types-data.rbi +16 -0
- data/sorbet/rbi/gems/mime-types.rbi +217 -0
- data/sorbet/rbi/gems/mimemagic.rbi +36 -0
- data/sorbet/rbi/gems/minitest.rbi +282 -0
- data/sorbet/rbi/gems/mustache.rbi +151 -0
- data/sorbet/rbi/gems/paperclip.rbi +644 -0
- data/sorbet/rbi/gems/parallel.rbi +81 -0
- data/sorbet/rbi/gems/parallel_tests.rbi +27 -0
- data/sorbet/rbi/gems/pry-rails.rbi +71 -0
- data/sorbet/rbi/gems/pry.rbi +1964 -0
- data/sorbet/rbi/gems/rack.rbi +228 -0
- data/sorbet/rbi/gems/railties.rbi +364 -0
- data/sorbet/rbi/gems/rspec-core.rbi +1704 -0
- data/sorbet/rbi/gems/rspec-expectations.rbi +389 -0
- data/sorbet/rbi/gems/rspec-support.rbi +206 -0
- data/sorbet/rbi/gems/rspec.rbi +14 -0
- data/sorbet/rbi/gems/spreadsheet_architect.rbi +10 -0
- data/sorbet/rbi/gems/sprockets.rbi +659 -0
- data/sorbet/rbi/gems/terrapin.rbi +115 -0
- data/sorbet/rbi/gems/testhq.rbi +14 -0
- data/sorbet/rbi/gems/thor.rbi +482 -0
- data/sorbet/rbi/gems/thread_safe.rbi +81 -0
- data/sorbet/rbi/gems/tzinfo.rbi +407 -0
- data/sorbet/rbi/gems/zeitwerk.rbi +131 -0
- data/sorbet/rbi/hidden-definitions/errors.txt +35111 -0
- data/sorbet/rbi/hidden-definitions/hidden.rbi +59974 -0
- data/sorbet/rbi/sorbet-typed/lib/actionmailer/all/actionmailer.rbi +13 -0
- data/sorbet/rbi/sorbet-typed/lib/actionpack/all/actionpack.rbi +855 -0
- data/sorbet/rbi/sorbet-typed/lib/actionview/all/actionview.rbi +314 -0
- data/sorbet/rbi/sorbet-typed/lib/activemodel/all/activemodel.rbi +423 -0
- data/sorbet/rbi/sorbet-typed/lib/activerecord/>=5.2/activerecord.rbi +17 -0
- data/sorbet/rbi/sorbet-typed/lib/activerecord/all/activerecord.rbi +1415 -0
- data/sorbet/rbi/sorbet-typed/lib/activerecord/~>6.0.0/activerecord.rbi +475 -0
- data/sorbet/rbi/sorbet-typed/lib/activestorage/>=6.0.0/activestorage.rbi +19 -0
- data/sorbet/rbi/sorbet-typed/lib/activestorage/all/activestorage.rbi +173 -0
- data/sorbet/rbi/sorbet-typed/lib/activesupport/>=6.0.0.rc1/activesupport.rbi +23 -0
- data/sorbet/rbi/sorbet-typed/lib/activesupport/all/activesupport.rbi +625 -0
- data/sorbet/rbi/sorbet-typed/lib/bundler/all/bundler.rbi +8684 -0
- data/sorbet/rbi/sorbet-typed/lib/minitest/all/minitest.rbi +99 -0
- data/sorbet/rbi/sorbet-typed/lib/railties/all/railties.rbi +25 -0
- data/sorbet/rbi/sorbet-typed/lib/rainbow/all/rainbow.rbi +254 -0
- data/sorbet/rbi/sorbet-typed/lib/ruby/all/gem.rbi +4222 -0
- data/sorbet/rbi/sorbet-typed/lib/ruby/all/open3.rbi +111 -0
- data/sorbet/rbi/sorbet-typed/lib/ruby/all/resolv.rbi +543 -0
- data/sorbet/rbi/todo.rbi +244 -0
- data/spec/commands/cards/available_cards_command_spec.rb +90 -0
- data/spec/commands/cards/card_data_command_spec.rb +1048 -0
- data/spec/constructors/has_helpers/base_constructor_spec.rb +49 -0
- data/spec/controllers/has_helpers/controller_spec.rb +71 -0
- data/spec/controllers/has_helpers/xlsx_export_spec.rb +96 -0
- data/spec/factories/advisors.rb +20 -0
- data/spec/factories/anonymous_cowards.rb +5 -0
- data/spec/factories/applications.rb +5 -0
- data/spec/factories/appointments.rb +6 -0
- data/spec/factories/attachment.rb +15 -0
- data/spec/factories/audits.rb +5 -0
- data/spec/factories/basic_schedules.rb +12 -0
- data/spec/factories/cards/card_definition_resources.rb +7 -0
- data/spec/factories/cards/card_definitions.rb +11 -0
- data/spec/factories/cards/card_fields.rb +22 -0
- data/spec/factories/cards/role_card_fields.rb +17 -0
- data/spec/factories/cards/role_resource_cards.rb +12 -0
- data/spec/factories/constant_definition.rb +7 -0
- data/spec/factories/contracts.rb +10 -0
- data/spec/factories/data_migration_models.rb +9 -0
- data/spec/factories/demographics.rb +22 -0
- data/spec/factories/events.rb +21 -0
- data/spec/factories/firms.rb +21 -0
- data/spec/factories/imports.rb +9 -0
- data/spec/factories/meetups.rb +13 -0
- data/spec/factories/organizations.rb +13 -0
- data/spec/factories/policies.rb +7 -0
- data/spec/factories/policy_notes.rb +9 -0
- data/spec/factories/read_only_values.rb +7 -0
- data/spec/factories/resources.rb +7 -0
- data/spec/factories/roles.rb +11 -0
- data/spec/factories/schedules.rb +13 -0
- data/spec/factories/shared/flattener/attributes.rb +10 -0
- data/spec/factories/shared/flattener/columns.rb +6 -0
- data/spec/factories/shared/flattener/resources.rb +5 -0
- data/spec/factories/templates.rb +12 -0
- data/spec/factories/user_organizations.rb +23 -0
- data/spec/factories/user_session.rb +11 -0
- data/spec/factories/users.rb +55 -0
- data/spec/fixtures/sample_config.yml +3 -0
- data/spec/graphql/has_helpers/mutations/external_data_sync_spec.rb +101 -0
- data/spec/graphql/has_helpers/resolvers/cards/available_cards_spec.rb +36 -0
- data/spec/graphql/has_helpers/resolvers/cards/card_data_spec.rb +42 -0
- data/spec/graphql/has_helpers/resolvers/cards/hover_card_data_spec.rb +59 -0
- data/spec/has_helpers/cards/cache_spec.rb +58 -0
- data/spec/has_helpers/cards/config_spec.rb +68 -0
- data/spec/has_helpers/cards/orphan_card_sweeper_spec.rb +97 -0
- data/spec/has_helpers/cards/remote_role_spec.rb +17 -0
- data/spec/has_helpers/cards/remote_roles_spec.rb +97 -0
- data/spec/has_helpers/cards/roles_spec.rb +52 -0
- data/spec/has_helpers/cards/smoke_spec.rb +168 -0
- data/spec/has_helpers/cards/support.rb +52 -0
- data/spec/has_helpers/resolvers/internal/roles_spec.rb +73 -0
- data/spec/has_helpers/service_trust/audit_log_cleanup_worker_spec.rb +33 -0
- data/spec/has_helpers/service_trust/audit_spec.rb +70 -0
- data/spec/has_helpers/service_trust/authenticated_as_service_spec.rb +65 -0
- data/spec/has_helpers/service_trust/authorization_spec.rb +37 -0
- data/spec/has_helpers/service_trust/capability_spec.rb +35 -0
- data/spec/has_helpers/service_trust/client_spec.rb +129 -0
- data/spec/has_helpers/service_trust/configuration_spec.rb +35 -0
- data/spec/has_helpers/service_trust/engine_spec.rb +13 -0
- data/spec/has_helpers/service_trust/errors_spec.rb +23 -0
- data/spec/has_helpers/service_trust/graphql_authentication_spec.rb +59 -0
- data/spec/has_helpers/service_trust/graphql_authorizable_spec.rb +91 -0
- data/spec/has_helpers/service_trust/graphql_client_spec.rb +47 -0
- data/spec/has_helpers/service_trust/jwks_client_spec.rb +85 -0
- data/spec/has_helpers/service_trust/jwks_controller_spec.rb +47 -0
- data/spec/has_helpers/service_trust/jwt_codec_spec.rb +30 -0
- data/spec/has_helpers/service_trust/key_manager_spec.rb +80 -0
- data/spec/has_helpers/service_trust/key_spec.rb +52 -0
- data/spec/has_helpers/service_trust/rake_tasks_spec.rb +58 -0
- data/spec/has_helpers/service_trust/rate_limiter_spec.rb +31 -0
- data/spec/has_helpers/service_trust/replay_cache_spec.rb +29 -0
- data/spec/has_helpers/service_trust/signer_verifier_spec.rb +159 -0
- data/spec/has_helpers/service_trust/smoke_spec.rb +216 -0
- data/spec/has_helpers/service_trust/support.rb +121 -0
- data/spec/has_helpers/service_trust/test_module_spec.rb +39 -0
- data/spec/has_helpers/service_trust/verify_middleware_spec.rb +82 -0
- data/spec/helpers/application_spec.rb +155 -0
- data/spec/importers/cards/importer_spec.rb +861 -0
- data/spec/internal/app/assets/config/manifest.js +0 -0
- data/spec/internal/app/constants/cat_egory.rb +33 -0
- data/spec/internal/app/constants/color.rb +19 -0
- data/spec/internal/app/constants/exclude_group_nil_constant.rb +5 -0
- data/spec/internal/app/constants/foo.rb +6 -0
- data/spec/internal/app/constants/fruit.rb +3 -0
- data/spec/internal/app/constants/generic_group_example.rb +12 -0
- data/spec/internal/app/constants/my_constant.rb +4 -0
- data/spec/internal/app/constants/product.rb +3 -0
- data/spec/internal/app/constants/shirt_size.rb +18 -0
- data/spec/internal/app/constants/simple_option.rb +14 -0
- data/spec/internal/app/constants/status.rb +5 -0
- data/spec/internal/app/constructors/organization_constructor.rb +4 -0
- data/spec/internal/app/controllers/application_controller.rb +27 -0
- data/spec/internal/app/controllers/search_extensions.rb +3 -0
- data/spec/internal/app/indices/advisor_index.rb +35 -0
- data/spec/internal/app/indices/contract_index.rb +52 -0
- data/spec/internal/app/indices/firm_index.rb +16 -0
- data/spec/internal/app/indices/policy_index.rb +20 -0
- data/spec/internal/app/models/advisor.rb +24 -0
- data/spec/internal/app/models/agency.rb +3 -0
- data/spec/internal/app/models/anonymous_coward.rb +5 -0
- data/spec/internal/app/models/another_event.rb +2 -0
- data/spec/internal/app/models/appointment.rb +3 -0
- data/spec/internal/app/models/attachment.rb +10 -0
- data/spec/internal/app/models/basic_schedule.rb +6 -0
- data/spec/internal/app/models/beneficiary.rb +6 -0
- data/spec/internal/app/models/client.rb +5 -0
- data/spec/internal/app/models/contract.rb +9 -0
- data/spec/internal/app/models/demographic.rb +4 -0
- data/spec/internal/app/models/division.rb +3 -0
- data/spec/internal/app/models/event.rb +10 -0
- data/spec/internal/app/models/event_template.rb +1 -0
- data/spec/internal/app/models/firm.rb +18 -0
- data/spec/internal/app/models/has_audits/audit.rb +15 -0
- data/spec/internal/app/models/import.rb +7 -0
- data/spec/internal/app/models/meetup.rb +8 -0
- data/spec/internal/app/models/organization.rb +17 -0
- data/spec/internal/app/models/organization_event.rb +5 -0
- data/spec/internal/app/models/policy.rb +16 -0
- data/spec/internal/app/models/policy_holder.rb +7 -0
- data/spec/internal/app/models/policy_note.rb +8 -0
- data/spec/internal/app/models/project.rb +3 -0
- data/spec/internal/app/models/read_only_value.rb +8 -0
- data/spec/internal/app/models/schedule.rb +12 -0
- data/spec/internal/app/models/template.rb +9 -0
- data/spec/internal/app/models/user.rb +31 -0
- data/spec/internal/app/models/user_organization.rb +17 -0
- data/spec/internal/app/views/has_lists/lists/_table.html.erb +1 -0
- data/spec/internal/config/database-standard.yml +24 -0
- data/spec/internal/config/initializers/resources.rb +19 -0
- data/spec/internal/config/routes.rb +19 -0
- data/spec/internal/config/storage.yml +3 -0
- data/spec/internal/db/good_job_schema.rb +141 -0
- data/spec/internal/db/schema.rb +334 -0
- data/spec/internal/log/.gitignore +1 -0
- data/spec/internal/public/favicon.ico +0 -0
- data/spec/lib/flattener/base_spec.rb +93 -0
- data/spec/lib/flattener/column/types_spec.rb +65 -0
- data/spec/lib/has_helpers/active_record/base_spec.rb +53 -0
- data/spec/lib/has_helpers/active_record/date_range_spec.rb +498 -0
- data/spec/lib/has_helpers/active_record/lookup_model_spec.rb +408 -0
- data/spec/lib/has_helpers/active_record/nested_resource_spec.rb +43 -0
- data/spec/lib/has_helpers/app/command/reports/generator_specs.rb +887 -0
- data/spec/lib/has_helpers/app/command_spec.rb +117 -0
- data/spec/lib/has_helpers/app/support/mocks/graphql_reports_helpers.rb +96 -0
- data/spec/lib/has_helpers/app/support/mocks/reports_api_mock.rb +47 -0
- data/spec/lib/has_helpers/attributes_spec.rb +220 -0
- data/spec/lib/has_helpers/aws_spec.rb +126 -0
- data/spec/lib/has_helpers/concerns/hydrate_concern_spec.rb +473 -0
- data/spec/lib/has_helpers/constant/definition_spec.rb +128 -0
- data/spec/lib/has_helpers/constant_spec.rb +432 -0
- data/spec/lib/has_helpers/core_ext/array_spec.rb +22 -0
- data/spec/lib/has_helpers/core_ext/date/infinity_spec.rb +67 -0
- data/spec/lib/has_helpers/core_ext/date_spec.rb +27 -0
- data/spec/lib/has_helpers/core_ext/false_class_spec.rb +7 -0
- data/spec/lib/has_helpers/core_ext/float_spec.rb +9 -0
- data/spec/lib/has_helpers/core_ext/hash_spec.rb +48 -0
- data/spec/lib/has_helpers/core_ext/integer_spec.rb +9 -0
- data/spec/lib/has_helpers/core_ext/nil_class_spec.rb +17 -0
- data/spec/lib/has_helpers/core_ext/object_spec.rb +54 -0
- data/spec/lib/has_helpers/core_ext/proc_spec.rb +24 -0
- data/spec/lib/has_helpers/core_ext/string_spec.rb +154 -0
- data/spec/lib/has_helpers/core_ext/true_class_spec.rb +7 -0
- data/spec/lib/has_helpers/data_migration/base_spec.rb +470 -0
- data/spec/lib/has_helpers/data_migration/large_worker_spec.rb +34 -0
- data/spec/lib/has_helpers/data_migration/master_worker_spec.rb +51 -0
- data/spec/lib/has_helpers/data_migration/migrator_spec.rb +113 -0
- data/spec/lib/has_helpers/data_migration/model_spec.rb +52 -0
- data/spec/lib/has_helpers/data_migration/small_worker_spec.rb +34 -0
- data/spec/lib/has_helpers/good_job/notification_alert_cleanup_worker_spec.rb +89 -0
- data/spec/lib/has_helpers/hash_accessors_spec.rb +66 -0
- data/spec/lib/has_helpers/index_spec.rb +442 -0
- data/spec/lib/has_helpers/lazy_config_spec.rb +31 -0
- data/spec/lib/has_helpers/organization_merge_spec.rb +88 -0
- data/spec/lib/has_helpers/resource_importer_spec.rb +119 -0
- data/spec/lib/has_helpers/rspec_spec.rb +16 -0
- data/spec/lib/has_helpers/search/provider_spec.rb +26 -0
- data/spec/lib/has_helpers/search/search_utils_spec.rb +134 -0
- data/spec/lib/has_helpers/sentry/graphql_trace_spec.rb +140 -0
- data/spec/lib/has_helpers/ui_attributes_spec.rb +42 -0
- data/spec/lib/has_helpers/utils/action_spec.rb +49 -0
- data/spec/lib/has_helpers/utils/alert_spec.rb +33 -0
- data/spec/lib/has_helpers/utils/dataset_spec.rb +10 -0
- data/spec/lib/has_helpers/utils/icon_spec.rb +23 -0
- data/spec/lib/has_helpers/utils/input_spec.rb +40 -0
- data/spec/lib/has_helpers/utils/log_notifier_spec.rb +25 -0
- data/spec/lib/has_helpers/utils/render_object_spec.rb +17 -0
- data/spec/lib/has_helpers/utils/s3_utils_spec.rb +78 -0
- data/spec/lib/has_helpers/utils/service_spec.rb +79 -0
- data/spec/lib/has_helpers/utils/slack_notifier_spec.rb +70 -0
- data/spec/lib/has_helpers/utils/user_restrictions_spec.rb +343 -0
- data/spec/lib/hq/structure/inserts_spec.rb +152 -0
- data/spec/lib/hq/structure_spec.rb +12 -0
- data/spec/models/cards/base_spec.rb +149 -0
- data/spec/models/cards/card_definition_resource_spec.rb +103 -0
- data/spec/models/cards/card_definition_spec.rb +50 -0
- data/spec/models/cards/card_field_spec.rb +46 -0
- data/spec/models/cards/field_builder_spec.rb +318 -0
- data/spec/models/cards/registry_spec.rb +147 -0
- data/spec/models/cards/role_card_field_spec.rb +111 -0
- data/spec/models/cards/role_resource_card_spec.rb +112 -0
- data/spec/models/has_helpers/master_record_spec.rb +20 -0
- data/spec/models/has_helpers/user_organization_spec.rb +28 -0
- data/spec/models/has_helpers/user_session_spec.rb +16 -0
- data/spec/presenters/base_presenter_spec.rb +166 -0
- data/spec/rails_helper.rb +119 -0
- data/spec/requests/has_helpers/factory_controller_spec.rb +159 -0
- data/spec/requests/has_helpers/health_check_controller_spec.rb +37 -0
- data/spec/scenarios/empty_setup.rb +6 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/support/has_helpers/database_helpers.rb +12 -0
- data/spec/utils/sync_utils_spec.rb +233 -0
- data/spec/workers/goodjob/elasticsearch_process_batch_spec.rb +94 -0
- data/spec/workers/goodjob/elasticsearch_process_change_logs_master_worker_spec.rb +40 -0
- data/spec/workers/goodjob/elasticsearch_process_change_logs_worker_spec.rb +82 -0
- data/spec/workers/goodjob/elasticsearch_process_queue_spec.rb +89 -0
- data/spec/workers/goodjob/elasticsearch_reindex_spec.rb +49 -0
- 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,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)
|