diff --git a/app/views/admin/links/edit.html.erb b/app/views/admin/links/edit.html.erb
index b9db752..bba1e74 100644
--- a/app/views/admin/links/edit.html.erb
+++ b/app/views/admin/links/edit.html.erb
@@ -11,7 +11,7 @@
Edit Link
- <%= render "form", link: @link %>
+ <%= render "form", link: @link, content_module: @link.content_module %>
diff --git a/app/views/admin/links/new.html.erb b/app/views/admin/links/new.html.erb
index 35513db..05b0d88 100644
--- a/app/views/admin/links/new.html.erb
+++ b/app/views/admin/links/new.html.erb
@@ -11,7 +11,7 @@
New Link
- <%= render "form", link: @link %>
+ <%= render "form", link: @link, content_module: @content_module %>
diff --git a/config/routes.rb b/config/routes.rb
index f0aa686..f51fd39 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -24,7 +24,7 @@
end
resources :content_modules, except: [ :show ] do
resource :position, only: :update, module: :content_modules
- resources :links, shallow: true, except: %i[index show]
+ resources :links, shallow: true, except: %i[index show destroy]
resources :contents, shallow: true, only: %i[create destroy] do
resource :position, only: :update, module: :contents
end
diff --git a/db/migrate/20261031181544_remove_content_module_from_links_and_games.rb b/db/migrate/20261031181544_remove_content_module_from_links_and_games.rb
new file mode 100644
index 0000000..51b9ca4
--- /dev/null
+++ b/db/migrate/20261031181544_remove_content_module_from_links_and_games.rb
@@ -0,0 +1,27 @@
+class RemoveContentModuleFromLinksAndGames < ActiveRecord::Migration[8.1]
+ def up
+ remove_reference :links, :content_module, foreign_key: true, index: true
+ remove_column :links, :position
+ remove_reference :games, :content_module, foreign_key: true, index: true
+ end
+
+ def down
+ add_reference :links, :content_module, foreign_key: true, index: true
+ add_column :links, :position, :integer
+ add_reference :games, :content_module, foreign_key: true, index: true
+
+ execute <<~SQL
+ UPDATE links SET
+ content_module_id = (SELECT content_module_id FROM contents WHERE contentable_type = 'Link' AND contentable_id = links.id),
+ position = (SELECT position FROM contents WHERE contentable_type = 'Link' AND contentable_id = links.id)
+ SQL
+
+ # A game can be in many modules, but the old column held one, so pick the lowest content id.
+ execute <<~SQL
+ UPDATE games SET
+ content_module_id = (SELECT content_module_id FROM contents WHERE contentable_type = 'Game' AND contentable_id = games.id ORDER BY id LIMIT 1)
+ SQL
+
+ change_column_null :links, :content_module_id, false
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 33607c6..fc75edd 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.1].define(version: 2026_10_31_181034) do
+ActiveRecord::Schema[8.1].define(version: 2026_10_31_181544) do
create_table "classroom_modules", force: :cascade do |t|
t.integer "classroom_program_id", null: false
t.integer "content_module_id", null: false
@@ -84,25 +84,20 @@
end
create_table "games", force: :cascade do |t|
- t.integer "content_module_id"
t.datetime "created_at", null: false
t.text "description"
t.string "slug", null: false
t.string "title", null: false
t.datetime "updated_at", null: false
- t.index ["content_module_id"], name: "index_games_on_content_module_id"
t.index ["slug"], name: "index_games_on_slug", unique: true
end
create_table "links", force: :cascade do |t|
- t.integer "content_module_id", null: false
t.datetime "created_at", null: false
t.string "link_type", null: false
- t.integer "position"
t.string "title", null: false
t.datetime "updated_at", null: false
t.string "url", null: false
- t.index ["content_module_id"], name: "index_links_on_content_module_id"
end
create_table "programs", force: :cascade do |t|
@@ -175,8 +170,6 @@
add_foreign_key "contents", "content_modules"
add_foreign_key "game_attempts", "games"
add_foreign_key "game_attempts", "students"
- add_foreign_key "games", "content_modules"
- add_foreign_key "links", "content_modules"
add_foreign_key "sessions", "users"
add_foreign_key "student_sessions", "students"
add_foreign_key "students", "classrooms"
diff --git a/db/seeds.rb b/db/seeds.rb
index 5b7b201..4c325cf 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -20,9 +20,8 @@ def maybe(&block)
programs.each do |program|
2.times do |i|
mod = program.content_modules.create!(name: "#{program.name} #{level.humanize} Module #{i + 1}", level: level)
- # TODO: Update this when the delegated types migration is completed
- link = Link.new(title: "Survey #{i + 1}", url: "https://example.com/survey/#{program.id}-#{level}-#{i}", link_type: "survey", position: 1, content_module: mod)
- mod.contents.create! contentable: link
+ link = Link.new(title: "Survey #{i + 1}", url: "https://example.com/survey/#{program.id}-#{level}-#{i}", link_type: "survey")
+ mod.contents.create!(contentable: link)
end
end
end
diff --git a/test/controllers/admin/links_controller_test.rb b/test/controllers/admin/links_controller_test.rb
index bf41aa1..2abe8dd 100644
--- a/test/controllers/admin/links_controller_test.rb
+++ b/test/controllers/admin/links_controller_test.rb
@@ -21,8 +21,17 @@ class Admin::LinksControllerTest < ActionDispatch::IntegrationTest
assert_redirected_to edit_admin_content_module_url(@mod)
end
+ test "should create a content in the module along with the link" do
+ assert_difference -> { @mod.contents.count }, 1 do
+ post admin_content_module_links_url(@mod), params: {
+ link: { title: "New Survey", url: "https://example.com/new", link_type: "survey" }
+ }
+ end
+ assert_equal "New Survey", @mod.contents.last.contentable.title
+ end
+
test "should not create with missing title" do
- assert_no_difference "Link.count" do
+ assert_no_difference [ "Link.count", "Content.count" ] do
post admin_content_module_links_url(@mod), params: {
link: { title: "", url: "https://example.com", link_type: "survey" }
}
@@ -40,11 +49,4 @@ class Admin::LinksControllerTest < ActionDispatch::IntegrationTest
assert_redirected_to edit_admin_content_module_url(@mod)
assert_equal "Updated Title", @link.reload.title
end
-
- test "should destroy link" do
- assert_difference "Link.count", -1 do
- delete admin_link_url(@link)
- end
- assert_redirected_to edit_admin_content_module_url(@mod)
- end
end
diff --git a/test/fixtures/games.yml b/test/fixtures/games.yml
index bd0ba0c..14779e4 100644
--- a/test/fixtures/games.yml
+++ b/test/fixtures/games.yml
@@ -3,11 +3,9 @@
one:
title: Game 1
slug: game-1
- content_module: one
description: MyText
two:
title: Game 2
slug: game-2
- content_module: one
description: MyText
diff --git a/test/fixtures/links.yml b/test/fixtures/links.yml
index c6fce72..e6b2526 100644
--- a/test/fixtures/links.yml
+++ b/test/fixtures/links.yml
@@ -1,6 +1,4 @@
one:
- content_module: one
title: Health Survey
url: https://example.com/survey/1
link_type: survey
- position: 1
diff --git a/test/models/content_module_test.rb b/test/models/content_module_test.rb
index 4c44fd7..20dfc12 100644
--- a/test/models/content_module_test.rb
+++ b/test/models/content_module_test.rb
@@ -28,19 +28,14 @@ class ContentModuleTest < ActiveSupport::TestCase
test "destroys associated links" do
content_module = ContentModule.create!(program: @program, level: "basic", name: "With Links")
- link = Link.create!(content_module: content_module, title: "A Link", url: "https://example.com", link_type: "survey")
- Content.create!(content_module: content_module, contentable: link)
+ link = Link.new(title: "A Link", url: "https://example.com", link_type: "survey")
+ content_module.contents.create!(contentable: link)
assert_difference "Link.count", -1 do
content_module.destroy!
end
end
- test "exposes delegated contentables" do
- assert_equal [ links(:one) ], content_modules(:one).links.to_a
- assert_equal [ games(:one) ], content_modules(:one).games.to_a
- end
-
test "returns contents ordered by position" do
contents(:one).update!(position: 2)
contents(:two).update!(position: 1)
diff --git a/test/models/game_test.rb b/test/models/game_test.rb
index ed2f92d..fb30204 100644
--- a/test/models/game_test.rb
+++ b/test/models/game_test.rb
@@ -2,19 +2,18 @@
class GameTest < ActiveSupport::TestCase
test "should not save game without title" do
- game = Game.new(slug: "test-slug", content_module: ContentModule.new)
+ game = Game.new(slug: "test-slug")
assert_not game.save, "Saved the game without a title"
end
test "should not save game without slug" do
- game = Game.new(title: "Test Game", content_module: ContentModule.new)
+ game = Game.new(title: "Test Game")
assert_not game.save, "Saved the game without a slug"
end
test "should not save game with duplicate title" do
- content_module = ContentModule.create!(program: programs(:one), level: "basic", name: "Test Module")
- Game.create!(title: "Test Game", slug: "test-slug", content_module: content_module)
- duplicate_game = Game.new(title: "Test Game", slug: "another-slug", content_module: content_module)
+ Game.create!(title: "Test Game", slug: "test-slug")
+ duplicate_game = Game.new(title: "Test Game", slug: "another-slug")
assert_not duplicate_game.save, "Saved the game with a duplicate title"
end
@@ -28,10 +27,16 @@ class GameTest < ActiveSupport::TestCase
assert_empty Content.where(contentable: games(:one))
end
+ test "not_in excludes games attached to the module and includes unattached games" do
+ available = Game.not_in(content_modules(:one))
+
+ assert_not_includes available, games(:one)
+ assert_includes available, games(:two)
+ end
+
test "should not save game with duplicate slug" do
- content_module = ContentModule.create!(program: programs(:one), level: "basic", name: "Test Module")
- Game.create!(title: "Test Game", slug: "test-slug", content_module: content_module)
- duplicate_game = Game.new(title: "Another Game", slug: "test-slug", content_module: content_module)
+ Game.create!(title: "Test Game", slug: "test-slug")
+ duplicate_game = Game.new(title: "Another Game", slug: "test-slug")
assert_not duplicate_game.save, "Saved the game with a duplicate slug"
end
end
diff --git a/test/models/link_test.rb b/test/models/link_test.rb
index e3f0680..f357244 100644
--- a/test/models/link_test.rb
+++ b/test/models/link_test.rb
@@ -2,18 +2,18 @@
class LinkTest < ActiveSupport::TestCase
test "is valid with required fields" do
- link = Link.new(content_module: content_modules(:one), title: "My Link", url: "https://example.com", link_type: "survey")
+ link = Link.new(title: "My Link", url: "https://example.com", link_type: "survey")
assert link.valid?
end
test "is invalid without a title" do
- link = Link.new(content_module: content_modules(:one), url: "https://example.com", link_type: "survey")
+ link = Link.new(url: "https://example.com", link_type: "survey")
assert_not link.valid?
assert_includes link.errors[:title], "can't be blank"
end
test "is invalid without a url" do
- link = Link.new(content_module: content_modules(:one), title: "My Link", link_type: "survey")
+ link = Link.new(title: "My Link", link_type: "survey")
assert_not link.valid?
assert_includes link.errors[:url], "can't be blank"
end
@@ -27,8 +27,12 @@ class LinkTest < ActiveSupport::TestCase
assert_nil Content.find_by(id: content_id)
end
+ test "finds its content module through its content" do
+ assert_equal content_modules(:one), links(:one).content_module
+ end
+
test "is invalid without a link_type" do
- link = Link.new(content_module: content_modules(:one), title: "My Link", url: "https://example.com")
+ link = Link.new(title: "My Link", url: "https://example.com")
assert_not link.valid?
end
end