diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index ef405be..1ecb966 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -4,7 +4,7 @@ class ProjectsController < ApplicationController before_action :fetch_project!, only: %w[show edit update destroy] def index - @projects = Project.all + @projects = available_projects end def show; end @@ -16,7 +16,7 @@ class ProjectsController < ApplicationController def create @project = Project.new(project_params) if @project.save - redirect_to @project + redirect_to url_for(action: :index), notice: "Project #{@project.name} being created." else render :new, status: :unprocessable_entity end @@ -36,8 +36,12 @@ class ProjectsController < ApplicationController private + def available_projects + Project.ready + end + def fetch_project! - @project = Project.find_by!(code: params[:id]) + @project = available_projects.find_by!(code: params[:id]) self.current_project = @project end diff --git a/app/jobs/project_post_init_job.rb b/app/jobs/project_post_init_job.rb new file mode 100644 index 0000000..4b38d58 --- /dev/null +++ b/app/jobs/project_post_init_job.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class ProjectPostInitJob < ApplicationJob + queue_as :default + + def perform(project_id) + project = Project.preparing.find(project_id) + + create_tasks_number_sequence(project) + project.update!(status: :ready) + end + + private + + def create_tasks_number_sequence(project) + Project.connection.execute "CREATE SEQUENCE IF NOT EXISTS #{project.tasks_number_sequence_name} AS INT UNSIGNED" + end +end diff --git a/app/models/project.rb b/app/models/project.rb index 9faf29f..898d729 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Project < ApplicationRecord + enum :status, %i[preparing ready archived].index_by(&:itself), default: :preparing + validates :name, :code, presence: true validates :code, exclusion: { in: %w[new] }, uniqueness: true, format: { with: /\A[a-z]{2,}\z/ } @@ -10,7 +12,7 @@ class Project < ApplicationRecord normalizes :code, with: ->(code) { code.strip.downcase.gsub(/\W+/, '') } - after_commit :create_tasks_number_sequence, on: :create + after_commit :schedule_post_init_job, on: :create after_destroy_commit :drop_tasks_number_sequence def to_param @@ -32,8 +34,8 @@ class Project < ApplicationRecord private - def create_tasks_number_sequence - self.class.connection.execute "CREATE SEQUENCE IF NOT EXISTS #{tasks_number_sequence_name} AS INT UNSIGNED" + def schedule_post_init_job + ProjectPostInitJob.perform_later id end def drop_tasks_number_sequence diff --git a/config/database.yml b/config/database.yml index 8b86401..f7fadad 100644 --- a/config/database.yml +++ b/config/database.yml @@ -18,15 +18,25 @@ default: &default host: <%= ENV.fetch("DB_HOST") { "127.0.0.1" } %> development: - <<: *default - database: subtle_storm_development + primary: + <<: *default + database: subtle_storm_development + queue: + <<: *default + database: subtle_storm_development_queue + migrations_paths: db/queue_migrate # Warning: The database defined as "test" will be erased and # re-generated from your development database when you run "rake". # Do not set this db to the same as development or production. test: - <<: *default - database: subtle_storm_test + primary: + <<: *default + database: subtle_storm_test + queue: + <<: *default + database: subtle_storm_test_queue + migrations_paths: db/queue_migrate # As with config/credentials.yml, you never want to store sensitive information, # like your database password, in your source code. If your source code is diff --git a/config/environments/development.rb b/config/environments/development.rb index 263e0c4..29ec324 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -55,6 +55,11 @@ Rails.application.configure do # Highlight code that enqueued background job in logs. config.active_job.verbose_enqueue_logs = true + # Replace the default in-process and non-durable queuing backend for Active Job. + config.active_job.queue_adapter = :solid_queue + config.solid_queue.connects_to = { database: { writing: :queue } } + config.solid_queue.logger = ActiveSupport::Logger.new(STDOUT) + # Raises error for missing translations. # config.i18n.raise_on_missing_translations = true diff --git a/db/migrate/20251021180400_add_status_to_projects.rb b/db/migrate/20251021180400_add_status_to_projects.rb new file mode 100644 index 0000000..5624614 --- /dev/null +++ b/db/migrate/20251021180400_add_status_to_projects.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class AddStatusToProjects < ActiveRecord::Migration[8.0] + def change + change_table :projects, bulk: true do |t| + t.string :status + t.index :status, where: 'status IS NOT NULL' + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 51352bf..9a44e05 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2025_06_21_203940) do +ActiveRecord::Schema[8.0].define(version: 2025_10_21_180400) do create_table "action_text_rich_texts", charset: "utf8mb4", collation: "utf8mb4_uca1400_ai_ci", force: :cascade do |t| t.string "name", null: false t.text "body", size: :long @@ -54,8 +54,10 @@ ActiveRecord::Schema[8.0].define(version: 2025_06_21_203940) do t.string "code", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.string "status" t.index ["code"], name: "index_projects_on_code", unique: true t.index ["name"], name: "index_projects_on_name" + t.index ["status"], name: "index_projects_on_status" end create_table "sessions", charset: "utf8mb4", collation: "utf8mb4_uca1400_ai_ci", force: :cascade do |t|