Dev and stress seeds for MishkaBlog resources.
"Archived" is seeded exactly one way: Ash.destroy!/2 through a resource's :destroy action, in
the very last pass of a run. That writes the archived_at soft-delete timestamp AND force-sets
status: :archived on BlogPost and Collection, the two resources whose status enum carries
that value.
Nothing else ever writes status: :archived. The status pool in MishkaBlog.Dev.Generator leaves
it out, and the seeds no longer mark live rows with the label: only archived_at decides which tab
a row appears in, so a labelled-but-live post reads to an admin as an archived post sitting in All
Posts. Seeded data therefore satisfies status :archived if and only if archived_at is set,
which MishkaBlog.Dev.SeedsArchivalTest pins.
Comment, Tag, Blogger, Team and AutoPosting have no :archived status at all: for them
the soft delete is the only archival concept, and their destroy actions leave the status untouched
— an archived :spam comment keeps its :spam label.
Receives foundation data from MishkaCmsCore.Dev.Seeds.dev_seed/0.
Summary
Functions
Executes the plan, children before parents, and reads one row back through :get_archived to prove
the rows really moved to the Archived tab.
Chooses the rows that will be soft-deleted, without touching the database.
Spreads creation timestamps over the last 18 months.
Creates realistic blog seed data for a single site: multilingual posts, threaded comments, views, engagement rows and, as the final pass, a soft-deleted minority that populates the admin's Archived tab.
Fills the engagement tables the admin aggregates over — ratings, bookmarks, comment votes, blogger and team followers — so the star, bookmark and follower columns are not hard-zero.
Keeps only the users that may legitimately author content on this site: master users (site_id nil)
and tenant users of this very site.
Stress seed: the same building blocks as dev_seed/3 — multilingual posts, views, engagement and
the archival pass — at large volumes, so stress data exercises the code paths the admin renders.
The :posts option is the total post count across all five languages.
Functions
Executes the plan, children before parents, and reads one row back through :get_archived to prove
the rows really moved to the Archived tab.
Ash.destroy!/2 on these resources is a soft delete: AshArchival rewrites the :destroy action to
set archived_at instead of removing the row. :permanent_destroy is the one that really deletes
and is never used here. On BlogPost and Collection the destroy also force-sets status: :archived; on Comment, Tag, Blogger and AutoPosting the status is left alone, so an archived
:spam comment keeps its moderation label.
A team's MishkaBlog.TeamMember rows are left in place: that join resource has no archival, so
destroying them would be a real delete and the membership could not come back with an unarchive.
Nothing may write to a record after this pass — the prevent_archived_*_updates triggers raise on
any update of an archived row.
Chooses the rows that will be soft-deleted, without touching the database.
The slice is picked from a hash of {site_id, record_id, :archive} so the archived set is the same
on every run, and it stays a genuine minority: a soft delete is a full Ash destroy that writes a
PaperTrail version, emits an event and broadcasts over PubSub.
Two rules shape the selection. Language masters are only archived together with their whole group,
since a master with live translations is a state the app declares invalid. And post comments are
archived alongside their post, because no resource declares archive_related — an orphan comment
would render as "On: Unknown".
Spreads creation timestamps over the last 18 months.
inserted_at is a create_timestamp and appears in no accept list, so Ash.Seed.update!/3 is the
only Ash-native way to set it. That call goes straight to the data layer: no action, no validation,
no notifier and no PaperTrail version, which is why a backdated row's inserted_at will not match
the timestamp of its own first version. It also applies update defaults, so updated_at has to be
passed explicitly or it silently resets to now.
This pass must run before archive_pass/2: the prevent_archived_*_updates triggers raise on any
update of a row that already has an archived_at.
Creates realistic blog seed data for a single site: multilingual posts, threaded comments, views, engagement rows and, as the final pass, a soft-deleted minority that populates the admin's Archived tab.
Per-site volumes are derived from a hash of site_id, so a run is reproducible while the three
sites differ from one another. users should include both master and tenant users; users belonging
to another site are filtered out before bloggers and comment authors are picked.
RUN AGAIN ON A SITE THAT HAS A BLOG, this adds no generated row and answers :already_seeded.
Every row it generates has no name to recognise it by afterwards, so what is either there or not
is the blog itself, and a site that has one keeps exactly the posts, comments and ratings it has
rather than being given a second set beside them. The locked and unlisted examples are recognised
by their slugs, so a re-run writes only the ones the site is missing.
Fills the engagement tables the admin aggregates over — ratings, bookmarks, comment votes, blogger and team followers — so the star, bookmark and follower columns are not hard-zero.
MishkaBlog.TeamPermission is not seeded: dev_seed/3 receives only sites and users, and a team
permission needs a MishkaCmsCore.Accounts.Permission record, which the core seed does not hand to
the extension seeds.
Archived posts and comments are skipped, so the aggregates the admin renders describe live rows.
Keeps only the users that may legitimately author content on this site: master users (site_id nil)
and tenant users of this very site.
MishkaCmsCore.Dev.Seeds hands every site the same "master" list, and that list contains
none@example.com, which is created as a tenant user of the localhost site. Without this filter a
blogger or comment author on one site can be another site's user.
Stress seed: the same building blocks as dev_seed/3 — multilingual posts, views, engagement and
the archival pass — at large volumes, so stress data exercises the code paths the admin renders.
The :posts option is the total post count across all five languages.