Drop the JSON source_config column
Source kinds that need configuring will get their own table keyed by
list_id, and build their source from it with Kind.for(list). Text needs
no configuration.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LG4iASZnHb1yEM1JAASiWn
diff --git a/AGENTS.md b/AGENTS.md
index ada20df..8236972 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -45,7 +45,7 @@ fly.toml — Fly config for the main demo and PR previews
## Domain model
- **User** has many Lists; identified by the login on the `Remote-User` header
-- **List** belongs to a User and has many Items and Axes, and one source (`source_kind` picks a class from `Sources::ALL`, `source_config` configures it) that produces its Items
+- **List** belongs to a User and has many Items and Axes, and one source (`source_kind` picks a class from `Sources::ALL`; a kind that needs configuring keeps it in its own table keyed by `list_id`) that produces its Items
- **Item** belongs to a List; items missing from the latest sync are archived (`archived_at`) and hidden everywhere
- **Axis** belongs to a List, has many Results
- **Result** belongs to an Axis, a winner (Item), and a loser (Item)
diff --git a/db/migrate/004_add_list_sources.rb b/db/migrate/004_add_list_sources.rb
index b2e305b..58c147b 100644
--- a/db/migrate/004_add_list_sources.rb
+++ b/db/migrate/004_add_list_sources.rb
@@ -3,10 +3,11 @@
Sequel.migration do
up do
# Every list gets its items from exactly one source. Lists before this
- # were made from the textarea, so they default to the text source.
+ # were made from the textarea, so they default to the text source. A
+ # kind that needs configuring gets its own table keyed by list_id; text
+ # needs none.
alter_table(:lists) do
add_column :source_kind, String, null: false, default: "text"
- add_column :source_config, String, null: false, default: "{}"
add_column :synced_at, DateTime
end
@@ -35,7 +36,6 @@ Sequel.migration do
alter_table(:lists) do
drop_column :source_kind
- drop_column :source_config
drop_column :synced_at
end
end
diff --git a/lib/rank_king/models.rb b/lib/rank_king/models.rb
index 326f2a6..04537a6 100644
--- a/lib/rank_king/models.rb
+++ b/lib/rank_king/models.rb
@@ -21,12 +21,7 @@ module RankKing
one_to_many :items, class: "RankKing::Item", conditions: { archived_at: nil }
one_to_many :axes, class: "RankKing::Axis"
- def source_config = JSON.parse(super)
- def source_config=(value)
- super(JSON.generate(value))
- end
-
- def source = Sources.fetch(source_kind).new(source_config)
+ def source = Sources.fetch(source_kind).for(self)
end
# @rbs skip
diff --git a/lib/rank_king/sources/text.rb b/lib/rank_king/sources/text.rb
index 8f274a6..aa1fdc8 100644
--- a/lib/rank_king/sources/text.rb
+++ b/lib/rank_king/sources/text.rb
@@ -4,18 +4,15 @@ module RankKing
module Sources
# A push source: the snapshot is pasted text, one item per line.
class Text
- # @rbs @config: Hash[String, untyped]
-
#: () -> String
def self.kind = "text"
#: () -> bool
def self.push? = true
- #: (?Hash[String, untyped] config) -> void
- def initialize(config = {})
- @config = config
- end
+ # Text has no configuration, so every list's text source is the same.
+ #: (List list) -> Text
+ def self.for(list) = new
#: (untyped snapshot) -> Array[Entry]
def entries(snapshot)
diff --git a/sig/rank_king/models.rbs b/sig/rank_king/models.rbs
index 8c08277..a136203 100644
--- a/sig/rank_king/models.rbs
+++ b/sig/rank_king/models.rbs
@@ -17,8 +17,6 @@ module RankKing
def items_dataset: () -> Sequel::Dataset[Item]
def axes_dataset: () -> Sequel::Dataset[Axis]
def source_kind: () -> String
- def source_config: () -> Hash[String, untyped]
- def source_config=: (Hash[String, untyped] value) -> void
def synced_at: () -> Time?
def source: () -> Sources::_Source
end
diff --git a/sig/rank_king/sources.rbs b/sig/rank_king/sources.rbs
index f389c79..a57e9d2 100644
--- a/sig/rank_king/sources.rbs
+++ b/sig/rank_king/sources.rbs
@@ -22,7 +22,8 @@ module RankKing
interface _SourceClass
def kind: () -> String
def push?: () -> bool
- def new: (Hash[String, untyped] config) -> _Source
+ # The source for a list, configured from the kind's own table.
+ def for: (List list) -> _Source
end
end
end