3

I'm referring to this blog on how to use postgresql arrays in Rails 4. To be more precise, I'm using Rails 4.1.8.

I would like to enforce that the array should not be empty(should have at least one or more values) wrt the below table definition for the phones attribute.

Setting null: false as done below prevents me from doing a Contacts.create(phones: nil), but Contacts.create(phones: []) saves to the database.

class CreateContacts < ActiveRecord::Migration
  def change
    create_table :contacts do |t|
      t.string :name

      t.text :phones, array: true, null: false

    end
  end
end

What constraint can I use to ensure that Contacts.create(phones: []) raises an error saying that there should be at least one entry within the array?

2
  • Are you looking for an exception from the database when phones is empty or just an ActiveRecord validation? Commented Mar 2, 2015 at 18:51
  • Actually both, until you brought up this question. I don't know the difference between the two currently and when should what be chosen accordingly. I will research more on this. Commented Mar 3, 2015 at 12:26

2 Answers 2

3

A regular presence validation should meet your requirement, since the empty array is not present?.

class Contact < ActiveRecord::Base
  validates :phones, presence: true
end
Sign up to request clarification or add additional context in comments.

1 Comment

Should be, validates :phones, presence: true
2

I suggest you use a serialized attribute instead.

class CreateContacts < ActiveRecord::Migration
  def change
    create_table :contacts do |t|
      t.string :name

      t.text :phones, null: false
    end
  end
end

And in your model:

class Contact < ActiveRecord::Base
  serialize :phones
  validate :phones, presence: true
  before_validation :clean_empty_array

  private

  def clean_empty_array
    self.phones = phones.presence
  end
end

Note that I added a before_validation cleaning method, to turn empty arrays into nil using presence ([].presence => nil; ["1"].presence => ["1"]). As an alternative, you could use attribute_normalizer gem to handle this kind of data normalization if you have this requirement in more places of your application.


EDIT: after discussion in the comments, serialize seems to be not the best way to go, since it carries important caveats such as querying issues. Thanks to @muistooshort for pointing these out. Also, the before_validation is not needed, since a presence: true validation is equivalent to what was proposed in the before_validation.

So the final solution to the question would involve keeping Postgresql array support and just validating against phones presence.

In short: keep array: true in your migration file and add this validation to your model:

class Contact < ActiveRecord::Base
  validate :phones, presence: true
end

21 Comments

I now realise why your answer is actually a better way to go instead using postgresql array support with rails4+ atleast from what I've seen so far and atleast for the above mentioned scenario. To be more precise, what I specifically observed was, if I'm storing an array of integers using the postgre sql array support like [3,4] and later on using them for comparison , I'm getting false, because it compares ["3","4"] with [3,4] . With serialization, this doesn't happen. Thank you very much for your answer.
Glad to help !! Using DB array field support is not really used in the community as far as I know.
The only thing is, I can't still do a check for the array to at least contain one element, I think this is because [ ].nil? in ruby returns false. I see that the record is still saving in the DB without throwing any error, given when a phones attribute is being assigned to an empty array.
A standard presence: true (which AFAIK calls blank?) should work as far as (Rails) validations go. If you want the database to raise an exception then you'd need to manually add a CHECK constraint; I'd do both because data validity and integrity logic really does belong inside the database. I stand by serialize being an ugly hack: it is quick and easy but almost always a big mistake that will make you suffer. Checking equality of YAML-strings can be problematic if the YAML encoder decides to change quotes, spacing, UTF-8 encoding, ... (these changes do happen over time).
+1 for the use of lacsadaisical :) And thanks, I think I actually fully agree with you, I just didn't know why.
|

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.