Rails style subset validation

Do you suddenly wake up in a cold sweat, wondering if there is finally a way to validate that your data is a proper subset that you desire? I too have experienced this horror, and that is why I made a rails style subset validator.

Validations über alles

Data validation is important. Having data you can depend on is the difference between a great application and one that might be classified as: "amateur hour". Rails provides a nice amount of validators via ActiveModel::Validations, but not an exhaustive set (get it?). One of those missing pieces involves validating sets of data. Just in case you haven't had your coffee yet, we define a set as an abstract data type that stores distinct values in no particular order.

Ruby does indeed have the Set class but most code I've come across simply uses the Array class. So, for simplicity's sake, all examples will use Array.

Now that we have all been convinced that subset validation is the greatest thing since sliced bread, how about some examples?

Example usage

To include validates_subset, simply add gem 'validates_subset' in your Gemfile of your projects. Or type gem install validates_subset in your terminal.

For rails applications, the gem is automagically available to you. For other ruby frameworks, just add the line: require 'validates_subset' wherever you need the library. For this, we can assume that our application is using Rails 4.0 and we have a class that looks like:

class HasASubset < ActiveRecord::Base
  attr_accessor :foo

  validates_subset :foo, [1, 2, 3]
end

Using our handy dandy test class, we can see that:

A valid subset is valid:

test = HasASubset.new
test.foo = [1]
test.valid?
# => true

An empty set is also very valid:

test = HasASubset.new
test.foo = []
test.valid?
# => true

An invalid set is... invalid:

test = HasASubset.new
test.foo = ['banana']
test.valid?
# => false

And a non-set is invalid:

test = HasASubset.new
test.foo = 99_999
test.valid?
# => false

Holy guacamole that is some sweet validation. But wait, there's more! Since this validator is built on top of rock solid ActiveModel::Validations logic, all modifiers are supported.

Modification nation

If your particular dataset needs something to be a subset or nil, it is a simple as:

class HasASubset < ActiveRecord::Base
  attr_accessor :foo

  validates_subset :foo, [1, 2, 3], allow_nil: true
end

If you needed the data to allow_nil and only validate on create, you could easily define the validation as:

class HasASubset < ActiveRecord::Base
  attr_accessor :foo

  validates_subset :foo, [1, 2, 3], allow_nil: true, on: :create
end

Any combination of modifiers is supported by validates_subset. For a complete list of modifiers, have a look at the rails documentationn.

As always, please contribute to and use this software for the low low price of free.