Subvalid
Subvalid decouples your validation logic from your object structure. With Subvalid you can define different validation rules for different contexts. So rather than defining validation on the object, and having it be "objective", you can define it in a separate class - so it's "subjective". (as in Subjective validation).
Subvalid was extracted from a project at Envato which required complex validation logic at each stage of the object's life cycle:
- Users upload videos. The videos are validated to make sure an actual video was uploaded (and not someone's university Powerpoint slides), that framerate is good, resolution and codec is acceptable etc. Failure here would reject the file straight away, and tell the user to try again with a new file.
- Next we generate thumbnails, resized preview videos etc. If anything fails validation here, it's a bug (wrong preview video size etc) - and we want to alert developers.
- After the video is uploaded and processed, users would enter metadata: title, description, tags etc. If that fails - we still want to save the item, but just leave it as "incomplete", and allow the user to come back later and complete it. Once this passes, the item is ready, and we submit it for review to our internal review team.
While ActiveModel::Validations is great if you've got simple validation logic, it doesn't cut it for something complex like this. When you have different validation for the same object at each point in it's life cycle, you need something more flexible.
ActiveModel also hooks in pretty deep into ActiveRecord. It's main use case assume you're just wanting to prevent bad data hitting your database - which isn't necessarily always the case.
We needed something more. So Subvalid was born.
And you can have the best of both worlds. Subvalid can exist alongside ActiveModel. ActiveModel::Validations is great for ensuring data consistency, and you can add it to your model classes as normal - and then write Subvalid validator classes in addition to handle more complex nuanced validation logic. Or do it all in Subvalid - up to you.
Features
- Very simple, consistent API
- Validation logic is defined in separate "Validator" classes completely decoupled from business logic
- Multiple validators can be defined for each piece of data in your system to be executed at different points
- Caller is in control. No magic happening under the hood
- Failing validation does not block saving to the database
- Does not add anything at all to business objects. No including modules, no monkey patching, no object extension. Subvalid assumes POROs, but works with anything. A key design goal is to not pollute the objects being validated at all
- Supports nested validation on nested object structures - and nicely handles nested errors.
Development Status 
Subvalid is extracted from production code in use at Envato. However, it is undergoing early development, and APIs and features are almost certain to be in flux.
Getting Started
Add this line to your application's Gemfile:
gem 'subvalid'
And then execute:
$ bundle
Or install it yourself as:
$ gem install subvalid
Usage
TODO: Write usage instructions here
Contributing
- Fork it ( https://github.com/[my-github-username]/subvalid/fork )
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request