2

Each question has an array of tags.

schema.rb:

create_table "questions", force: true do |t|
  t.text     "tags", default: [], array: true

How to atomically append to tags?

How to prevent dups within the array?

I tried question.update_attribute tags: tags << :ruby, but this doesn't work.

Rails 4.17 and Postgres.

EDIT: This seems to have been fixed in Rails 4.2

B Seven
  • 44,484
  • 66
  • 240
  • 385

1 Answers1

2

I don't think update_attribute is going to be useful as it will replace the array with the new value rather than append to it (but see better explanation below in --Update-- section).

I'm not sure what best practices are here, but this should work to just add something if it isn't already there:

question.tags << :ruby unless question.tags.include?(:ruby)
question.save

I would write a custom method on the Question model to manage adding tags and checking for uniqueness:

def add_tag(tag)
  if tags.include?(tag)
    # do whatever you want to do when tag isn't unique
    p "#{tag} is already in tags!"
  else
    tags << tag
    save
  end
end

Then call it with question.add_tag(:ruby).

-------------------- Update -----------------------

If this doesn't work, there could be an issue with ActiveRecord not recognizing the field has changed (although it seems to work OK in Rails 4.2).

These links explain the issue:

New data not persisting to Rails array column on Postgres

http://paweljaniak.co.za/2013/07/28/rails-4-and-postgres-arrays/

As they explain, you can use update_attribute here, but you need to replace the entire array rather than push a value onto it, like this:

question.update_attribute(:tags, question.tags << tag)

You should also be able to force ActiveRecord to consider the attribute updated by including will_change! like this:

def add_tag(tag)
  if tags.include?(tag)
    # do whatever you want to do when tag isn't unique
    p "#{tag} is already in tags!"
  else
    tags_will_change!
    tags << tag
    save
  end
end
Community
  • 1
  • 1
Joseph
  • 760
  • 1
  • 4
  • 9
  • I actually wrote this method. But it doesn't work for some reason. Apparently ActiveRecord can't update an array field from within the model. However, it does work from the controller. – B Seven Jun 16 '15 at 16:45
  • Hmm, not sure what the trouble is -- were you calling it on a question object or the model class? I did a quick test in rails console on an object and it seemed to work. The use case I was picturing was an instance method on the model that is called in the controller like: `question = Question.find(params[:id]` then something like `question.add_tag(params[:tag])`. – Joseph Jun 16 '15 at 17:09
  • I called it on the question object. Did `question.add_tag` work for you? I guess there could be something else going on because the code in `add_tag` works in the controller, but not when calling `add_tag` on the object. It is strange because updating other fields works when calling `add_tag` on the object. – B Seven Jun 16 '15 at 18:13
  • Another strange thing is that the `save` method returns true, but the record is not updated. So I think there is an issue with ActiveRecord and array fields. – B Seven Jun 16 '15 at 18:14
  • That does sound strange. I was able to get it working both in the console and by calling through a controller. I'm using Rails 4.2/postgres 9.3.5. Schema for tags is: `t.text "tags", default: [], array: true`. Not sure what's going on. If you want to post the updated model/controller code, I'll take a look and see if it is different from mine. – Joseph Jun 16 '15 at 20:00
  • 1
    I have a Rack app, instead of Rails. So that is one possible cause. `self.tags << tag; save` and will not update the tags field. It will update other fields. – B Seven Jun 16 '15 at 21:48
  • Interesting... I did a little more research, and now I'm surprised my way is working for me. Possibly it's a recent Rails or ActiveRecord update, but I couldn't find it. Anyway, check out these links for different approaches: http://stackoverflow.com/questions/17283854/new-data-not-persisting-to-rails-array-column-on-postgres or this one: http://paweljaniak.co.za/2013/07/28/rails-4-and-postgres-arrays/ -- will add an edit to the answer as well. – Joseph Jun 16 '15 at 23:03