What is this code add_to_serializer in all these plugins

I have seen this code in discourse_signatures plugin

User.register_custom_field_type('see_signatures', :boolean)
User.register_custom_field_type('signature_url', :text)
User.register_custom_field_type('signature_raw', :text)

if SiteSetting.signatures_enabled then
  add_to_serializer(:post, :user_signature, false) {
    if SiteSetting.signatures_advanced_mode then
      object.user.custom_fields['signature_raw']
    else
      object.user.custom_fields['signature_url']
    end
  }

  # I guess this should be the default @ discourse. PR maybe?
  add_to_serializer(:user, :custom_fields, false) {
    if object.custom_fields == nil then
      {}
    else
      object.custom_fields
    end
  }
end

In discourse national flags plugin

User.register_custom_field_type('nationalflag_iso', :text)

if SiteSetting.nationalflag_enabled then
  byebug;
  add_to_serializer(:post, :user_signature, false) {
    object.user.custom_fields['nationalflag_iso']
  }
  byebug;
  # I guess this should be the default @ discourse. PR maybe?
  add_to_serializer(:user, :custom_fields, false) {
    if object.custom_fields == nil then
      {}
    else
      object.custom_fields
    end
  }
end

I am not able to understand what is add_to_serializer is doing.

I checked in def add_to_serializer

def add_to_serializer(serializer, attr, define_include_method=true, &block)
  klass = "#{serializer.to_s.classify}Serializer".constantize rescue "#{serializer.to_s}Serializer".constantize

  klass.attributes(attr) unless attr.to_s.start_with?("include_")

  klass.send(:define_method, attr, &block)

  return unless define_include_method

  # Don't include serialized methods if the plugin is disabled
  plugin = self
  klass.send(:define_method, "include_#{attr}?") { plugin.enabled? }
end

I understand that User.register_custom_field_type('signature_raw', :text) will register a custom field type to User object. But what is add_to_serializer doing? I am not able to understand, can you explain me in plain english?

2 Likes

It’s essentially adding to the information that Discourse serializes out when sending that model to the client. For example, you may see a request to this endpoint when loading up a topic page:

http://meta.discourse.org/t/2349082394.json

which returns some payload like this:

{
  title: "Some topic title",
  archetype: 'regular',
  category_id: 7,
  post_stream: { posts: [...] },
  posts_count: 15,
  (etc etc.. lots more info about the topic in question)
}

Which is the json representation of a Discourse topic.

If I want to know more about the topic because of additional ‘stuff’ I do in my plugin, I can put in a line like

add_to_serializer :topic, :flag_name do
  object.flag_name # <- NB that 'object' here is the topic being serialized
end

(Also, by default, this method simply sends the specified message to ‘object’, meaning that

add_to_serializer :topic, :flag_name

is the same as

add_to_serializer :topic, :flag_name do
  object.flag_name
end

)

Once I’ve added the method to the serializer, I can end up with JSON output which includes that additional info

{
  title: "Some topic title",
  archetype: 'regular',
  category_id: 7,
  post_stream: { posts: [...] },
  posts_count: 15,
  flag_name: "some flag name"
  ...
}

Also note that you could achieve the same thing by overwriting the TopicSerializer class (which is exactly what the code snippet you’ve provided is doing)

# plugin.rb
TopicSerializer.class_eval do
  attributes :flag_name
  def flag_name
    object.flag_name
  end
end
17 Likes

Hi,
When running the following code,

TopicSerializer.class_eval do
  attributes :flag_name
  def flag_name
    object.flag_name
  end
end

I was getting the error “block in activate!’: uninitialized constant TopicSerializer (NameError)”. I think I have to “import” or “require” the TopicSerializer dependency. Can you please tell me what would be the dependency, as I am not able to figure it out.

1 Like

Add this directly above your code require_dependency 'topic_serializer'

require_dependency 'topic_serializer'
class ::TopicSerializer
  attributes :flag_name
  def flag_name
    object.flag_name
  end
end
1 Like

Hi thanks, but I got the error " `load’: No such file to load – topic_serializer (LoadError)". Does it pull from the app/serializers folder? I checked in that folder, I could find topic_list_serializer.rb, and many other related to topic, but couldnt find topic_serializer.rb

1 Like

Right! Try TopicViewSerializer.

require_dependency 'topic_view_serializer'
class ::TopicViewSerializer
  attributes :flag_name
  def flag_name
    object.flag_name
  end
end
5 Likes

Heyy, I have added a custom field to my category edit box.

When I submit the form, I can see something these fields are submitted in the js console
custom_fields[default_tag][]:good-feedback custom_fields[default_tag][]:one-more-tag

in my main plugin file, I am doing

add_to_serializer(:basic_category, :default_tag){object.custom_fields[“default_tag”]}

My custom fields aren’t getting saved to the db.

does doing:

add_to_serializer(:basic_category, :default_tag, false ){object.custom_fields[“default_tag”]}

now work?

I had solved it that time by adding a function on :before_action event and converted the array to a delimited string and vice versa while retrieving.

I didn’t actually dig into why an array couldn’t be stored as custom field out of the box.

1 Like

So this might streamline the code potentially?

Yes it would if it worked in this case. I’ll try this in sometime and get back to you.

1 Like

Poor you that must’ve been painful at the time :wink:

1 Like

It was a sunday evening and curiosity took over. It was somewhat painful but fun.
It was something that @pfaffman was trying to do.

No pain no gain :wink:

1 Like

haha, so true! …

1 Like

Hello, I have the same issue (custom_fields are submitted in the js console, but are not getting saved to the db) I used the add_to_serializer(:basic_category, :default_tag, false ){object.custom_fields[“default_tag”]} way in my main plugin file.

Would you still reccomend this solution?

1 Like

There are a lot of questions arising here. Custom fields of what first of all?

I’m working on a plugin to automate discourse group syncing with ldap. I added a custom field to Group (ldap_dn) to save the ldap group name in. I’m trying to retrieve the custom_field value from an input field that i added in the membership plugin-outlet and save that to the db to later use it.

To do so i added this to my plugin.rb file

Group.register_custom_field_type(‘ldap_dn’, :text)
Group.preload_custom_fields<< “ldap_dn” if
Group.respond_to? :preloaded_custom_fields

if SiteSetting.groups_sync_enabled then
add_to_serializer(:group_show, :custom_fields, false) {
object.custom_fields
}
end

I’m new to rails and ember so I’m not sure if there is another step I should be doing in order to get the custom_fields saved to the db, or where the problem lies.

1 Like

I think if you add
register_editable_group_custom_field 'field_name'
to your plugin.rb

that should do the job

Note:
Serializer is used to send the data to the client and not receive it.

1 Like

ahh okay! I’m a newbie so this might be a very ‘duh’ question, but to receive the data, do I do so using the api?

1 Like

My solution will give a green flag to your field when it comes through to the server. So its about receiving the data

1 Like