4

Weirdness:

I'm working in rails 4.1 with a postgres backend. One of my models has some array fields, which I'm hoping are properly handled by the 'pg' gem (0.17.1). My migration file and the resulting schema can be found below.

The field in question is a string array. Below, I've posted the migration, the resulting schema, and the code that doesn't seem to be working (along with output when this code is run in the rails console).

I don't have anything in the User model that references this field.

migration:

add_column :users, :test, :string, :array => true, default: '{}'

schema:

t.string   "test",     default: [], array: true

Here are the steps I'm using to to reproduce this in the rails console (assuming a basic user model has been created to hold the field):

u = User.first()
u.test.push('testValue')

u # => #<User id: 1, name: "dave", passhash: nil, email: nil, created_at: "2014-10-04 10:12:29", updated_at: "2014-10-04 10:12:29", provider: "identity", uid: "1", oauth_token: nil, oauth_expires_at: nil, test: ["testValue"]>

u.save
User.find(1).test # => []

So the user record seems to be initialized properly -- it has an empty array set for that field -- and I can do array operations on it. But somehow I can't get any modifications to stick.

FYI, I am able to modify and save other fields using the same procedure; it's just the arrays that are behaving like this.

Any ideas?

Edit:

u.test.push('newitem')returns the new array as I would expect. No sign of anything going wrong, or some error with validates_uniqueness_of -- would I see that firing in the console? Thanks to the first comment, I've now tested using 'update':

u.update(test: [(u.test << 'testValue')].flatten)

gives me the behavior I want, but it seems really clumsy. Is there something I need to change in the model? I'm not sure how what I'm doing makes validates_uniqueness_of fire (it's not set in the model).

4
  • I assume it is failing on validate_uniqueness_of, have you tried u.update instead of u.save? Commented Oct 4, 2014 at 11:14
  • How can I see the error that's preventing it from actually saving? u.test.push('newitem') returns the new array as I would expect. No sign of anything going wrong. Commented Oct 4, 2014 at 11:46
  • You can see errors with u.errors.messages or u.errors.full_messages. You should read Active Record Validations — Ruby on Rails Guides - 7 Working with Validation Errors for more explanations. Commented Oct 4, 2014 at 13:13
  • Thanks, I did this and tried editing my comment, but the edit limit was 5 minutes. Those collections are all empty; no errors returned. Commented Oct 4, 2014 at 13:24

1 Answer 1

6

There is (at least it used to be) a problem with using ruby array operators like push or << on ActiveRecord fields. They are not marked as dirty and are not updated in the database on save. Try this:

u = User.first()
u.test_will_change!
u.test.push('testValue')
u.save
User.find(1).test

attribute_will_change! marks attribute as dirty, making sure that it will be serialized and saved into the database on save.

Sign up to request clarification or add additional context in comments.

2 Comments

That's exactly what I needed! Thanks so much. Any ideas where I can read up on the rationale behind why this isn't supported? I feel like I must be missing some background knowledge.
This is wider a problem with mutable objects. You can alter them in-place and using many different methods. Normally ActiveRecord sets attribute_name= setter and when it's called it marks the attribute as dirty. It is cumbersome with mutable objects, as you may change them in so many ways... Before Rails 4 (IIRC) when you used serialization it was always serialized and saved, even if unchanged, which caused a big overhead. When native support for hstore and arrays came, they decided to make only = assignment making automatically dirty, but somehow forgot to document it properly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.