Phase 2: move the 8 catalog models into the WarpEngine engine

- Software, Release, ReleaseAsset, ExternalLink, PlatformLink, Image,
  SoftwareImage, Download now live in the engine under WarpEngine::, on top
  of WarpEngine::ApplicationRecord; table names unchanged (empty prefix)
- each model runs an ActiveSupport load hook (:warp_engine_<model>) as a
  host extension point
- WarpEngine::Image reads its upload path from WarpEngine.config
- host references fully qualified (services, serializers, admin, specs);
  admin registrations renamed with as: so /admin URLs and route helpers are
  byte-identical; factories pinned to the namespaced classes
- Member#image now class_name: "WarpEngine::Image"

Verified: full suite green, /api/software and /api/builds byte-identical to
the phase-0 baselines, admin routes unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-04 18:51:12 +02:00
co-authored by Claude Fable 5
parent 2f6cc4cdc2
commit 92f10197c1
47 changed files with 314 additions and 278 deletions
@@ -6,7 +6,7 @@ RSpec.describe Api::SoftwareBuildsController, type: :request do
let!(:release) { create(:release, software: software, version: "2.0.0") }
before do
ReleaseAsset.create!(release: release, kind: "html", path: "/test/html")
WarpEngine::ReleaseAsset.create!(release: release, kind: "html", path: "/test/html")
end
it "returns per-software build info" do
+1 -1
View File
@@ -1,5 +1,5 @@
FactoryBot.define do
factory :download do
factory :download, class: "WarpEngine::Download" do
file_path { "/test/file.tic" }
ip_address { "127.0.0.1" }
end
+1 -1
View File
@@ -1,5 +1,5 @@
FactoryBot.define do
factory :platform_link do
factory :platform_link, class: "WarpEngine::PlatformLink" do
sequence(:name) { |n| "Link #{n}" }
platform { "tic80" }
url { "https://example.com/link" }
+1 -1
View File
@@ -1,5 +1,5 @@
FactoryBot.define do
factory :release do
factory :release, class: "WarpEngine::Release" do
software
sequence(:version) { |n| "1.0.#{n}" }
end
+1 -1
View File
@@ -1,5 +1,5 @@
FactoryBot.define do
factory :software do
factory :software, class: "WarpEngine::Software" do
sequence(:name) { |n| "game-#{n}" }
title { "Test Game" }
author { "dev" }
+1 -1
View File
@@ -1,6 +1,6 @@
require "rails_helper"
RSpec.describe Download, type: :model do
RSpec.describe WarpEngine::Download, type: :model do
it { should validate_presence_of(:file_path) }
it { should belong_to(:release).optional }
end
+6 -6
View File
@@ -1,6 +1,6 @@
require "rails_helper"
RSpec.describe PlatformLink, type: :model do
RSpec.describe WarpEngine::PlatformLink, type: :model do
describe "validations" do
it { should validate_presence_of(:name) }
it { should validate_presence_of(:url) }
@@ -13,7 +13,7 @@ RSpec.describe PlatformLink, type: :model do
end
it "accepts valid platforms" do
PlatformLink::SUPPORTED_PLATFORMS.each do |p|
WarpEngine::PlatformLink::SUPPORTED_PLATFORMS.each do |p|
link = build(:platform_link, platform: p)
expect(link).to be_valid
end
@@ -25,14 +25,14 @@ RSpec.describe PlatformLink, type: :model do
active = create(:platform_link)
create(:platform_link, deleted_at: Time.current)
expect(PlatformLink.all).to eq([active])
expect(WarpEngine::PlatformLink.all).to eq([active])
end
it "orders by position" do
second = create(:platform_link, position: 2)
first = create(:platform_link, position: 1)
expect(PlatformLink.all).to eq([first, second])
expect(WarpEngine::PlatformLink.all).to eq([first, second])
end
end
@@ -41,12 +41,12 @@ RSpec.describe PlatformLink, type: :model do
tic80_link = create(:platform_link, platform: "tic80")
create(:platform_link, platform: "love")
result = PlatformLink.for_platform("tic80")
result = WarpEngine::PlatformLink.for_platform("tic80")
expect(result).to eq([tic80_link])
end
it "returns empty array for platform without links" do
expect(PlatformLink.for_platform("godot")).to eq([])
expect(WarpEngine::PlatformLink.for_platform("godot")).to eq([])
end
end
end
+1 -1
View File
@@ -1,6 +1,6 @@
require "rails_helper"
RSpec.describe Release, type: :model do
RSpec.describe WarpEngine::Release, type: :model do
it { should belong_to(:software) }
it { should have_many(:downloads) }
+2 -2
View File
@@ -1,6 +1,6 @@
require "rails_helper"
RSpec.describe Software, type: :model do
RSpec.describe WarpEngine::Software, type: :model do
subject { build(:software) }
it { should validate_presence_of(:name) }
@@ -19,7 +19,7 @@ RSpec.describe Software, type: :model do
active = create(:software)
create(:software, deleted_at: Time.current)
expect(Software.all).to eq([active])
expect(WarpEngine::Software.all).to eq([active])
end
end
end
@@ -14,12 +14,12 @@ RSpec.describe BuildsService do
expect(result[:platforms]["c64"][:kinds]).to eq(["cartridge"])
end
it "returns allKinds matching ReleaseAsset::KINDS" do
expect(result[:allKinds]).to eq(ReleaseAsset::KINDS)
it "returns allKinds matching WarpEngine::ReleaseAsset::KINDS" do
expect(result[:allKinds]).to eq(WarpEngine::ReleaseAsset::KINDS)
end
it "includes all supported platforms" do
PlatformLink::SUPPORTED_PLATFORMS.each do |platform|
WarpEngine::PlatformLink::SUPPORTED_PLATFORMS.each do |platform|
expect(result[:platforms]).to have_key(platform)
end
end
@@ -30,8 +30,8 @@ RSpec.describe BuildsService do
let!(:release) { create(:release, software: software, version: "1.0.0") }
before do
ReleaseAsset.create!(release: release, kind: "html", path: "/test/html")
ReleaseAsset.create!(release: release, kind: "win_x64", path: "/test/win")
WarpEngine::ReleaseAsset.create!(release: release, kind: "html", path: "/test/html")
WarpEngine::ReleaseAsset.create!(release: release, kind: "win_x64", path: "/test/win")
end
it "returns expected and actual kinds per release" do
@@ -25,12 +25,12 @@ RSpec.describe SoftwareUpdater::Tic80Service do
after do
FileUtils.remove_entry(tmpdir)
Software.unscoped.where(name: name).each do |sw|
Release.unscoped.where(software_id: sw.id).each do |r|
ReleaseAsset.unscoped.where(release_id: r.id).delete_all
WarpEngine::Software.unscoped.where(name: name).each do |sw|
WarpEngine::Release.unscoped.where(software_id: sw.id).each do |r|
WarpEngine::ReleaseAsset.unscoped.where(release_id: r.id).delete_all
r.delete
end
ExternalLink.unscoped.where(software_id: sw.id).delete_all
WarpEngine::ExternalLink.unscoped.where(software_id: sw.id).delete_all
sw.delete
end
end
@@ -44,7 +44,7 @@ RSpec.describe SoftwareUpdater::Tic80Service do
end
def asset_kinds(release)
ReleaseAsset.unscoped.where(release_id: release.id).pluck(:kind)
WarpEngine::ReleaseAsset.unscoped.where(release_id: release.id).pluck(:kind)
end
it "registers the release without a docs zip" do