dry-data 
A simple type system for Ruby with support for coercions.
Installation
Add this line to your application's Gemfile:
gem 'dry-data'
And then execute:
$ bundle
Or install it yourself as:
$ gem install dry-data
Usage
You can use dry-data for defining various data types in your application, like
domain entities and value objects or hashes with coercible values used to handle
params.
Built-in types are grouped under 5 categories:
- default: pass-through without any checks
strict- doesn't coerce and checks the input type against the primitive classcoercible- tries to coerce and raises type-error if it failedform- non-strict coercion types suitable for form paramsmaybe- accepts either a nil or something else
Built-in Type Categories
Non-coercible:
niltruefalsedatedate_timetime
Coercible types using kernel coercion methods:
coercible.stringcoercible.intcoercible.floatcoercible.decimalcoercible.arraycoercible.hash
Optional strict types:
maybe.strict.stringmaybe.strict.intmaybe.strict.floatmaybe.strict.decimalmaybe.strict.arraymaybe.strict.hash
Optional coercible types:
maybe.coercible.stringmaybe.coercible.intmaybe.coercible.floatmaybe.coercible.decimalmaybe.coercible.arraymaybe.coercible.hash
Coercible types suitable for form param processing:
form.nilform.dateform.date_timeform.timeform.trueform.falseform.boolform.intform.floatform.decimal
Accessing Built-in Types
# 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]
# form group
date = Dry::Data["form.date"]
date['2015-11-29'] # => #<Date: 2015-11-29 ((2457356j,0s,0n),+0s,2299161j)>
Optional types
All built-in types have their optional versions too, you can access them under
"maybe.strict" and "maybe.coercible" categories:
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:
maybe_string = Dry::Data["optional"] | 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 hash with explicit schema
The built-in hash type has constructors that you can use to define hashes with explicit schemas and coercible values using the built-in types.
Hash Schema
# using simple kernel coercions
hash = Dry::Data['hash'].schema(name: 'string', age: 'coercible.int')
hash[name: 'Jane', age: '21']
# => { :name => "Jane", :age => 21 }
# using form param coercions
hash = Dry::Data['hash'].schema(name: 'string', birthdate: 'form.date')
hash[name: 'Jane', birthdate: '1994-11-11']
# => { :name => "Jane", :birthdate => #<Date: 1994-11-11 ((2449668j,0s,0n),+0s,2299161j)> }
Strict Hash
Strict hash will raise errors when keys are missing or value types are incorrect.
hash = Dry::Data['hash'].strict(name: 'string', age: 'coercible.int')
hash[email: '[email protected]', name: 'Jane', age: 21]
# => Dry::Data::SchemaKeyError: :email is missing in Hash input
Symbolized Hash
Symbolized hash will turn string key names into symbols
hash = Dry::Data['hash'].symbolized(name: 'string', age: 'coercible.int')
hash['name' => 'Jane', 'age' => '21']
# => { :name => "Jane", :age => 21 }
Defining a struct
You can define struct objects which will have attribute readers for specified attributes using a simple dsl:
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.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/dryrb/dry-data.