README.md in dry-data-0.2.1 vs README.md in dry-data-0.3.0

- old
+ new

@@ -49,10 +49,12 @@ ### Built-in Type Categories Non-coercible: - `nil` +- `symbol` +- `class` - `true` - `false` - `date` - `date_time` - `time` @@ -160,10 +162,125 @@ maybe_string['something'].fmap(&:upcase).value # => "SOMETHING" ``` +### Constrained Types + +You can create constrained types that will use validation rules to check if the +input is not violating any of the configured contraints. You can treat it as +a lower level guarantee that you're not instantiating objects that are broken. + +All types support constraints API, but not all constraints are suitable for a +particular primitive, it's up to you to set up constraints that make sense. + +Under the hood it uses `dry-validation`[https://github.com/dryrb/dry-validation] +and all of its predicates are supported. + +IMPORTANT: `dry-data` does not have a runtime dependency on `dry-validation` so +if you want to use contrained types you need to add it to your Gemfile + +``` ruby +string = Dry::Data["strict.string"].constrained(min_size: 3) + +string['foo'] +# => "foo" + +string['fo'] +# => Dry::Data::ConstraintError: "fo" violates constraints + +email = Dry::Data['strict.string'].constrained( + format: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i +) + +email["jane@doe.org"] +# => "jane@doe.org" + +email["jane"] +# => Dry::Data::ConstraintError: "fo" violates constraints +``` + +### Setting Type Constants + +Types can be stored as easily accessible constants in a configured namespace: + +``` ruby +module Types; end + +Dry::Data.configure do |config| + config.namespace = Types +end + +# after defining your custom types (if you've got any) you can finalize setup +Dry::Data.finalize + +# this defines all types under your namespace +Types::Coercible::String +# => #<Dry::Data::Type:0x007feffb104aa8 @constructor=#<Method: Kernel.String>, @primitive=String> +``` + +With types accessible as constants you can easily compose more complex types, +like sum-types or constrained types, in hash schemas or structs: + +``` ruby +Dry::Data.configure do |config| + config.namespace = Types +end + +Dry::Data.finalize + +module Types + Email = String.constrained(format: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i) + Age = Int.constrained(gt: 18) +end + +class User < Dry::Data::Struct + attribute :name, Types::String + attribute :email, Types::Email + attribute :age, Types::Age +end +``` + +### Defining Enums + +In many cases you may want to define an enum. For example in a blog application +a post may have a finite list of statuses. Apart from accessing the current status +value it is useful to have all possible values accessible too. Furthermore an +enum is a `int => value` map, so you can store integers somewhere and have them +mapped to enum values conveniently. + +You can define enums for every type but it probably only makes sense for `string`: + +``` ruby +# assuming we have types loaded into `Types` namespace +# we can easily define an enum for our post struct +class Post < Dry::Data::Struct + Statuses = Types::Strict::String.enum('draft', 'published', 'archived') + + attribute :title, Types::Strict::String + attribute :body, Types::Strict::String + attribute :status, Statuses +end + +# enum values are frozen, let's be paranoid, doesn't hurt and have potential to +# eliminate silly bugs +Post::Statuses.values.frozen? # => true +Post::Statuses.values.all?(&:frozen?) # => true + +# you can access values using indices or actual values +Post::Statuses[0] # => "draft" +Post::Statuses['draft'] # => "draft" + +# it'll raise if something silly was passed in +Post::Statuses['something silly'] +# => Dry::Data::ConstraintError: "something silly" violates constraints + +# nil is considered as something silly too +Post::Statuses[nil] +# => Dry::Data::ConstraintError: nil violates constraints +``` + ### 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. @@ -228,15 +345,15 @@ user.name # => Some("Jane") user.age # => 21 ``` -## WIP +## Status and Roadmap -This is early alpha with a rough plan to: +This library is in an early stage of development but you are encauraged to try it +out and provide feedback. -* 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 +For planned features check out [the issues](https://github.com/dryrb/dry-data/labels/feature). ## 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.