Repository navigation
Handle deleted entities - #2
Merged
Merged
Conversation
A polymorphic flippable cannot carry a foreign key, so the host app can delete the record behind an entity without Flipside noticing. The entities page then passed nil to display_entity, which looked up the registration by nil.class and raised KeyError (key not found: "NilClass"), taking down the page that holds the Remove button. display_entity now also accepts the Flipside::Entity join row, which always knows the flippable type and id, and labels a missing record (or a class that is no longer registered) instead of raising. Passing a record directly still works. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Entities point at their record polymorphically, so the database cannot remove them when the record goes away. Host apps can now include Flipside::Flippable in an entity model to delete its entity rows when a record is destroyed. It is an explicit include rather than something register_entity wires up: registration runs in an initializer before models load, and patching a class from outside would be lost on the next Zeitwerk reload. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Removes entities left behind by records deleted without callbacks, or before Flipside::Flippable was included, and by entity classes that no longer exist. Ids are plucked rather than joined so it also works when a flippable model lives in another database. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
register_role appended to a list, so registering the same class and method again listed the role twice in the UI. Key registered roles by method name so a repeated registration replaces the previous one, which lets registration run again whenever a class is reloaded. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Models can now declare how they are used as entities and roles next to
the code they describe:
class User < ApplicationRecord
include Flipside::Flippable
flipside_entity search_by: :name, display_as: :name
flipside_role :admin?
end
The macros register when the class loads, which in an app that does not
eager load is only once something references it. The initializer
therefore lists the class names in Flipside.flippables, and Flipside
constantizes them before the UI reads the registry. That loads them the
same way whether or not the app eager loads, keeps the gem free of Rails
hooks, and picks up a reloaded class. A macro in an unlisted class, or a
listed class that registers nothing, raises instead of appearing in one
environment only.
flipside_entity now also declares the association that removes a
record's entities on destroy, so including the concern no longer does it
on its own. register_entity and register_role remain for classes the app
cannot edit.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The reported 500 came from rendering the entities page, which the display_entity specs do not reach. Request the page through the Roda app with an entity whose record is gone, and check that it renders with the Remove form. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Flipside::Flippable is now the way to register entities and roles, so the initializer API is deprecated for removal in 1.0. Warnings go through a Flipside deprecator, which a Railtie hands to the host app on Rails 7.1+ so its deprecation settings apply to them. The macros register through add_registered_entity and add_registered_role instead, so only the deprecated entry points warn, and removing them later leaves the macros untouched. The README now documents the macros, registering a class the app cannot edit from to_prepare, and how to move off the deprecated methods. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Rails stores the base class in flippable_type, so an entity for an Admin < User record was looked up as "User" and shown as "User #id" when only Admin calls flipside_entity. Look up the record's class instead, the same key display_flippable uses. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A soft-deleting default scope hid records from the existence check, so prune_orphaned_entities deleted their entities for good and a restored record came back without its features. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
entity.flippable raises NameError when flippable_type no longer names a class, so the entities page still failed for the rows prune_orphaned_entities deletes for that reason. Show them as deleted. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The macros raised unless their class was already listed, but a model referenced from an initializer that runs before config/initializers/flipside.rb loads while the list is still empty, which failed boot for a correctly configured app. The macros now record their class, and load_flippables raises for any that is not listed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
An entity points at its record polymorphically (
flippable_type/flippable_id), so there is no foreign key to remove it when the record is deleted. Such an entity then breaks the feature's entities page:display_entity(entity.flippable)getsniland raises on theregistered_entities.fetch("NilClass")lookup. The page cannot be opened, so the stale entity cannot be removed from the UI either.What
The UI no longer raises on a deleted record
Flipside.display_entityalso accepts aFlipside::Entityrow. A deleted record is shown asUser #42 (deleted), and a record whose class is no longer registered asUser #42. Both keep their remove button.Entities are cleaned up when a record is destroyed
Flipside::Flippableconcern withflipside_entityandflipside_rolemacros, so a model registers itself instead of being registered from an initializer.flipside_entityadds ahas_many :flipside_entities, dependent: :delete_all, so destroying a record removes its entities.Flipside.flippables. Flipside loads the listed classes on demand, so the UI finds them even when the app does not eager load. It raises if a class uses a macro without being listed, or is listed but calls neither macro.Existing orphans can be removed
Flipside.prune_orphaned_entitiesdeletes entities whose record, or whose class, no longer exists, and returns how many it deleted. This covers records deleted without callbacks (delete,delete_all, SQL) and anything left over from before this change.Smaller changes
Flipside.register_entityandFlipside.register_roleare deprecated in favour of the macros and will be removed in 1.0. They still work. In a Rails 7.1+ app the warnings follow the app's own deprecation settings (Rails.application.deprecators[:flipside]).Flipside.flippables, registering a class from another gem throughto_prepare, and upgrading fromregister_*. It also fixes some heading levels.[Unreleased]entry.Upgrading
Nothing breaks.
register_entityandregister_rolekeep working and only warn. To move over:After that, run
Flipside.prune_orphaned_entitiesonce to clear out entities that were orphaned before the upgrade.Tests
spec/flipside/flippable_spec.rb: the macros register, destroying a record removes its entities, bothflippableschecks raise, and a listed class that is not loaded yet gets loaded.spec/flipside/web_spec.rb: the entities page renders a deleted record, with its remove form.spec/flipside_spec.rb:prune_orphaned_entities, the deprecation warnings, a role registered twice, and the newdisplay_entitycases.🤖 Generated with Claude Code