# Dry::Data ![Join the chat at https://gitter.im/dryrb/chat](https://badges.gitter.im/Join%20Chat.svg)
![Gem Version](https://badge.fury.io/rb/dry-data.svg)
![Build Status](https://travis-ci.org/dryrb/dry-data.svg?branch=master)
![Dependency Status](https://gemnasium.com/dryrb/dry-data.svg)
![Code Climate](https://codeclimate.com/github/dryrb/dry-data/badges/gpa.svg)
![Documentation Status](http://inch-ci.org/github/dryrb/dry-data.svg?branch=master&style=flat)
A simple type-system for Ruby respecting ruby's built-in coercion mechanisms.
## Installation
Add this line to your application's Gemfile:
```ruby
gem 'dry-data'
```
And then execute:
$ bundle
Or install it yourself as:
$ gem install dry-data
## Why?
Unlike seemingly similar libraries like virtus, attrio, fast_attrs, attribs etc.
`Dry::Data` provides you an interface to explicitly specify data types you want
to use in your application domain which gives you type-safety and *simple* coercion
mechanism using built-in coercion methods on the kernel.
Main difference is that `Dry::Data` is not designed to handle all kinds of complex
coercions that are typically required when dealing with, let's say, form params
in a web application. Its primary focus is to allow you to specify the exact shape
of the custom application data types to avoid silly bugs that are often hard to debug
(`NoMethodError: undefined method `size' for nil:NilClass` anyone?).
## Usage
Primary usage of this library is defining domain data types that your application
will work with. The interface consists of lower-level type definitions and a higher-level
virtus-like interface for defining structs.
### Accessing built-in types
Coercible types using kernel coercion methods:
- `string`
- `int`
- `float`
- `decimal`
- `array`
- `hash`
Non-coercible:
- `nil`
- `true`
- `false`
- `date`
- `date_time`
- `time`
More types will be added soon.
Types are grouped under 4 categories:
- default: pass-through without any checks
- `strict` - doesn't coerce and checks the input type against the primitive class
- `coercible` - tries to coerce and raises type-error if it failed
- `maybe` - accepts either a nil or something else
``` ruby
# default passthrough category
float = Dry::Data["float"]
float[3.2] # => 3.2
float["3.2"] # "3.2"
# strict type-check category
int = Dry::Data["strict.int"]
int[1] # => 1
int['1'] # => raises TypeError
# coercible type-check group
string = Dry::Data["coercible.string"]
array = Dry::Data["coercible.array"]
string[:foo] # => 'foo'
array[:foo] # => [:foo]
```
### Optional types
All built-in types have their optional versions too, you can access them under
`"maybe.strict"` and `"maybe.coercible"` categories:
``` ruby
maybe_int = Dry::Data["maybe.strict.int"]
maybe_int[nil] # None
maybe_int[123] # Some(123)
maybe_coercible_float = Dry::Data["maybe.coercible.float"]
maybe_int[nil] # None
maybe_int['12.3'] # Some(12.3)
```
You can define your own optional types too:
``` ruby
maybe_string = Dry::Data["nil"] | Dry::Data["string"]
maybe_string[nil]
# => None
maybe_string[nil].fmap(&:upcase)
# => None
maybe_string['something']
# => Some('something')
maybe_string['something'].fmap(&:upcase)
# => Some('SOMETHING')
maybe_string['something'].fmap(&:upcase).value
# => "SOMETHING"
```
### Defining a struct
``` ruby
class User < Dry::Data::Struct
attribute :name, "maybe.coercible.string"
attribute :age, "coercible.int"
end
# becomes available like any other type
user_type = Dry::Data["user"]
user = user_type[name: nil, age: '21']
user.name # None
user.age # 21
user = user_type[name: 'Jane', age: '21']
user.name # => Some("Jane")
user.age # => 21
```
## WIP
This is early alpha with a rough plan to:
* Add constrained types (ie a string with a strict length, a number with a strict range etc.)
* Benchmark against other libs and make sure it's fast enough
## Development
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/dryrb/dry-data.