Inconsistent behaviour in plugins between development and production

There is an inconsistency on how add_to_serializer works between development and production. This is due how plugins are applied

https://github.com/discourse/discourse/blob/master/lib/plugin/instance.rb#L600-L610

Given we reload the code in development, every modification we do to a parent serializer, children will pick them up. To reproduce this inconsistency you can execute the code below

add_to_serializer(:basic_user, :created_at) { user.created_at }

If you execute this in development, all descendants of BasicUserSerializer will have the created_at attribute. In production only BasicUserSerializer will have that attribute.

UserNameSerializer._attributes
=> {:id=>:id, :username=>:username, :avatar_template=>:avatar_template, :name=>:name, :title=>:title}
BasicUserSerializer._attributes
=> {:id=>:id, :username=>:username, :avatar_template=>:avatar_template, :created_at=>:created_at}

The problem is due how ActiveModel::Serializer works, and I’m not sure if they want to support this use case, they only get the attributes from the parent once when you call the attributes(*attrs) method.

https://github.com/rails-api/active_model_serializers/blob/0-8-stable/lib/active_model/serializer.rb#L81

Workaround

Here’s a workaround to this problem, this is how I’m dealing with it atm.

Inside a plugin do

after_initialize do
  # Monkey patch ActiveModel::Serializer to allow us
  # reload children serializers attributes after parent is modified
  class ::ActiveModel::Serializer
    # Update _attributes with superclass _attributes
    def self.reload
      self._attributes = _attributes.merge(superclass._attributes)
    end
  end

  add_to_serializer(:basic_user, :created_at) { user.created_at }

  # Reload all children serializers, so they include :created_at
  BasicUserSerializer.descendants.each(&:reload)
end

I saw some PRs from you @sam in the AMS repo, maybe you can take a look at this.

Thanks!

10 Likes

FYI there is an similar topic here Plugin working locally But not in live. Anyway good catch and your workaround looks awesome.

5 Likes

I want to get this fixed in core, the behaviour should be the same between environments, but not sure how to approach it. I opened an issue in the AMS repo to get feedback there too https://github.com/rails-api/active_model_serializers/issues/2254

Any suggestions?

4 Likes