Forwarding the `scope:` down a tree of serializers: just do it, or not?

AI-generated summary

mdoggydog raises a question about the development philosophy around forwarding the scope: parameter—intended to provide permissions context (typically a Guardian)—down a tree of serializers in Discourse. While ApplicationController attempts to ensure Guardian is injected into all serializers, explicit serializer constructions (e.g., XxxxSerializer.new(...)) often require manual scope: ... injection—and not all do. This led to a bug in Chat when BasicUserSerializer began relying on scope, revealing a missing scope forward in a deeper serializer chain. The core question is whether: (1) scope should always be forwarded unless there’s a specific reason not to (and omissions are bugs to fix proactively), or (2) it’s acceptable to fix scope omissions reactively only when they cause actual breakage. The author leans toward the first approach, given the subtle, hard-to-debug nature of such issues.

Serializers can be provided with a scope: parameter intended to provide the permissions-context for the serializer. In the Discourse code, the provided scope is a Guardian, and it looks like every serializer is intended to receive a Guardian as its scope. ApplicationController (the base class of all the controllers) maintains a Guardian and it has multiple mechanisms to try to ensure that Guardian is injected as the scope: for all serializers.

However, not every scenario is not covered by these mechanisms. Whenever a serializer is explictly constructed, e.g., calls to ActiveModel::ArraySerializer.new(...), then the scope needs to be injected manually. Indeed, most instances of XxxxSerializer.new(...) have a scope: scope or scope: guardian in the argument list. But, not all of them.

So, my question: what is the development philosophy here? Is it:

  1. Scope should simply always be forwarded (unless there is a specific reason not to); if someone forgot to do that, it is a bug-waiting-to-happen and should get fixed with an appropriate scope: ... added; OR
  2. Yeah, that is fine; someone can fix those spots as they come up when they actually break something for someone.

In my case, I added a use of scope to BasicUserSerializer… and then was sorely perplexed when I discovered that it broke things in Chat… which I finally realized (after way too much digging) was a result of some serializer buried in a chain XxxSerializer -> YyySerializer -> ... -> BasicUserSerializer not forwarding the scope!

1 Like