# ParamValidation

A standalone and simple ruby hash validation lib, useful for validating the data passed to your functions. 

```rb
new_charge_validation = {
  amount: {
    required: true
  },
  stripe_card_token: {
    required: true,
    format: /^tok_.*$/
  }
}

def update_database(data)
  ParamValidation.new(data, new_charge_validation)
  # do stuff
  return result
end
```

The above checks the `:amount` and `:stripe_card_token` keys inside the `data`
hash and runs validations on them. If a value is invalid, an exception is
thrown. The exception can be handled outside your function call, typically in a
controller/router or in a test suite.

An exception is thrown for each failure.

## ParamValidation::Error

The ParamValidation::Error object has the following information on it:

```rb
begin 
  update_database(data)
rescue ParamValidation::Error => e
  e.message # string validation failure message
  e.val     # value that failed validation
  e.key     # key name of the above value inside the data hash
  e.name    # name of the validator that failed
rescue Exception => e
  # a non-validation exception
end
```

## Using in Rails

To handle validation exceptions from the controller, you can add a custom helper function in your ApplicationController like this:

```rb
def render_json(&block)
  begin
    result = yield block
  rescue ParamValidation::Error => e
    return {status: 422, json: {error: e.message, key: e.key}}
  rescue Error => e # a non-validation related exception
    return {status: 500, json: e}
  end
  return {status: 200, json: result}
end
```

With the above, you can simply call render_json on your validated function call:

```rb
UsersController < ApplicationController
  def update
    render_json{ update_user(params[:user]) }
  end
end
```

```rb
update_validation = {
  email: {
    presence: :optional,
    format: email_regex
  }
}

def update_user(params)
  ParamValidation.new(params, update_validation)
end
```

### built-in validators

- required: value must be non-nil
- absent: value must be nil
- not_included_in: value must not be in array
- included_in: value must be in array
- format: value must match regex
- is_integer: value must look like an integer (can be a string)
- is_float: value must look like a float (can be a string)
- min_length: array value must have length >= min
- max_length: array value must have length <= max
- length_range: array value must have length within given range
- length_equals: array value length must equal given arg
- equals: value must equal given arg
- min: value must be >= arg
- max: value must be <= arg
- in_range: value must be within the given range

### custom validators

ParamValidation.add_validator takes a name and a block. That block performs the
validations and simply returns true/false. The block takes three params: the
actual value to validate, and the argument provided in the validation hash, and
the entire data hash being validated.

```rb
# You can use other validators inside a new validator
ParamValidation.add_validator(:dollars) do |val, arg, data|
  ParamValidation.validators[:format](val, /^\$\d+\.(\d\d)$?, data)
end

# # Validators that don't need an argument typically just get 'true' passed in
# ParamValidation.new(params, { 
#   amount: { dollars: true }
# })

# Other examples

# Uniqueness validation (of course it may be better to use the UNIQUE constraint in sql)
ParamValidation.add_validator(:unique) do |val, arg, data|
  Qx.select("COUNT(*)").from(:table).where(name: val).first['count'] == 0
end
```

### custom validation messages

Within a validation instance, you can set a custom message with the :message key.

```rb
ParamValidation.new(params, {
  amount: {
    dollars: true,
    message: 'Please enter a dollar amount'
  }
})
```

To change the global default message, simply use ParamValidation.set_message(:validation_name, &block).

```rb
ParamValidation.set_message(:dollars) do |h|
  "#{h[:key]} must be in dollars"
end
```

The `set_message` block receives a hash with some data:

* :key  - name of the key in the hash that failed this validation
* :arg  - argument to validator (eg format regex)
* :val  - actual value that failed validation
* :data - entire data hash that is being validated


#### internationalization -- TODO!

Internationalization support is not yet in place; please make a PR for it!