Git Product home page Git Product logo

compel's Introduction

Compel

Code Climate Test 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 to have validations before any business logic execution and to build a easy way coerce and validate params on Sinatra.

The schema builder is based on Joi.

Usage

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:

=> <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:

. [1, 2, 3]
. [{ a: 1, b: 2}
. { a: 3, b: 4 }]

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

Compel#hash

Methods:

  • keys(``schema_hash``)

Examples:

. { 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:

. "{\"a\":1,\"b\":2,\"c\":3}"

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

Compel#boolean

Examples:

. 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.

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:

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:

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

compel's People

Contributors

joaquimadraz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

groyoh

compel's Issues

[enhancement] shortcut request

@joaquimadraz first of all thanks for building this gem ;)

From the readme it seems that the only way to build a "schema" is through the deep method ".keys"

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')
  })
})

Because in the future I'll probably forget that to build a schema I'll have to access a ".hash" method that in turn has a ".keys" method, I propose a ".schema" shortcut method that does the same.

This is a bias proposal, in the future I will still remember that ".schema" builds a schema but this might not be true for other people.

Does the following proposal:

schema = Compel.schema({
  first_name: Compel.string.required,
  ...
})

makes sense to anyone else?

Edit: I'm not suggesting the removal of ".hash.keys", just a alias method that does the same.

require key X if other key Y is sent with value Z

something like this:

Compel.hash.keys({
  y: Compel.string.in('A', 'B', 'Z'),
  x: Compel.string.in('D', 'E', 'F').required.if(:y, 'Z')
})

and with a lambda:

Compel.hash.keys({
  y: Compel.string.in('A', 'B', 'Z'),
  x: Compel.string.in('D', 'E', 'F').required.if(->(obj){ obj[:y] == 'Z' })
})

How to run custom validation against DB?

Hi @joaquimadraz ,

I was looking for hash validation library and even started building one from scratch. Coming from JS background I was looking for something that validate Hash, most of libraries I found only works with Class. This library looks great and thanks for that!

One question, as part of my validation I want to be able to validate unique fields in database.
For example I want to validate that param "email" is a valid email address and also is unique in "users" table/collection.

The reason I want it in the same library is because I want to return the validation errors to the client all in the same format of:

{
  "errors": {
    "name": [
      "name is required"
    ],
    "email": [
      "email must be valid email address",
      "email must be unique"
    ]
  }
}

Is it possible to add custom validation rules?

Thanks,
Ran.

Validate individual elements of an array of hashes depending on another key of the hash

Hi! first of all, thanks for this amazing gem, it's the best gem I could find to validate params in Sinatra in a clear and succinct way.
I was wondering if it's possible to validate the hash elements of an array depending on the type of the element. It's similar to issue #6. Example:

[{
  type: 'A',
  attribute1: 'random1'
}, 
{
  type: 'A',
  attribute1: 'random1'
}, 
{
  type: 'A',
  attribute1: 'random1'
}, 
{
  type: 'B',
  attribute2: 'random2'
}, 
{
  type: 'B',
  attribute2: 'random2'
}]

I would like to validate the elements individually depending on the type:

  • keys of type A:
    • min_length(3).max_length(3), that is, count of element of type A is equal 3
    • attribute1 required
  • keys of type B:
    • min_length(2).max_length(2), that is, count of element of type B is equal 2
    • attribute2 required

Do you think it's possible with the current implementation? the if condition just lets me put a condition on the current value but I don't have access to the parent object to check the type key. Alternatively, I can do a find_all by type and validate them by type but it's more verbose.

Thanks

requesting ruby 1.9.3 compatibility

I made RestMyCase start using Compel and now my tests brake when using ruby 1.9.3.

If this is a too big of a change, don't worry about it but if not, please consider supporting ruby 1.9.3

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.