Read more

Virtual attributes for array fields

Henning Koch
November 29, 2010Software engineer at makandra GmbH

When a has_many association basically serves to store a list of associated strings (tags, categories, ...), it can be convenient to represent this association as a string array in the containing model. Here is an example for this pattern from the acts-as-taggable-on Show archive.org snapshot gem:

post = Post.last
p post.tag_list # ['foo', 'bar', 'baz']
post.tag_list = ['bam']
p post.tag_list # ['bam']
Illustration UI/UX Design

UI/UX Design by makandra brand

We make sure that your target audience has the best possible experience with your digital product. You get:

  • Design tailored to your audience
  • Proven processes customized to your needs
  • An expert team of experienced designers
Read more Show archive.org snapshot

This string array tag_list is magical in several ways:

  • It is read from and written to a has_many association
  • It can be manipulated in forms using a single text_field Show archive.org snapshot . Multiple elements will be separated with commas.
  • In order to change its elements, it can be assigned both an array of strings or a comma-separated string
  • It survives form roundtrips in case of validation errors as expected. ActiveRecord has this horrible API where changes to associations are written to the database immediately after calling the setter. Just like regular, flat attributes they will be converted to their underlying associations only if the record is valid and could be saved.

Use the attached Modularity Show archive.org snapshot below to add such a magical array to your model. Besides using the trait, you will need to implement two methods to synchronize the string array with its underlying association:

class Note < ActiveRecord::Base

  include DoesListField[:topic_list]

  private

  def read_topic_list
    topics.collect(&:name)
  end

  def write_topic_list(list)
    topics.delete_all
    for name in list
      topics.create!(:name => name)
    end
  end

end

In case your list holds integer values, you might want to use the :integer option so elements get casted to numbers automagically:

class Note < ActiveRecord::Base
  include DoesListField[:author_list, integer: true]
end
Posted by Henning Koch to makandra dev (2010-11-29 13:42)