Rails Essentials: custom fields, adding to serializer, callbacks

See discourse/instance.rb at main · discourse/discourse · GitHub for the plugin-related methods Discourse provides.

GitHub - literatecomputing/discourse-google-group-link is a simple plugin that does most of the plugin basics:

  • adds a custom category field
  • adds it to the serializer
  • adds some stuff to a page
  • uses a callback to make code fire when a topic is created

Custom Field Types

For simple additions to a model, MODEL_custom_fields are the way to go. A few lines of code enable you to add a new field to a model. The Google Group Link plugin adds a custom category field to indicate whether new topics in the category should be scanned for the Google Group name, and a custom topic field to hold the link to the message in Google Groups.

Here’s another example:

register_category_custom_field_type("ratings_list", :string)

Will add a string custom category field (that will be a |-delimited string of custom items (that will be added to topics in that category).

register_topic_custom_field_type("rating_topic_id", :integer)

Will add a field for the topic that this rating is discussing.

These helper methods are defined in instance.rb.

Adding data to the serializer

Not all fields in an object are made available to the front end (or sent out in .json routes), so when we add a field that we want to be made available to the front end, we need to add it to the serializer.

TODO: Insert here a way to find out what the serializers are.

Here is how the Google Group plugin adds the Google Group link to the topic serializer, so that when Rails renders a topic in the serializer, this new field will be a part of it. It is then up to the front end (Ember) to do something with that information, like display it or use it in Javascript to make something else happen.

The above code checks to make sure that there is a value to render to avoid causing an error by trying to render nil. A well-written plugin would include specs that have tests to see that the expected data get added to the serializer and that conditions like nil values do not cause errors.

The example below will add to the category serializer the custom ratings that are to be included in topics created in that category. (I don’t know right now if this will actually get the information where we need it for the plugin that I am currently planning, but that’s only important to me.)

add_to_serializer(:category, :ratings_list) do
  next unless (self.category.custom_fields[RATINGS_LIST]
  value = self.category.custom_fields[RATINGS_LIST]
end

Adding methods to a class

In addition to adding attributes (aka customfields fields) to a model, you can also add methods. Ruby and Rails provide a means to override an existing class, but Discourse provides this helper method to make things easier. This method, called later from a callback, checks if the field has been populated already, then parses the raw text of the message to look for the Google Group link, and if it finds it, stores it in the custom field and saves the record. It includes a rescue to catch and log any errors generated when it runs.

self is the instance of the model being added to. In this example, a variable topic is created to make that more clear, but then the code uses self rather than the more readable topic.

You might also add methods if you need to add some value to a serializer that needs to be computed at the time that a page’s data is rendered.

Adding callbacks

Callbacks make it possible to execute code when something happens (see Active Record Callbacks — Ruby on Rails Guides for details and a list of available callbacks).

This example runs after a new topic is committed. As with the add_to_class helper, self is the instance of the model being modified. Here the category is looked up to gain access to the category custom field defined earlier. We check to make sure that a category was found, which can happen when a PM is created. We might also have used t.archetype to avoid looking for a category when none can exist.

(Next, we break out if this category is not a mailinglist_mirror. @pfaffman is quite confused, as mailinglist_mirror isn’t defined anywhere, and is pretty sure that the code was working when it was deployed.)

The custom method add_google_group_link is used to add the link to the topic_custom_field.