joaquimadraz/compel

View on GitHub
README.md

Summary

Maintainability
Test Coverage
Compel
==========================
![](https://travis-ci.org/joaquimadraz/compel.svg)
[![Code Climate](https://codeclimate.com/github/joaquimadraz/compel/badges/gpa.svg)](https://codeclimate.com/github/joaquimadraz/compel)
[![Test Coverage](https://codeclimate.com/github/joaquimadraz/compel/badges/coverage.svg)](https://codeclimate.com/github/joaquimadraz/compel/coverage)

Ruby Object Coercion and Validation

This is a straight forward way to validate any Ruby object: just give an object and the schema.

The motivation was to create an integration for [RestMyCase](https://github.com/goncalvesjoao/rest_my_case) to have validations before any business logic execution and to build a easy way coerce and validate params on [Sinatra](https://github.com/sinatra/sinatra).

The schema builder is based on [Joi](https://github.com/hapijs/joi).

### Usage

```ruby
object = {
  first_name: 'Joaquim',
  birth_date: '1989-0',
  address: {
    line_one: 'Lisboa',
    post_code: '1100',
    country_code: 'PT'
  }
}

schema = Compel.hash.keys({
  first_name: Compel.string.required,
  last_name: Compel.string.required,
  birth_date: Compel.datetime,
  address: Compel.hash.keys({
    line_one: Compel.string.required,
    line_two: Compel.string.default('-'),
    post_code: Compel.string.format(/^\d{4}-\d{3}$/).required,
    country_code: Compel.string.in(['PT', 'GB']).default('PT')
  })
})

Compel.run(object, schema) # or schema.validate(object)
```

Will return a `Compel::Result` object:

```ruby
=> <Compel::Result
  @errors={
    "last_name" => ["is required"],
    "birth_date" => ["'1989-0' is not a parsable datetime with format: %FT%T"],
    "address" => {
      "post_code" => ["must match format ^\\d{4}-\\d{3}$"]
    }
  },
  @valid=false,
  @value={
    "first_name" => "Joaquim",
    "birth_date" => "1989-0",
    "address" => {
      "line_one" => "Lisboa",
      "post_code" => "1100",
      "country_code" => "PT",
      "line_two" => "-"
    },
    "errors" => {
      "last_name" => ["is required"],
      "birth_date" => ["'1989-0' is not a parsable datetime with format: %FT%T"],
      "address" => {
        "post_code" => ["must match format ^\\d{4}-\\d{3}$"]
      }
    }
  }>
```

There are 4 ways to run validations:

Method  | Behaviour
------------- | -------------
`#run`  | Validates and returns a `Compel::Result` (see below)
`#run!` | Validates and raises `Compel::InvalidObjectError` exception with the coerced params and errors.
`#run?` | Validates and returns true or false.
`schema#validate` | Check below

==========================

### Schema Builder API

#### Compel#any
`Any` referes to any type that is available to coerce with Compel.
Methods `length`, `min_length` and `max_length` turn the object to validate into a `string` to compare the length.

**Methods**:
- `is(``value``)`
- `required`
- `default(``value``)`
- `length(``integer``)`
- `min_length(``integer``)`
- `max_length(``integer``)`
- `if`
  - `if(->(value){ value == 1 })`
  - `if{|value| value == 1 }`
  - `if{:custom_validation} # Check the specs for now, I'm rewriting the docs ;)` 

==========================

#### Compel#array

**Methods**:
- `#items(``schema``)`

**Examples**:
```ruby
. [1, 2, 3]
. [{ a: 1, b: 2}
. { a: 3, b: 4 }]
```

==========================

#### Compel#hash

**Methods**:
- `keys(``schema_hash``)`

**Examples**:
```ruby
. { a: 1,  b: 2, c: 3 }
```

==========================

#### Compel#date

**Methods**:
- `format(``ruby_date_format``)`
- `iso8601`, set format to: `%Y-%m-%d`

==========================

#### Compel#datetime & Compel#time

**Methods**:
- `format(``ruby_date_format``)`
- `iso8601`, set format to: `%FT%T`

==========================

#### Compel#json

**Examples**:
```ruby
. "{\"a\":1,\"b\":2,\"c\":3}"
```

==========================

#### Compel#boolean

**Examples**:
```ruby
. 1/0
. true/false
. 't'/'f'
. 'yes'/'no'
. 'y'/'n'
```

==========================

#### Compel#string

**Methods**:
- `in(``array``)`
- `min(``value``)`
- `max(``value``)`
- `format(``regexp``)`
- `email`
- `url`

==========================

#### Compel#integer

**Methods**:
- `in(``array``)`
- `min(``value``)`
- `max(``value``)`

==========================

#### Compel#float

**Methods**:
- `in(``array``)`
- `min(``value``)`
- `max(``value``)`

==========================

### Schema Validate

For straight forward validations, you can call `#validate` on schema and it will return a `Compel::Result` object.

```ruby
result = Compel.string
               .format(/^\d{4}-\d{3}$/)
               .required
               .validate('1234')

puts result.errors
# => ["must match format ^\\d{4}-\\d{3}$"]
```

#### Compel Result

Simple object that encapsulates a validation result.

Method  | Behaviour
------------- | -------------
`#value`  | the coerced value or the input value is invalid
`#errors` | array of errors is any.
`#valid?` | `true` or `false`
`#raise?` | raises a `Compel::InvalidObjectError` if invalid, otherwise returns `#value`

#### Custom Options

`Custom error message`

Examples:
```ruby
schema = Compel.string.required(message: 'this is really required')

result = schema.validate(nil)

p result.errors
=> ["this is really required"]

schema = Compel.string.is('Hello', message: 'give me an Hello!')

result = schema.validate(nil)

p result.errors
=> ["give me an Hello!"]
```

==========================

### Sinatra Integration

If you want to use with `Sinatra`, here's an example:

```ruby
class App < Sinatra::Base

  set :show_exceptions, false
  set :raise_errors, true

  before do
    content_type :json
  end

  helpers do

    def compel(schema)
      params.merge! Compel.run!(params, Compel.hash.keys(schema))
    end

  end

  error Compel::InvalidObjectError do |exception|
    status 400
    { errors: exception.object[:errors] }.to_json
  end

  configure :development do
    set :show_exceptions, false
    set :raise_errors, true
  end

  post '/api/posts' do
    compel({
      post: Compel.hash.keys({
        title: Compel.string.required,
        body: Compel.string,
        published: Compel.boolean.default(false)
      }).required
    })

    params.to_json
  end

end
```

###Installation

Add this line to your application's Gemfile:

    gem 'compel', '~> 0.5.0'

And then execute:

    $ bundle

### Get in touch

If you have any questions, write an issue or get in touch [@joaquimadraz](https://twitter.com/joaquimadraz)