A pragmatic approach to validation in Rails inspired by Laravel's Validator.
Install the gem and add to the application's Gemfile by executing:
$ bundle add mini_defender
If bundler is not being used to manage dependencies, install the gem by executing:
$ gem install mini_defender
Mini Defender allows the developer to quickly and easily validate incoming requests data as the verify first step in the request, alongside this Mini Defender also provides the following benefits:
- Concise and easy to write validation logic
- Validate complex and nested data structures
- More than 80 validation rules available
- Custom validation rules
The easiest way to use Mini Defender is to include the concern MiniDefender::ValidatesInput
to your
ApplicationController
as follows:
class ApplicationController < ActionController::Base
include MiniDefender::ValidatesInput
end
Now you will have access to a method called validate!
which will accept a hash of keys and their respective rules.
# frozen_string_literal: true
class BooksController < ActionController::Base
def create
Book.create!(book_params)
end
private
def book_params
# Optional if you need easy access to rule definitions
rules = MiniDefender::Rules
validate!({
'name' => 'string|required|max:255',
'type' => ['string', rules::In.new(Book::TYPES)],
'tags' => 'array',
'tags.*' => 'required|string|max:255',
'pages' => 'required|integer',
'author' => 'required|hash',
'author.name' => 'required|string',
'author.email' => 'email',
}, true)
end
end
The validate!
method accepts two arguments, the first is a hash of keys and rules and the second a boolean to indicate
if you want the values to be coerced to the type indicated by rule, i.e. integer
.
Suppose we passed the following input to our app:
{
"name": "The Story of Some Guy",
"type": "Biography",
"tags": ["inspiring", "business"],
"pages": "200",
"author": {
"name": "Some Guy himself",
"random_field": "hello i can haz hakc?"
}
}
Notice that we have passed a string "200"
to page instead of 200
and added an extra field to author
called random_field
.
We didn't also provide an email
field.
The validation will pass, since email
is not optional, and we will get the following Hash
as a result:
{
'name' => 'The Story of Some Guy',
'type' => 'Biography',
'tags' => %w[inspiring business],
'pages' => 200,
'author' => {
'name' => 'Some Guy himself'
}
}
You can see that pages
has the value converted to 200
(integer), since we chose to coerce by passing true
to validate!
.
You can also see that Mini Defender left out the key random_field
as it was not a part of our validations.
When the validation fails, an error of type MiniDefender::ValidationError
will be raised.
You can either handle the error using rescue
or add a global rescue_from
to handle the errors throughout the application.
The validate!
method is actually the following four lines of code:
def validate!(rules, coerced = false)
data = params.to_unsafe_hash.deep_stringify_keys
validator = MiniDefender::Validator.new(rules, data)
validator.validate!
coerced ? validator.coerced : validator.data
end
You can use Mini Defender out side of the controller by creating a new instance of MiniDefender::Validator
and calling
any of the methods on it, see validator.rb for the API.
Mini Defender tries to implement the same set of rules provided by Laravel, you can see the available rules here at lib/mini_defender/rules
To implement your custom rules, you need to create a new class that inherent from MiniDefender::Rule
and implement
at least the following:
class MyAwesomeRule < MiniDefender::Rule
def self.signature
'all_caps'
end
def passes?(attribute, value, validator)
value.is_a?(String) && /^[A-Z]+$/.match?(value)
end
def message(attribute, value, validator)
'ONLY CAPS ALLOWED!!!'
end
end
After creating the class, you can register it as follows:
MiniDefender::RulesFactory.register(MyAwesomeRule)
Bug reports and pull requests are welcome on GitHub at https://github.com/ahoshaiyan/mini_defender.
The gem is available as open source under the terms of the MIT License.