diff --git a/.dassie/Gemfile b/.dassie/Gemfile index c184f15fec..52bb72fb47 100644 --- a/.dassie/Gemfile +++ b/.dassie/Gemfile @@ -30,7 +30,7 @@ gem 'jbuilder', '~> 2.5' gem 'jquery-rails' gem 'pg', '~> 1.3' gem 'puma' -gem 'rails', '~> 6.1' +gem 'rails', '~> 7.2', '< 8.0' gem 'riiif', '~> 2.1' gem 'rsolr', '>= 1.0', '< 3' gem 'sass-rails', '~> 6.0' @@ -39,6 +39,7 @@ gem 'turbolinks', '~> 5' gem 'twitter-typeahead-rails', '0.11.1.pre.corejavascript' gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] gem 'uglifier', '>= 1.3.0' +gem 'activerecord-nulldb-adapter', git: 'https://github.com/taylorthurlow/nulldb', branch: 'fix/activerecord72-register-adapter' group :development do gem 'better_errors' # add command line in browser when errors @@ -46,7 +47,6 @@ group :development do # Access an interactive console on exception pages or by calling 'console' anywhere in the code. gem 'web-console', '>= 3.3.0' - gem 'listen', '>= 3.0.5', '< 3.2' # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring gem 'spring' gem 'spring-watcher-listen', '~> 2.0.0' @@ -57,4 +57,4 @@ group :development, :test do gem 'pry-doc' gem 'pry-rails' gem 'pry-rescue' -end \ No newline at end of file +end diff --git a/.dassie/config/environments/development.rb b/.dassie/config/environments/development.rb index 0649a79e15..7895155304 100644 --- a/.dassie/config/environments/development.rb +++ b/.dassie/config/environments/development.rb @@ -65,7 +65,7 @@ config.assets.prefix = '/dev-assets' # Raises error for missing translations - config.action_view.raise_on_missing_translations = true + config.i18n.raise_on_missing_translations = true # Use an evented file watcher to asynchronously detect changes in source code, # routes, locales, etc. This feature depends on the listen gem. diff --git a/.dassie/config/environments/production.rb b/.dassie/config/environments/production.rb index 729c6f5b55..2081dfcc04 100644 --- a/.dassie/config/environments/production.rb +++ b/.dassie/config/environments/production.rb @@ -23,8 +23,10 @@ config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? # Compress JavaScripts and CSS. - config.assets.js_compressor = Uglifier.new(harmony: true) - # config.assets.css_compressor = :sass + config.assets.configure do |env| + env.js_compressor = :uglifier + # env.css_compressor = :sass + end # Do not fallback to assets pipeline if a precompiled asset is missed. config.assets.compile = false diff --git a/.dassie/config/environments/test.rb b/.dassie/config/environments/test.rb index ee6f33e0cd..cd984de809 100644 --- a/.dassie/config/environments/test.rb +++ b/.dassie/config/environments/test.rb @@ -43,5 +43,5 @@ config.active_support.deprecation = :stderr # Raises error for missing translations - config.action_view.raise_on_missing_translations = true + config.i18n.raise_on_missing_translations = true end diff --git a/.dassie/config/initializers/riiif.rb b/.dassie/config/initializers/riiif.rb index b675e5afa0..47bafca4ab 100644 --- a/.dassie/config/initializers/riiif.rb +++ b/.dassie/config/initializers/riiif.rb @@ -35,29 +35,31 @@ module Hyrax # Adds file locking to Riiif::File # @see RiiifFileResolver - class RiiifFile < Riiif::File - include ActiveSupport::Benchmarkable - - attr_reader :id - def initialize(input_path, tempfile = nil, id:) - super(input_path, tempfile) - raise(ArgumentError, "must specify id") if id.blank? - @id = id - end + Rails.application.reloader.to_prepare do + class RiiifFile < Riiif::File + include ActiveSupport::Benchmarkable + + attr_reader :id + def initialize(input_path, tempfile = nil, id:) + super(input_path, tempfile) + raise(ArgumentError, "must specify id") if id.blank? + @id = id + end - # Wrap extract in a read lock and benchmark it - def extract(transformation, image_info = nil) - Riiif::Image.file_resolver.file_locks[id].with_read_lock do - benchmark "RiiifFile extracted #{path} with #{transformation.to_params}", level: :debug do - super + # Wrap extract in a read lock and benchmark it + def extract(transformation, image_info = nil) + Riiif::Image.file_resolver.file_locks[id].with_read_lock do + benchmark "RiiifFile extracted #{path} with #{transformation.to_params}", level: :debug do + super + end end end - end - private + private - def logger - Hyrax.logger + def logger + Hyrax.logger + end end end diff --git a/.dassie/db/schema.rb b/.dassie/db/schema.rb index 1515b687c5..dbbe3b38d2 100644 --- a/.dassie/db/schema.rb +++ b/.dassie/db/schema.rb @@ -10,8 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2024_05_06_070809) do - +ActiveRecord::Schema[7.2].define(version: 2024_05_06_070809) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" enable_extension "uuid-ossp" @@ -22,8 +21,8 @@ t.string "document_id" t.string "document_type" t.binary "title" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["document_id"], name: "index_bookmarks_on_document_id" t.index ["user_id"], name: "index_bookmarks_on_user_id" end @@ -34,8 +33,8 @@ t.string "checked_uri" t.string "expected_result" t.string "actual_result" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.boolean "passed" t.index ["checked_uri"], name: "index_checksum_audit_logs_on_checked_uri" t.index ["file_set_id", "file_id"], name: "by_file_set_id_and_file_id" @@ -49,8 +48,8 @@ t.string "target_url" t.integer "height" t.integer "width" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false end create_table "collection_type_participants", force: :cascade do |t| @@ -58,16 +57,16 @@ t.string "agent_type" t.string "agent_id" t.string "access" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["hyrax_collection_type_id"], name: "hyrax_collection_type_id" end create_table "content_blocks", force: :cascade do |t| t.string "name" t.text "value" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.string "external_key" end @@ -84,8 +83,8 @@ t.integer "rgt", null: false t.integer "depth", default: 0, null: false t.integer "children_count", default: 0, null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["lft"], name: "index_curation_concerns_operations_on_lft" t.index ["parent_id"], name: "index_curation_concerns_operations_on_parent_id" t.index ["rgt"], name: "index_curation_concerns_operations_on_rgt" @@ -95,29 +94,29 @@ create_table "featured_works", force: :cascade do |t| t.integer "order", default: 5 t.string "work_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["order"], name: "index_featured_works_on_order" t.index ["work_id"], name: "index_featured_works_on_work_id" end create_table "file_download_stats", force: :cascade do |t| - t.datetime "date" + t.datetime "date", precision: nil t.integer "downloads" t.string "file_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.integer "user_id" t.index ["file_id"], name: "index_file_download_stats_on_file_id" t.index ["user_id"], name: "index_file_download_stats_on_user_id" end create_table "file_view_stats", force: :cascade do |t| - t.datetime "date" + t.datetime "date", precision: nil t.integer "views" t.string "file_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.integer "user_id" t.index ["file_id"], name: "index_file_view_stats_on_file_id" t.index ["user_id"], name: "index_file_view_stats_on_user_id" @@ -147,8 +146,8 @@ t.date "date" t.integer "total_item_investigations" t.integer "total_item_requests" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.string "title" t.integer "year_of_publication" t.string "publisher" @@ -161,15 +160,15 @@ create_table "hyrax_default_administrative_set", force: :cascade do |t| t.string "default_admin_set_id", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false end create_table "hyrax_features", force: :cascade do |t| t.string "key", null: false t.boolean "enabled", default: false, null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false end create_table "job_io_wrappers", force: :cascade do |t| @@ -180,8 +179,8 @@ t.string "original_name" t.string "path" t.string "relation" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["uploaded_file_id"], name: "index_job_io_wrappers_on_uploaded_file_id" t.index ["user_id"], name: "index_job_io_wrappers_on_user_id" end @@ -196,8 +195,8 @@ create_table "mailboxer_conversations", id: :serial, force: :cascade do |t| t.string "subject", default: "" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false end create_table "mailboxer_notifications", id: :serial, force: :cascade do |t| @@ -212,10 +211,10 @@ t.string "notified_object_type" t.integer "notified_object_id" t.string "attachment" - t.datetime "updated_at", null: false - t.datetime "created_at", null: false + t.datetime "updated_at", precision: nil, null: false + t.datetime "created_at", precision: nil, null: false t.boolean "global", default: false - t.datetime "expires" + t.datetime "expires", precision: nil t.index ["conversation_id"], name: "index_mailboxer_notifications_on_conversation_id" t.index ["notified_object_id", "notified_object_type"], name: "index_mailboxer_notifications_on_notified_object_id_and_type" t.index ["notified_object_type", "notified_object_id"], name: "mailboxer_notifications_notified_object" @@ -231,8 +230,8 @@ t.boolean "trashed", default: false t.boolean "deleted", default: false t.string "mailbox_type", limit: 25 - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.boolean "is_delivered", default: false t.string "delivery_method" t.string "message_id" @@ -246,15 +245,15 @@ t.text "counters" t.bigint "seq", default: 0 t.binary "rand" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["namespace"], name: "index_minter_states_on_namespace", unique: true end create_table "orm_resources", id: :text, default: -> { "(uuid_generate_v4())::text" }, force: :cascade do |t| t.jsonb "metadata", default: {}, null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.string "internal_resource" t.integer "lock_version" t.index ["internal_resource"], name: "index_orm_resources_on_internal_resource" @@ -268,8 +267,8 @@ t.string "agent_type" t.string "agent_id" t.string "access" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["permission_template_id", "agent_id", "agent_type", "access"], name: "uk_permission_template_accesses", unique: true t.index ["permission_template_id"], name: "index_permission_template_accesses_on_permission_template_id" end @@ -277,8 +276,8 @@ create_table "permission_templates", force: :cascade do |t| t.string "source_id" t.string "visibility" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.date "release_date" t.string "release_period" t.index ["source_id"], name: "index_permission_templates_on_source_id", unique: true @@ -288,12 +287,12 @@ t.string "work_id", null: false t.bigint "sending_user_id", null: false t.bigint "receiving_user_id", null: false - t.datetime "fulfillment_date" + t.datetime "fulfillment_date", precision: nil t.string "status", default: "pending", null: false t.text "sender_comment" t.text "receiver_comment" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["receiving_user_id"], name: "index_proxy_deposit_requests_on_receiving_user_id" t.index ["sending_user_id"], name: "index_proxy_deposit_requests_on_sending_user_id" end @@ -301,16 +300,16 @@ create_table "proxy_deposit_rights", force: :cascade do |t| t.bigint "grantor_id" t.bigint "grantee_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["grantee_id"], name: "index_proxy_deposit_rights_on_grantee_id" t.index ["grantor_id"], name: "index_proxy_deposit_rights_on_grantor_id" end create_table "qa_local_authorities", force: :cascade do |t| t.string "name" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["name"], name: "index_qa_local_authorities_on_name", unique: true end @@ -318,8 +317,8 @@ t.bigint "local_authority_id" t.string "label" t.string "uri" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["local_authority_id"], name: "index_qa_local_authority_entries_on_local_authority_id" t.index ["uri"], name: "index_qa_local_authority_entries_on_uri", unique: true end @@ -328,8 +327,8 @@ t.binary "query_params" t.integer "user_id" t.string "user_type" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["user_id"], name: "index_searches_on_user_id" end @@ -337,16 +336,16 @@ t.string "download_key" t.string "path" t.string "item_id" - t.datetime "expires" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "expires", precision: nil + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false end create_table "sipity_agents", force: :cascade do |t| t.string "proxy_for_id", null: false t.string "proxy_for_type", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["proxy_for_id", "proxy_for_type"], name: "sipity_agents_proxy_for", unique: true end @@ -354,8 +353,8 @@ t.integer "entity_id", null: false t.integer "agent_id", null: false t.text "comment" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["agent_id"], name: "index_sipity_comments_on_agent_id" t.index ["created_at"], name: "index_sipity_comments_on_created_at" t.index ["entity_id"], name: "index_sipity_comments_on_entity_id" @@ -365,8 +364,8 @@ t.string "proxy_for_global_id", null: false t.integer "workflow_id", null: false t.integer "workflow_state_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["proxy_for_global_id"], name: "sipity_entities_proxy_for_global_id", unique: true t.index ["workflow_id"], name: "index_sipity_entities_on_workflow_id" t.index ["workflow_state_id"], name: "index_sipity_entities_on_workflow_state_id" @@ -376,8 +375,8 @@ t.integer "workflow_role_id", null: false t.integer "entity_id", null: false t.integer "agent_id", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["agent_id"], name: "sipity_entity_specific_responsibilities_agent" t.index ["entity_id"], name: "sipity_entity_specific_responsibilities_entity" t.index ["workflow_role_id", "entity_id", "agent_id"], name: "sipity_entity_specific_responsibilities_aggregate", unique: true @@ -389,8 +388,8 @@ t.string "scope_for_notification_type", null: false t.string "reason_for_notification", null: false t.integer "notification_id", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["notification_id"], name: "sipity_notifiable_contexts_notification_id" t.index ["scope_for_notification_id", "scope_for_notification_type", "reason_for_notification", "notification_id"], name: "sipity_notifiable_contexts_concern_surrogate", unique: true t.index ["scope_for_notification_id", "scope_for_notification_type", "reason_for_notification"], name: "sipity_notifiable_contexts_concern_context" @@ -401,8 +400,8 @@ t.integer "notification_id", null: false t.integer "role_id", null: false t.string "recipient_strategy", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["notification_id", "role_id", "recipient_strategy"], name: "sipity_notifications_recipients_surrogate" t.index ["notification_id"], name: "sipity_notification_recipients_notification" t.index ["recipient_strategy"], name: "sipity_notification_recipients_recipient_strategy" @@ -412,8 +411,8 @@ create_table "sipity_notifications", force: :cascade do |t| t.string "name", null: false t.string "notification_type", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["name"], name: "index_sipity_notifications_on_name", unique: true t.index ["notification_type"], name: "index_sipity_notifications_on_notification_type" end @@ -421,8 +420,8 @@ create_table "sipity_roles", force: :cascade do |t| t.string "name", null: false t.text "description" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["name"], name: "index_sipity_roles_on_name", unique: true end @@ -430,8 +429,8 @@ t.integer "workflow_id", null: false t.integer "resulting_workflow_state_id" t.string "name", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["resulting_workflow_state_id"], name: "sipity_workflow_actions_resulting_workflow_state" t.index ["workflow_id", "name"], name: "sipity_workflow_actions_aggregate", unique: true t.index ["workflow_id"], name: "sipity_workflow_actions_workflow" @@ -441,48 +440,48 @@ t.string "service_name", null: false t.integer "weight", null: false t.integer "workflow_action_id", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["workflow_action_id"], name: "index_sipity_workflow_methods_on_workflow_action_id" end create_table "sipity_workflow_responsibilities", force: :cascade do |t| t.integer "agent_id", null: false t.integer "workflow_role_id", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["agent_id", "workflow_role_id"], name: "sipity_workflow_responsibilities_aggregate", unique: true end create_table "sipity_workflow_roles", force: :cascade do |t| t.integer "workflow_id", null: false t.integer "role_id", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["workflow_id", "role_id"], name: "sipity_workflow_roles_aggregate", unique: true end create_table "sipity_workflow_state_action_permissions", force: :cascade do |t| t.integer "workflow_role_id", null: false t.integer "workflow_state_action_id", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["workflow_role_id", "workflow_state_action_id"], name: "sipity_workflow_state_action_permissions_aggregate", unique: true end create_table "sipity_workflow_state_actions", force: :cascade do |t| t.integer "originating_workflow_state_id", null: false t.integer "workflow_action_id", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["originating_workflow_state_id", "workflow_action_id"], name: "sipity_workflow_state_actions_aggregate", unique: true end create_table "sipity_workflow_states", force: :cascade do |t| t.integer "workflow_id", null: false t.string "name", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["name"], name: "index_sipity_workflow_states_on_name" t.index ["workflow_id", "name"], name: "sipity_type_state_aggregate", unique: true end @@ -491,8 +490,8 @@ t.string "name", null: false t.string "label" t.text "description" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.integer "permission_template_id" t.boolean "active" t.boolean "allows_access_grant" @@ -501,34 +500,34 @@ create_table "tinymce_assets", force: :cascade do |t| t.string "file" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false end create_table "trophies", force: :cascade do |t| t.integer "user_id" t.string "work_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false end create_table "uploaded_files", force: :cascade do |t| t.string "file" t.bigint "user_id" t.string "file_set_uri" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["file_set_uri"], name: "index_uploaded_files_on_file_set_uri" t.index ["user_id"], name: "index_uploaded_files_on_user_id" end create_table "user_stats", force: :cascade do |t| t.integer "user_id" - t.datetime "date" + t.datetime "date", precision: nil t.integer "file_views" t.integer "file_downloads" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.integer "work_views" t.index ["user_id"], name: "index_user_stats_on_user_id" end @@ -537,10 +536,10 @@ t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false t.string "reset_password_token" - t.datetime "reset_password_sent_at" - t.datetime "remember_created_at" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "reset_password_sent_at", precision: nil + t.datetime "remember_created_at", precision: nil + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.boolean "guest", default: false t.string "facebook_handle" t.string "twitter_handle" @@ -558,7 +557,7 @@ t.string "avatar_file_name" t.string "avatar_content_type" t.integer "avatar_file_size" - t.datetime "avatar_updated_at" + t.datetime "avatar_updated_at", precision: nil t.string "linkedin_handle" t.string "orcid" t.string "arkivo_token" @@ -575,16 +574,16 @@ t.string "datastream_id" t.string "version_id" t.string "committer_login" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false end create_table "work_view_stats", force: :cascade do |t| - t.datetime "date" + t.datetime "date", precision: nil t.integer "work_views" t.string "work_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.integer "user_id" t.index ["user_id"], name: "index_work_view_stats_on_user_id" t.index ["work_id"], name: "index_work_view_stats_on_work_id" diff --git a/.github/workflows/lint-build-test.yml b/.github/workflows/lint-build-test.yml index 04be1da356..485c9d2d79 100644 --- a/.github/workflows/lint-build-test.yml +++ b/.github/workflows/lint-build-test.yml @@ -27,7 +27,7 @@ jobs: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: - ruby-version: '3.2' + ruby-version: '3.3' bundler-cache: true cache-version: 1 - name: Rubocop diff --git a/.koppie/Gemfile b/.koppie/Gemfile index 8d14016f5a..9e3a8e69ac 100644 --- a/.koppie/Gemfile +++ b/.koppie/Gemfile @@ -30,7 +30,7 @@ gem 'jbuilder', '~> 2.5' gem 'jquery-rails' gem 'pg', '~> 1.3' gem 'puma' -gem 'rails', '~> 6.1' +gem 'rails', '~> 7.2', '< 8.0' gem 'riiif', '~> 2.1' gem 'rsolr', '>= 1.0', '< 3' gem 'sass-rails', '~> 6.0' @@ -39,6 +39,7 @@ gem 'turbolinks', '~> 5' gem 'twitter-typeahead-rails', '0.11.1.pre.corejavascript' gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] gem 'uglifier', '>= 1.3.0' +gem 'activerecord-nulldb-adapter', git: 'https://github.com/taylorthurlow/nulldb', branch: 'fix/activerecord72-register-adapter' group :development do gem 'better_errors' # add command line in browser when errors @@ -46,7 +47,6 @@ group :development do # Access an interactive console on exception pages or by calling 'console' anywhere in the code. gem 'web-console', '>= 3.3.0' - gem 'listen', '>= 3.0.5', '< 3.2' # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring gem 'spring' gem 'spring-watcher-listen', '~> 2.0.0' diff --git a/.koppie/config/environments/development.rb b/.koppie/config/environments/development.rb index 21239d8532..1a28938836 100644 --- a/.koppie/config/environments/development.rb +++ b/.koppie/config/environments/development.rb @@ -55,7 +55,7 @@ config.assets.quiet = true # Raises error for missing translations - # config.action_view.raise_on_missing_translations = true + # config.i18n.raise_on_missing_translations = true # Use an evented file watcher to asynchronously detect changes in source code, # routes, locales, etc. This feature depends on the listen gem. diff --git a/.koppie/config/environments/production.rb b/.koppie/config/environments/production.rb index 5033acb146..5689d2eb81 100644 --- a/.koppie/config/environments/production.rb +++ b/.koppie/config/environments/production.rb @@ -26,8 +26,10 @@ config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? # Compress JavaScripts and CSS. - config.assets.js_compressor = Uglifier.new(:harmony => true) - # config.assets.css_compressor = :sass + config.assets.configure do |env| + env.js_compressor = :uglifier + # env.css_compressor = :sass + end # Do not fallback to assets pipeline if a precompiled asset is missed. config.assets.compile = false diff --git a/.koppie/config/environments/test.rb b/.koppie/config/environments/test.rb index 0aa292b9f8..e340d0f20d 100644 --- a/.koppie/config/environments/test.rb +++ b/.koppie/config/environments/test.rb @@ -43,7 +43,7 @@ config.active_support.deprecation = :stderr # Raises error for missing translations - # config.action_view.raise_on_missing_translations = true + # config.i18n.raise_on_missing_translations = true config.log_level = :warn end diff --git a/.koppie/config/initializers/hyrax.rb b/.koppie/config/initializers/hyrax.rb index f99e224d4c..9128a4064d 100644 --- a/.koppie/config/initializers/hyrax.rb +++ b/.koppie/config/initializers/hyrax.rb @@ -317,23 +317,26 @@ Qa::Authorities::Local.register_subauthority('languages', 'Qa::Authorities::Local::TableBasedAuthority') Qa::Authorities::Local.register_subauthority('genres', 'Qa::Authorities::Local::TableBasedAuthority') -custom_queries = [Hyrax::CustomQueries::Navigators::CollectionMembers, - Hyrax::CustomQueries::Navigators::ChildCollectionsNavigator, - Hyrax::CustomQueries::Navigators::ParentCollectionsNavigator, - Hyrax::CustomQueries::Navigators::ChildFileSetsNavigator, - Hyrax::CustomQueries::Navigators::ChildWorksNavigator, - Hyrax::CustomQueries::Navigators::ParentWorkNavigator, - Hyrax::CustomQueries::Navigators::FindFiles, - Hyrax::CustomQueries::FindAccessControl, - Hyrax::CustomQueries::FindCollectionsByType, - Hyrax::CustomQueries::FindFileMetadata, - Hyrax::CustomQueries::FindIdsByModel, - Hyrax::CustomQueries::FindManyByAlternateIds, - Hyrax::CustomQueries::FindModelsByAccess, - Hyrax::CustomQueries::FindCountBy, - Hyrax::CustomQueries::FindByDateRange] -custom_queries.each do |handler| - Hyrax.query_service.custom_queries.register_query_handler(handler) +Rails.application.reloader.to_prepare do + custom_queries = [Hyrax::CustomQueries::Navigators::CollectionMembers, + Hyrax::CustomQueries::Navigators::ChildCollectionsNavigator, + Hyrax::CustomQueries::Navigators::ParentCollectionsNavigator, + Hyrax::CustomQueries::Navigators::ChildFileSetsNavigator, + Hyrax::CustomQueries::Navigators::ChildWorksNavigator, + Hyrax::CustomQueries::Navigators::ParentWorkNavigator, + Hyrax::CustomQueries::Navigators::FindFiles, + Hyrax::CustomQueries::FindAccessControl, + Hyrax::CustomQueries::FindCollectionsByType, + Hyrax::CustomQueries::FindFileMetadata, + Hyrax::CustomQueries::FindIdsByModel, + Hyrax::CustomQueries::FindManyByAlternateIds, + Hyrax::CustomQueries::FindModelsByAccess, + Hyrax::CustomQueries::FindCountBy, + Hyrax::CustomQueries::FindByDateRange] + custom_queries.each do |handler| + Hyrax.query_service.custom_queries.register_query_handler(handler) + end end + ActiveFedora.init(solr_config_path: Rails.root.join('config', 'solr.yml')) diff --git a/.koppie/config/initializers/riiif.rb b/.koppie/config/initializers/riiif.rb index e4835610ea..b635933bcc 100644 --- a/.koppie/config/initializers/riiif.rb +++ b/.koppie/config/initializers/riiif.rb @@ -35,32 +35,35 @@ module Hyrax # Adds file locking to Riiif::File # @see RiiifFileResolver - class RiiifFile < Riiif::File - include ActiveSupport::Benchmarkable - - attr_reader :id - def initialize(input_path, tempfile = nil, id:) - super(input_path, tempfile) - raise(ArgumentError, "must specify id") if id.blank? - @id = id - end + Rails.application.reloader.to_prepare do + class RiiifFile < Riiif::File + include ActiveSupport::Benchmarkable + + attr_reader :id + def initialize(input_path, tempfile = nil, id:) + super(input_path, tempfile) + raise(ArgumentError, "must specify id") if id.blank? + @id = id + end - # Wrap extract in a read lock and benchmark it - def extract(transformation, image_info = nil) - Riiif::Image.file_resolver.file_locks[id].with_read_lock do - benchmark "RiiifFile extracted #{path} with #{transformation.to_params}", level: :debug do - super + # Wrap extract in a read lock and benchmark it + def extract(transformation, image_info = nil) + Riiif::Image.file_resolver.file_locks[id].with_read_lock do + benchmark "RiiifFile extracted #{path} with #{transformation.to_params}", level: :debug do + super + end end end - end - private + private - def logger - Hyrax.logger + def logger + Hyrax.logger + end end end + class RiiifFileResolver include ActiveSupport::Benchmarkable diff --git a/Dockerfile b/Dockerfile index b3c3b47a02..42702f60fb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -56,7 +56,7 @@ ARG BUNDLE_WITHOUT="development test" ONBUILD COPY --chown=1001:101 $APP_PATH /app/samvera/hyrax-webapp ONBUILD RUN bundle install --jobs "$(nproc)" -ONBUILD RUN RAILS_ENV=production SECRET_KEY_BASE=`bin/rake secret` DB_ADAPTER=nulldb DATABASE_URL='postgresql://fake' bundle exec rake assets:precompile +ONBUILD RUN RAILS_ENV=production SECRET_KEY_BASE=`bin/rake secret` DATABASE_URL='nulldb://nulldb' bundle exec rake assets:precompile FROM hyrax-base as hyrax-worker-base @@ -88,7 +88,7 @@ ARG BUNDLE_WITHOUT="development test" ONBUILD COPY --chown=1001:101 $APP_PATH /app/samvera/hyrax-webapp ONBUILD RUN bundle install --jobs "$(nproc)" -ONBUILD RUN RAILS_ENV=production SECRET_KEY_BASE=`bin/rake secret` DB_ADAPTER=nulldb DATABASE_URL='postgresql://fake' bundle exec rake assets:precompile +ONBUILD RUN RAILS_ENV=production SECRET_KEY_BASE=`bin/rake secret` DATABASE_URL='nulldb://nulldb' bundle exec rake assets:precompile FROM hyrax-base as hyrax-engine-dev @@ -114,7 +114,7 @@ RUN bundle -v && \ bundle install --jobs "$(nproc)" && \ yarn && yarn cache clean -RUN RAILS_ENV=production SECRET_KEY_BASE='fakesecret1234' DB_ADAPTER=nulldb DATABASE_URL='postgresql://fake' bundle exec rake assets:precompile +RUN RAILS_ENV=production SECRET_KEY_BASE='fakesecret1234' DATABASE_URL='nulldb://nulldb' bundle exec rake assets:precompile FROM hyrax-worker-base as hyrax-engine-dev-worker diff --git a/app/services/hyrax/listeners.rb b/app/services/hyrax/listeners.rb index ca99f2371e..a8c2a15a74 100644 --- a/app/services/hyrax/listeners.rb +++ b/app/services/hyrax/listeners.rb @@ -20,6 +20,7 @@ module Listeners autoload :ACLIndexListener autoload :ActiveFedoraACLIndexListener autoload :BatchNotificationListener + autoload :FileListener autoload :FileMetadataListener autoload :FileSetLifecycleListener autoload :FileSetLifecycleNotificationListener diff --git a/app/services/hyrax/workflow.rb b/app/services/hyrax/workflow.rb new file mode 100644 index 0000000000..145782fc8a --- /dev/null +++ b/app/services/hyrax/workflow.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true +module Hyrax + module Workflow + extend ActiveSupport::Autoload + + autoload :WorkflowFactory + end +end diff --git a/config/initializers/1_healthz.rb b/config/initializers/1_healthz.rb index e362e41112..7cf041ae3f 100644 --- a/config/initializers/1_healthz.rb +++ b/config/initializers/1_healthz.rb @@ -9,7 +9,8 @@ # application's `Gemfile`. # # @see https://github.com/sportngin/okcomputer/ -begin + +Rails.application.reloader.to_prepare do OkComputer.mount_at = 'healthz' require 'hyrax/health_checks' diff --git a/config/initializers/ar_test_fixture_monkey_patch.rb b/config/initializers/ar_test_fixture_monkey_patch.rb new file mode 100644 index 0000000000..70ebafd58b --- /dev/null +++ b/config/initializers/ar_test_fixture_monkey_patch.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true +module ActiveRecord + module TestFixtures + def fixture_path + fixture_paths[0] + end + end +end diff --git a/config/initializers/arel_rails_7_2_monkey_patch.rb b/config/initializers/arel_rails_7_2_monkey_patch.rb new file mode 100644 index 0000000000..926a5db99f --- /dev/null +++ b/config/initializers/arel_rails_7_2_monkey_patch.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true +class Arel::Table + def table_name + name + end +end diff --git a/config/initializers/listeners.rb b/config/initializers/listeners.rb index 7ff019f00f..33e140268c 100644 --- a/config/initializers/listeners.rb +++ b/config/initializers/listeners.rb @@ -2,8 +2,10 @@ Hyrax.publisher.subscribe(Hyrax::Listeners::ActiveFedoraACLIndexListener.new) unless Hyrax.config.disable_wings -Hyrax.publisher.default_listeners.each do |listener| - Hyrax.publisher.subscribe(listener) +Rails.application.reloader.to_prepare do + Hyrax.publisher.default_listeners.each do |listener| + Hyrax.publisher.subscribe(listener) + end end # Publish events from old style Hyrax::Callbacks to trigger the listeners diff --git a/config/initializers/reform_rails_6_1_monkey_patch.rb b/config/initializers/reform_rails_6_1_monkey_patch.rb deleted file mode 100644 index a4be6a6ef1..0000000000 --- a/config/initializers/reform_rails_6_1_monkey_patch.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true - -# Taken from https://github.com/trailblazer/reform-rails/issues/86#issuecomment-763120151 -# rubocop:disable Metrics/CyclomaticComplexity -# rubocop:disable Lint/UselessAssignment -module Reform - class Contract < Disposable::Twin - class Result - private - - # this doesn't do nested errors (e.g. ) - def filter_for(method, *args) - @results.collect { |r| r.public_send(method, *args).to_h } - .inject({}) { |hah, err| hah.merge(err) { |_key, old_v, new_v| (new_v.is_a?(Array) ? (old_v |= new_v) : old_v.merge(new_v)) } } - .find_all do |_k, v| # filter :nested=>{:something=>["too nested!"]} #DISCUSS: do we want that here? - if v.is_a?(Hash) - nested_errors = v.select { |attr_key, val| attr_key.is_a?(Integer) && val.is_a?(Array) && val.any? } - v = nested_errors.to_a if nested_errors.any? - end - v.is_a?(ActiveModel::DeprecationHandlingMessageArray) - end.to_h - end - end - end -end -# rubocop:enable Metrics/CyclomaticComplexity -# rubocop:disable Lint/UselessAssignment diff --git a/hyrax.gemspec b/hyrax.gemspec index f444e098dd..9a288c4ef3 100644 --- a/hyrax.gemspec +++ b/hyrax.gemspec @@ -32,9 +32,9 @@ SUMMARY # NOTE: rails does not follow sem-ver conventions, it's # minor version releases can include breaking changes; see # http://guides.rubyonrails.org/maintenance_policy.html - spec.add_dependency 'rails', '~> 6.1' + spec.add_dependency 'rails', '~> 7.2', '< 8.0' - spec.add_dependency 'active-fedora', '~> 14.0' + spec.add_dependency 'active-fedora', '~> 15.0' spec.add_dependency 'almond-rails', '~> 0.1' spec.add_dependency 'awesome_nested_set', '~> 3.1' spec.add_dependency 'blacklight', '~> 7.29' @@ -57,10 +57,10 @@ SUMMARY spec.add_dependency 'flot-rails', '~> 0.0.6' spec.add_dependency 'font-awesome-rails', '~> 4.2' spec.add_dependency 'google-analytics-data', '~> 0.6' - spec.add_dependency 'hydra-derivatives', '~> 3.3' + spec.add_dependency 'hydra-derivatives', '~> 4.0' spec.add_dependency 'hydra-editor', '~> 6.0' spec.add_dependency 'hydra-file_characterization', '~> 1.1' - spec.add_dependency 'hydra-head', '~> 12.0' + spec.add_dependency 'hydra-head', '~> 13.0' spec.add_dependency 'hydra-works', '>= 0.16' spec.add_dependency 'iiif_manifest', '>= 0.3', '< 2.0' spec.add_dependency 'json-schema' # for Arkivo @@ -93,7 +93,7 @@ SUMMARY spec.add_development_dependency "capybara", '~> 3.29' spec.add_development_dependency 'capybara-screenshot', '~> 1.0' - spec.add_development_dependency 'database_cleaner', '~> 1.3' + spec.add_development_dependency 'database_cleaner', '>= 1.3' spec.add_development_dependency "equivalent-xml", '~> 0.5' spec.add_development_dependency "factory_bot", '~> 4.4' spec.add_development_dependency 'mida', '~> 0.3' @@ -101,7 +101,7 @@ SUMMARY spec.add_development_dependency 'pg', '~> 1.2' spec.add_development_dependency 'rspec-activemodel-mocks', '~> 1.0' spec.add_development_dependency 'rspec-its', '~> 1.1' - spec.add_development_dependency 'rspec-rails', '~> 6.0' + spec.add_development_dependency 'rspec-rails', '~> 7.0' spec.add_development_dependency 'rspec_junit_formatter' spec.add_development_dependency "selenium-webdriver", '~> 4.4' spec.add_development_dependency 'i18n-debug' diff --git a/lib/active_fedora/attribute_methods.rb b/lib/active_fedora/attribute_methods.rb new file mode 100644 index 0000000000..1182972cf7 --- /dev/null +++ b/lib/active_fedora/attribute_methods.rb @@ -0,0 +1,249 @@ +# frozen_string_literal: true +require 'active_support/core_ext/object' +require 'active_support/core_ext/class/attribute' +require 'mutex_m' + +module ActiveFedora + module AttributeMethods + # rubocop:disable Naming/PredicateName + extend ActiveSupport::Concern + include ActiveModel::AttributeMethods + + AttrNames = Module.new do + def self.set_name_cache(name, value) + const_name = "ATTR_#{name}" + const_set const_name, value.dup.freeze unless const_defined? const_name + end + end + + RESTRICTED_CLASS_METHODS = %w[private public protected allocate new name parent superclass].freeze + + class GeneratedAttributeMethods < Module; end # :nodoc: + + module ClassMethods + def inherited(child_class) # :nodoc: + child_class.initialize_generated_modules + super + end + + def initialize_generated_modules # :nodoc: + @generated_attribute_methods = GeneratedAttributeMethods.new { extend Mutex_m } + @attribute_methods_generated = false + include @generated_attribute_methods + + super + end + + # Raises an ActiveFedora::DangerousAttributeError exception when an + # \Active \Record method is defined in the model, otherwise +false+. + # + # class Person < ActiveRecord::Base + # def save + # 'already defined by Active Fedora' + # end + # end + # + # Person.instance_method_already_implemented?(:save) + # # => ActiveFedora::DangerousAttributeError: save is defined by Active Record. Check to make sure that you don't have an attribute or method with the same name. + # + # Person.instance_method_already_implemented?(:name) + # # => false + def instance_method_already_implemented?(method_name) + if dangerous_attribute_method?(method_name) + raise DangerousAttributeError, +"#{method_name} is defined by Active Fedora. Check to make sure that you don't have an attribute or method with the same name." + end + + if superclass == Base + super + else + # If ThisClass < ... < SomeSuperClass < ... < Base and SomeSuperClass + # defines its own attribute method, then we don't want to overwrite that. + defined = method_defined_within?(method_name, superclass, Base) && + !superclass.instance_method(method_name).owner.is_a?(GeneratedAttributeMethods) + defined || super + end + end + + # A method name is 'dangerous' if it is already (re)defined by Active Fedora, but + # not by any ancestors. (So 'puts' is not dangerous but 'save' is.) + def dangerous_attribute_method?(name) # :nodoc: + method_defined_within?(name, Base) + end + + def method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc: + if klass.method_defined?(name) || klass.private_method_defined?(name) + if superklass.method_defined?(name) || superklass.private_method_defined?(name) + klass.instance_method(name).owner != superklass.instance_method(name).owner + else + true + end + else + false + end + end + + # A class method is 'dangerous' if it is already (re)defined by Active Record, but + # not by any ancestors. (So 'puts' is not dangerous but 'new' is.) + def dangerous_class_method?(method_name) + RESTRICTED_CLASS_METHODS.include?(method_name.to_s) || class_method_defined_within?(method_name, Base) + end + + def class_method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc: + if klass.respond_to?(name, true) + if superklass.respond_to?(name, true) + klass.method(name).owner != superklass.method(name).owner + else + true + end + else + false + end + end + + private + + # @param name [Symbol] name of the attribute to generate + def generate_method(name) + generated_attribute_methods.synchronize do + define_attribute_methods name + end + end + end + + included do + initialize_generated_modules + include Read + include Write + include Dirty + end + + # Returns +true+ if the given attribute is in the attributes hash, otherwise +false+. + # + # class Person < ActiveRecord::Base + # end + # + # person = Person.new + # person.has_attribute?(:name) # => true + # person.has_attribute?('age') # => true + # person.has_attribute?(:nothing) # => false + def has_attribute?(attr_name) + attribute_names.include?(attr_name.to_s) + end + + # Returns an array of names for the attributes available on this object. + # + # class Person < ActiveFedora::Base + # end + # + # person = Person.new + # person.attribute_names + # # => ["id", "created_at", "updated_at", "name", "age"] + def attribute_names + @local_attributes.keys + end + + # Returns a hash of all the attributes with their names as keys and the values of the attributes as values. + # + # class Person < ActiveFedora::Base + # end + # + # person = Person.create(name: 'Francesco', age: 22) + # person.attributes + # # => {"id"=>3, "created_at"=>Sun, 21 Oct 2012 04:53:04, "updated_at"=>Sun, 21 Oct 2012 04:53:04, "name"=>"Francesco", "age"=>22} + def attributes + result = {} + attribute_names.each do |name| + result[name] = read_attribute(name) + end + result + end + + # Returns an #inspect-like string for the value of the + # attribute +attr_name+. String attributes are truncated up to 50 + # characters, Date and Time attributes are returned in the + # :db format, Array attributes are truncated up to 10 values. + # Other attributes return the value of #inspect without + # modification. + # + # person = Person.create!(name: 'David Heinemeier Hansson ' * 3) + # + # person.attribute_for_inspect(:name) + # # => "\"David Heinemeier Hansson David Heinemeier Hansson ...\"" + # + # person.attribute_for_inspect(:created_at) + # # => "\"2012-10-22 00:15:07\"" + # + # person.attribute_for_inspect(:tag_ids) + # # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...]" + def attribute_for_inspect(attr_name) + value = self[attr_name] + + if value.is_a?(String) && value.length > 50 + "#{value[0, 50]}...".inspect + elsif value.is_a?(Date) || value.is_a?(Time) + %("#{value}") + elsif value.is_a?(Array) && value.size > 10 + inspected = value.first(10).inspect + %(#{inspected[0...-1]}, ...]) + else + value.inspect + end + end + + # Returns +true+ if the specified +attribute+ has been set by the user or by a + # database load and is neither +nil+ nor empty? (the latter only applies + # to objects that respond to empty?, most notably Strings). Otherwise, +false+. + # Note that it always returns +true+ with boolean attributes. + # + # class Task < ActiveRecord::Base + # end + # + # task = Task.new(title: '', is_done: false) + # task.attribute_present?(:title) # => false + # task.attribute_present?(:is_done) # => true + # task.title = 'Buy milk' + # task.is_done = true + # task.attribute_present?(:title) # => true + # task.attribute_present?(:is_done) # => true + def attribute_present?(attribute) + value = self[attribute] + !value.nil? && !(value.respond_to?(:empty?) && value.empty?) + end + + # Returns the value of the attribute identified by attr_name after it has been typecast (for example, + # "2004-12-12" in a date column is cast to a date object, like Date.new(2004, 12, 12)). It raises + # ActiveModel::MissingAttributeError if the identified attribute is missing. + # + # Alias for the read_attribute method. + # + # class Person < ActiveRecord::Base + # belongs_to :organization + # end + # + # person = Person.new(name: 'Francesco', age: '22') + # person[:name] # => "Francesco" + # person[:age] # => 22 + # + # person = Person.select('id').first + # person[:name] # => ActiveModel::MissingAttributeError: missing attribute: name + # person[:organization_id] # => ActiveModel::MissingAttributeError: missing attribute: organization_id + def [](attr_name) + read_attribute(attr_name) { |n| missing_attribute(n, caller) } + end + + # Updates the attribute identified by attr_name with the specified +value+. + # (Alias for the protected write_attribute method). + # + # class Person < ActiveFedora::Base + # end + # + # person = Person.new + # person[:age] = '22' + # person[:age] # => 22 + # person[:age] # => Integer + def []=(attr_name, value) + write_attribute(attr_name, value) + end + end +end diff --git a/lib/generators/hyrax/templates/config/initializers/riiif.rb b/lib/generators/hyrax/templates/config/initializers/riiif.rb index eace5cdad8..47bafca4ab 100644 --- a/lib/generators/hyrax/templates/config/initializers/riiif.rb +++ b/lib/generators/hyrax/templates/config/initializers/riiif.rb @@ -29,35 +29,37 @@ Riiif.not_found_image = Rails.root.join('app', 'assets', 'images', 'us_404.svg') Riiif.unauthorized_image = Rails.root.join('app', 'assets', 'images', 'us_404.svg') - Riiif::Engine.config.cache_duration = 1.month + Riiif::Engine.config.cache_duration = 1.day end module Hyrax # Adds file locking to Riiif::File # @see RiiifFileResolver - class RiiifFile < Riiif::File - include ActiveSupport::Benchmarkable - - attr_reader :id - def initialize(input_path, tempfile = nil, id:) - super(input_path, tempfile) - raise(ArgumentError, "must specify id") if id.blank? - @id = id - end + Rails.application.reloader.to_prepare do + class RiiifFile < Riiif::File + include ActiveSupport::Benchmarkable + + attr_reader :id + def initialize(input_path, tempfile = nil, id:) + super(input_path, tempfile) + raise(ArgumentError, "must specify id") if id.blank? + @id = id + end - # Wrap extract in a read lock and benchmark it - def extract(transformation, image_info = nil) - Riiif::Image.file_resolver.file_locks[id].with_read_lock do - benchmark "RiiifFile extracted #{path} with #{transformation.to_params}", level: :debug do - super + # Wrap extract in a read lock and benchmark it + def extract(transformation, image_info = nil) + Riiif::Image.file_resolver.file_locks[id].with_read_lock do + benchmark "RiiifFile extracted #{path} with #{transformation.to_params}", level: :debug do + super + end end end - end - private + private - def logger - Hyrax.logger + def logger + Hyrax.logger + end end end diff --git a/lib/hyrax.rb b/lib/hyrax.rb index 4024b5a38d..3a6ebf0836 100644 --- a/lib/hyrax.rb +++ b/lib/hyrax.rb @@ -46,6 +46,10 @@ module Hyrax autoload :ResourceSync autoload :Zotero autoload :Listeners + autoload :Workflow + autoload :SimpleSchemaLoader + autoload :VirusScanner + autoload :DerivativeBucketedStorage end ## diff --git a/lib/hyrax/configuration.rb b/lib/hyrax/configuration.rb index 470873b192..7a909738a5 100644 --- a/lib/hyrax/configuration.rb +++ b/lib/hyrax/configuration.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true +require 'hyrax/callbacks' require 'hyrax/role_registry' module Hyrax diff --git a/lib/hyrax/engine.rb b/lib/hyrax/engine.rb index 71983435b3..31889b548e 100644 --- a/lib/hyrax/engine.rb +++ b/lib/hyrax/engine.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true module Hyrax - class Engine < ::Rails::Engine + class Engine < ::Rails::Engine # rubocop:disable Metrics/ClassLength isolate_namespace Hyrax require 'almond-rails' @@ -88,9 +88,11 @@ class Engine < ::Rails::Engine end initializer 'requires' do - require 'wings' unless Hyrax.config.disable_wings - require 'freyja' unless Hyrax.config.disable_freyja - require 'frigg' unless Hyrax.config.disable_frigg + ActiveSupport::Reloader.to_prepare do + require 'wings' unless Hyrax.config.disable_wings + require 'freyja' unless Hyrax.config.disable_freyja + require 'frigg' unless Hyrax.config.disable_frigg + end end initializer 'routing' do diff --git a/spec/controllers/hyrax/generic_works_controller_spec.rb b/spec/controllers/hyrax/generic_works_controller_spec.rb index 15820ecf6e..38c3c1b468 100644 --- a/spec/controllers/hyrax/generic_works_controller_spec.rb +++ b/spec/controllers/hyrax/generic_works_controller_spec.rb @@ -379,7 +379,7 @@ # makes one work, two file sets and calls ImportUrlJob twice. expect(actor).to receive(:create).with(Hyrax::Actors::Environment) do |env| expect(env.attributes['uploaded_files']).to eq [] - expect(env.attributes['remote_files']).to eq browse_everything_params.values + expect(env.attributes['remote_files'].map! { |v| v.permit!.to_h }).to eq browse_everything_params.values end post :create, params: { diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a9ddfb9b0e..13a546ba13 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -26,7 +26,8 @@ def ci_build? require 'factory_bot' require File.expand_path("config/environment", '../hyrax-webapp') -db_config = ActiveRecord::Base.configurations[ENV['RAILS_ENV']] +# db_config = ActiveRecord::Base.configurations[ENV['RAILS_ENV']] +db_config = ActiveRecord::Base.configurations.configs_for(env_name: ENV['RAILS_ENV'])[0] ActiveRecord::Tasks::DatabaseTasks.create(db_config) ActiveRecord::Migrator.migrations_paths = [Pathname.new(ENV['RAILS_ROOT']).join('db', 'migrate').to_s] ActiveRecord::Tasks::DatabaseTasks.migrate @@ -161,13 +162,24 @@ def clean_active_fedora_repository c.syntax = :expect end - config.fixture_path = File.expand_path("../fixtures", __FILE__) + config.fixture_paths = [File.expand_path("../fixtures", __FILE__)] config.file_fixture_path = File.expand_path("../fixtures", __FILE__) config.use_transactional_fixtures = false config.before :suite do FactoryBot::SyntaxRunner.include ActiveJob::TestHelper FactoryBot::SyntaxRunner.include RSpec::Mocks::ExampleMethods + # Workarounds for perform_enqueued_jobs + FactoryBot::SyntaxRunner.include ActiveSupport::Testing::TaggedLogging + # See https://github.com/rspec/rspec-rails/issues/2545 + FactoryBot::SyntaxRunner.class_eval do + def name + 'FactoryBot::SyntaxRunner' + end + end + require 'rspec/core/minitest_assertions_adapter' + FactoryBot::SyntaxRunner.include RSpec::Core::MinitestAssertionsAdapter + # End of workarounds Hyrax::RedisEventStore.instance.then(&:flushdb) DatabaseCleaner.clean_with(:truncation) # Noid minting causes extra LDP requests which slow the test suite. diff --git a/spec/views/hyrax/admin/admin_sets/_show_document_list.html.erb_spec.rb b/spec/views/hyrax/admin/admin_sets/_show_document_list.html.erb_spec.rb index a29caf583d..6883d26475 100644 --- a/spec/views/hyrax/admin/admin_sets/_show_document_list.html.erb_spec.rb +++ b/spec/views/hyrax/admin/admin_sets/_show_document_list.html.erb_spec.rb @@ -8,7 +8,7 @@ end it "renders rows of works" do - render('hyrax/admin/admin_sets/show_document_list.html.erb', documents: documents) + render('hyrax/admin/admin_sets/show_document_list', documents: documents) expect(rendered).to have_css('tbody', text: documents.join) end end diff --git a/spec/views/hyrax/admin/admin_sets/_show_document_list_row.html.erb_spec.rb b/spec/views/hyrax/admin/admin_sets/_show_document_list_row.html.erb_spec.rb index 92a2ad22db..b18dc40c56 100644 --- a/spec/views/hyrax/admin/admin_sets/_show_document_list_row.html.erb_spec.rb +++ b/spec/views/hyrax/admin/admin_sets/_show_document_list_row.html.erb_spec.rb @@ -22,7 +22,7 @@ end it "renders works" do - render 'hyrax/admin/admin_sets/show_document_list_row.html.erb', show_document_list_row: work + render 'hyrax/admin/admin_sets/show_document_list_row', show_document_list_row: work expect(rendered).to have_content 'One Hundred Years of Solitude' expect(rendered).to have_content 'deposited' end diff --git a/spec/views/hyrax/base/_member.html.erb_spec.rb b/spec/views/hyrax/base/_member.html.erb_spec.rb index 13a2325929..b2f38fb057 100644 --- a/spec/views/hyrax/base/_member.html.erb_spec.rb +++ b/spec/views/hyrax/base/_member.html.erb_spec.rb @@ -31,7 +31,7 @@ allow(view).to receive(:contextual_path).with(anything, anything) do |x, y| Hyrax::ContextualPath.new(x, y).show end - render 'hyrax/base/member.html.erb', member: presenter + render 'hyrax/base/member', member: presenter end it 'checks the :download ability' do diff --git a/spec/views/hyrax/batch_select/_add_button.html.erb_spec.rb b/spec/views/hyrax/batch_select/_add_button.html.erb_spec.rb index b382af7f02..c9ab0a73b3 100644 --- a/spec/views/hyrax/batch_select/_add_button.html.erb_spec.rb +++ b/spec/views/hyrax/batch_select/_add_button.html.erb_spec.rb @@ -3,7 +3,7 @@ RSpec.describe 'hyrax/batch_select/_add_button.html.erb', type: :view do let(:document) { double(id: 123) } before do - render 'hyrax/batch_select/add_button.html.erb', document: document + render 'hyrax/batch_select/add_button', document: document end it 'renders a checkbox named "batch_document_ids[]"' do diff --git a/spec/views/hyrax/collections/_show_document_list.html.erb_spec.rb b/spec/views/hyrax/collections/_show_document_list.html.erb_spec.rb index c158b7290f..6838893940 100644 --- a/spec/views/hyrax/collections/_show_document_list.html.erb_spec.rb +++ b/spec/views/hyrax/collections/_show_document_list.html.erb_spec.rb @@ -8,7 +8,7 @@ context 'when not logged in' do it "renders the documents without an Action section" do allow(view).to receive(:current_user).and_return(nil) - render('hyrax/collections/show_document_list.html.erb', documents: documents) + render('hyrax/collections/show_document_list', documents: documents) expect(rendered).to have_css('tbody', text: documents.join) expect(rendered).not_to have_css('th', text: 'Action') end @@ -17,7 +17,7 @@ context 'when logged in' do it "renders the documents without an Action section" do allow(view).to receive(:current_user).and_return(true) - render('hyrax/collections/show_document_list.html.erb', documents: documents) + render('hyrax/collections/show_document_list', documents: documents) expect(rendered).to have_css('tbody', text: documents.join) expect(rendered).not_to have_css('th', text: 'Action') end diff --git a/spec/views/hyrax/collections/_show_document_list_row.html.erb_spec.rb b/spec/views/hyrax/collections/_show_document_list_row.html.erb_spec.rb index add7a3db95..e26fec01a4 100644 --- a/spec/views/hyrax/collections/_show_document_list_row.html.erb_spec.rb +++ b/spec/views/hyrax/collections/_show_document_list_row.html.erb_spec.rb @@ -26,12 +26,12 @@ end it "renders collections links" do - render('hyrax/collections/show_document_list_row.html.erb', document: solr_doc) + render('hyrax/collections/show_document_list_row', document: solr_doc) expect(rendered).not_to have_content 'My awesome collection' end it "renders works" do - render('hyrax/collections/show_document_list_row.html.erb', document: solr_doc) + render('hyrax/collections/show_document_list_row', document: solr_doc) expect(rendered).to have_content 'One Hundred Years of Solitude' expect(rendered).not_to have_content('Edit Access:') end diff --git a/spec/views/hyrax/collections/_show_parent_collections.html.erb_spec.rb b/spec/views/hyrax/collections/_show_parent_collections.html.erb_spec.rb index 6543a7d729..1f7dc0fc0c 100644 --- a/spec/views/hyrax/collections/_show_parent_collections.html.erb_spec.rb +++ b/spec/views/hyrax/collections/_show_parent_collections.html.erb_spec.rb @@ -8,7 +8,7 @@ 'date_created_tesim' => '2000-01-01' } end - let(:subject) { render('show_parent_collections.html.erb', presenter: presenter) } + let(:subject) { render('show_parent_collections', presenter: presenter) } let(:ability) { double } let(:solr_document) { SolrDocument.new(collection_doc) } let(:presenter) { Hyrax::CollectionPresenter.new(solr_document, ability) } diff --git a/spec/views/hyrax/collections/_subcollection_list.html.erb_spec.rb b/spec/views/hyrax/collections/_subcollection_list.html.erb_spec.rb index 56bade728d..2d66321bcb 100644 --- a/spec/views/hyrax/collections/_subcollection_list.html.erb_spec.rb +++ b/spec/views/hyrax/collections/_subcollection_list.html.erb_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true RSpec.describe 'hyrax/collections/_subcollection_list.html.erb', type: :view do - let(:subject) { render('subcollection_list.html.erb', collection: subcollection) } + let(:subject) { render('subcollection_list', collection: subcollection) } let(:collection) { stub_model(Collection, id: '123') } context 'when subcollection list is empty' do @@ -11,7 +11,7 @@ end it "posts a warning message" do - render('subcollection_list.html.erb', collection: subcollection) + render('subcollection_list', collection: subcollection) expect(rendered).to have_text("There are no visible subcollections.") end end diff --git a/spec/views/hyrax/dashboard/collections/_list_collections.html.erb_spec.rb b/spec/views/hyrax/dashboard/collections/_list_collections.html.erb_spec.rb index ffe0cb227a..7ac1926aa3 100644 --- a/spec/views/hyrax/dashboard/collections/_list_collections.html.erb_spec.rb +++ b/spec/views/hyrax/dashboard/collections/_list_collections.html.erb_spec.rb @@ -33,7 +33,7 @@ before do allow(ability).to receive(:admin?).and_return(false) allow(presenter).to receive(:managed_access).and_return('Manage Access') - render('list_collections.html.erb', collection_presenter: presenter) + render('list_collections', collection_presenter: presenter) end # NOTE: Real labels are Manage, Deposit, or View, but UI shows whatever label gets returned, @@ -47,7 +47,7 @@ before do allow(ability).to receive(:admin?).and_return(true) allow(presenter).to receive(:managed_access).and_return('Manage') - render('list_collections.html.erb', collection_presenter: presenter) + render('list_collections', collection_presenter: presenter) end it "doesn't show access" do diff --git a/spec/views/hyrax/dashboard/collections/_show_document_list.html.erb_spec.rb b/spec/views/hyrax/dashboard/collections/_show_document_list.html.erb_spec.rb index 4be6da630d..e27eddf312 100644 --- a/spec/views/hyrax/dashboard/collections/_show_document_list.html.erb_spec.rb +++ b/spec/views/hyrax/dashboard/collections/_show_document_list.html.erb_spec.rb @@ -8,7 +8,7 @@ context 'when not logged in' do it "renders the documents without an Action section" do allow(view).to receive(:current_user).and_return(nil) - render('show_document_list.html.erb', documents: documents) + render('show_document_list', documents: documents) expect(rendered).to have_css('tbody', text: documents.join) expect(rendered).not_to have_css('th', text: 'Action') end @@ -17,7 +17,7 @@ context 'when logged in' do it "renders the documents with an Action section" do allow(view).to receive(:current_user).and_return(true) - render('show_document_list.html.erb', documents: documents) + render('show_document_list', documents: documents) expect(rendered).to have_css('tbody', text: documents.join) expect(rendered).to have_css('th', text: 'Action') end diff --git a/spec/views/hyrax/dashboard/collections/_show_parent_collection_row.html.erb_spec.rb b/spec/views/hyrax/dashboard/collections/_show_parent_collection_row.html.erb_spec.rb index b2f3868625..394b5fe315 100644 --- a/spec/views/hyrax/dashboard/collections/_show_parent_collection_row.html.erb_spec.rb +++ b/spec/views/hyrax/dashboard/collections/_show_parent_collection_row.html.erb_spec.rb @@ -8,7 +8,7 @@ 'date_created_tesim' => '2000-01-01' } end let(:document) { SolrDocument.new(parent_collection_doc) } - let(:subject) { render('show_parent_collection_row.html.erb', id: child_collection.id, document: document) } + let(:subject) { render('show_parent_collection_row', id: child_collection.id, document: document) } context 'when user cannot edit the child collection' do before do diff --git a/spec/views/hyrax/dashboard/collections/_show_parent_collections.html.erb_spec.rb b/spec/views/hyrax/dashboard/collections/_show_parent_collections.html.erb_spec.rb index 457687e3c0..229804df39 100644 --- a/spec/views/hyrax/dashboard/collections/_show_parent_collections.html.erb_spec.rb +++ b/spec/views/hyrax/dashboard/collections/_show_parent_collections.html.erb_spec.rb @@ -8,7 +8,7 @@ 'date_created_tesim' => '2000-01-01' } end - let(:subject) { render('show_parent_collections.html.erb', presenter: presenter) } + let(:subject) { render('show_parent_collections', presenter: presenter) } let(:ability) { double } let(:solr_document) { SolrDocument.new(collection_doc) } let(:presenter) { Hyrax::CollectionPresenter.new(solr_document, ability) } diff --git a/spec/views/hyrax/dashboard/collections/_subcollection_list.html.erb_spec.rb b/spec/views/hyrax/dashboard/collections/_subcollection_list.html.erb_spec.rb index 43ee414629..1b44edc7f4 100644 --- a/spec/views/hyrax/dashboard/collections/_subcollection_list.html.erb_spec.rb +++ b/spec/views/hyrax/dashboard/collections/_subcollection_list.html.erb_spec.rb @@ -3,7 +3,7 @@ let(:user) { create :user } let(:ability) { instance_double("Ability") } let(:collection) { stub_model(Collection, id: '123') } - let(:subject) { render('subcollection_list.html.erb', id: collection.id, collection: subcollection) } + let(:subject) { render('subcollection_list', id: collection.id, collection: subcollection) } before do allow(view).to receive(:id).and_return(collection.id) @@ -19,7 +19,7 @@ end it "posts a warning message" do - render('subcollection_list.html.erb', collection: subcollection) + render('subcollection_list', collection: subcollection) expect(rendered).to have_text("There are no visible subcollections.") end end diff --git a/spec/views/hyrax/file_sets/_show_actions.html.erb_spec.rb b/spec/views/hyrax/file_sets/_show_actions.html.erb_spec.rb index 89beedab5f..538b244051 100644 --- a/spec/views/hyrax/file_sets/_show_actions.html.erb_spec.rb +++ b/spec/views/hyrax/file_sets/_show_actions.html.erb_spec.rb @@ -31,7 +31,7 @@ Hyrax.config.citations = citations allow(ability).to receive(:can?).with(:edit, anything).and_return(false) assign(:presenter, presenter) - view.lookup_context.view_paths.push 'app/views/hyrax/base' + stub_template '_social_media.html.erb' => 'social_media' render end @@ -57,7 +57,7 @@ allow(ability).to receive(:can?).with(:edit, anything).and_return(true) allow(presenter).to receive(:editor?).and_return(true) assign(:presenter, presenter) - view.lookup_context.view_paths.push 'app/views/hyrax/base' + stub_template '_social_media.html.erb' => 'social_media' render end diff --git a/spec/views/hyrax/file_sets/_single_use_links.html.erb_spec.rb b/spec/views/hyrax/file_sets/_single_use_links.html.erb_spec.rb index b43a5ebca4..0e93feccfb 100644 --- a/spec/views/hyrax/file_sets/_single_use_links.html.erb_spec.rb +++ b/spec/views/hyrax/file_sets/_single_use_links.html.erb_spec.rb @@ -8,7 +8,7 @@ context "with no single-use links" do before do allow(presenter).to receive(:single_use_links).and_return([]) - render 'hyrax/file_sets/single_use_links.html.erb', presenter: presenter + render 'hyrax/file_sets/single_use_links', presenter: presenter end it "renders a table with no links" do expect(rendered).to include("No links have been generated") @@ -22,7 +22,7 @@ before do controller.params = { id: "1234" } allow(presenter).to receive(:single_use_links).and_return([link_presenter]) - render 'hyrax/file_sets/single_use_links.html.erb', presenter: presenter + render 'hyrax/file_sets/single_use_links', presenter: presenter end it "renders a table with links" do expect(rendered).to include("Link sha2ha expires in 23 hours")