Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update docs to recommend upfront module definition (i.e. Types = Dry.Types()) #432

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docsite/source/array-with-member.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ name: dry-types
The built-in array type supports defining the member's type:

``` ruby
Types = Dry.Types()

PostStatuses = Types::Array.of(Types::Coercible::String)

PostStatuses[[:foo, :bar]] # ["foo", "bar"]
Expand Down
2 changes: 1 addition & 1 deletion docsite/source/built-in-types.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Built-in types are grouped under 6 categories:

### Categories

Assuming you included `Dry::Types` ([see instructions](docs::getting-started)) in a module called `Types`:
Assuming you've defined your own `Types` module ([see instructions](docs::getting-started)):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you feel about changing the assertion to one that would be more indicative that they should do this, rather than conditional that they might have.

Suggested change
Assuming you've defined your own `Types` module ([see instructions](docs::getting-started)):
After you've defined your own `Types` module ([see instructions](docs::getting-started)):


* Nominal types:
- `Types::Nominal::Any`
Expand Down
2 changes: 2 additions & 0 deletions docsite/source/constraints.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ All types support the constraints API, but not all constraints are suitable for
Under the hood it uses [`dry-logic`](/gems/dry-logic) and all of its predicates are supported.

``` ruby
Types = Dry.Types()

string = Types::String.constrained(min_size: 3)

string['foo']
Expand Down
18 changes: 17 additions & 1 deletion docsite/source/custom-types.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ There are a bunch of helpers for building your own types based on existing class
`Types.Instance` builds a type that checks if a value has the given class.

```ruby
Types = Dry.Types()

range_type = Types.Instance(Range)
range_type[1..2] # => 1..2
```
Expand All @@ -20,6 +22,8 @@ range_type[1..2] # => 1..2
`Types.Value` builds a type that checks a value for equality (using `==`).

```ruby
Types = Dry.Types()

valid = Types.Value('valid')
valid['valid'] # => 'valid'
valid['invalid']
Expand All @@ -31,6 +35,8 @@ valid['invalid']
`Types.Constant` builds a type that checks a value for identity (using `equal?`).

```ruby
Types = Dry.Types()

valid = Types.Constant(:valid)
valid[:valid] # => :valid
valid[:invalid]
Expand All @@ -42,6 +48,8 @@ valid[:invalid]
`Types.Constructor` builds a new constructor type for the given class. By default uses the `new` method as a constructor.

```ruby
Types = Dry.Types()

user_type = Types.Constructor(User)

# It is equivalent to User.new(name: 'John')
Expand All @@ -59,6 +67,8 @@ user_type = Types.Constructor(User) { |values| User.new(values) }
`Types.Nominal` wraps the given class with a simple definition without any behavior attached.

```ruby
Types = Dry.Types()

int = Types.Nominal(Integer)
int[1] # => 1

Expand All @@ -71,6 +81,8 @@ int['one'] # => 'one'
`Types.Hash` builds a new hash schema.

```ruby
Types = Dry.Types()

# In the full form
Types::Hash.schema(name: Types::String, age: Types::Coercible::Integer)

Expand All @@ -83,6 +95,8 @@ Types.Hash(name: Types::String, age: Types::Coercible::Integer)
`Types.Array` is a shortcut for `Types::Array.of`

```ruby
Types = Dry.Types()

ListOfStrings = Types.Array(Types::String)
```

Expand All @@ -91,6 +105,8 @@ ListOfStrings = Types.Array(Types::String)
`Types.Interface` builds a type that checks a value responds to given methods.

```ruby
Types = Dry.Types()

Callable = Types.Interface(:call)
Contact = Types.Interface(:name, :phone)
```
```
2 changes: 2 additions & 0 deletions docsite/source/default-values.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ name: dry-types
A type with a default value will return the configured value when the input is not defined:

``` ruby
Types = Dry.Types()

PostStatus = Types::String.default('draft')

PostStatus[] # "draft"
Expand Down
13 changes: 7 additions & 6 deletions docsite/source/enum.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ In many cases you may want to define an enum. For example, in a blog application
require 'dry-types'
require 'dry-struct'

module Types
include Dry.Types()
end
Types = Dry.Types()

class Post < Dry::Struct
Statuses = Types::String.enum('draft', 'published', 'archived')
Expand Down Expand Up @@ -41,23 +39,26 @@ Post::Statuses[nil]
Note that if you want to define an enum type with a default, you must call `.default` *before* calling `.enum`, not the other way around:

```ruby
Types = Dry.Types()

# this is the correct usage:
Dry::Types::String.default('red').enum('blue', 'green', 'red')
Types::String.default('red').enum('blue', 'green', 'red')

# this will raise an error:
Dry::Types::String.enum('blue', 'green', 'red').default('red')
Types::String.enum('blue', 'green', 'red').default('red')
```

### Mappings

A classic example is mapping integers coming from somewhere (API/database/etc) to something more understandable:

```ruby
Types = Dry.Types()

class Cell < Dry::Struct
attribute :state, Types::String.enum('locked' => 0, 'open' => 1)
end


Cell.new(state: 'locked')
# => #<Cell state="locked">

Expand Down
2 changes: 1 addition & 1 deletion docsite/source/extensions.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ sections:
- monads
---

`dry-types` can be extended with extension. Those extensions are loaded with `Dry::Types.load_extensions`.
dry-types can be extended with extension. Those extensions are loaded with `Dry::Types.load_extensions`.

Available extensions:

Expand Down
7 changes: 3 additions & 4 deletions docsite/source/extensions/maybe.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@ The [dry-monads gem](/gems/dry-monads/) provides approach to handling optional v
require 'dry-types'

Dry::Types.load_extensions(:maybe)
module Types
include Dry.Types()
end

Types = Dry.Types()
```

2. Append `.maybe` to a _Type_ to return a _Monad_ object
2. Append `.maybe` to a _Type_ to return a _Monad_ object

```ruby
x = Types::Maybe::Strict::Integer[nil]
Expand Down
23 changes: 11 additions & 12 deletions docsite/source/getting-started.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,22 @@ layout: gem-single
name: dry-types
---

### Using `Dry::Types` in Your Application
### Using dry-types in your application

1. Make `Dry::Types` available to the application by creating a namespace that includes `Dry::Types`:
1. Make the base types available to your application by defining your own module built from `Dry.Types()`:

```ruby
module Types
include Dry.Types()
end
Types = Dry.Types()
```
2. Reload the environment, & type `Types::Coercible::String` in the ruby console to confirm it worked:

2. Reload the environment, & enter `Types::Coercible::String` in your ruby console to confirm it worked:

``` ruby
Types::Coercible::String
# => #<Dry::Types::Constructor type=#<Dry::Types::Definition primitive=String options={}>>
# => #<Dry::Types[Constructor<Nominal<String> fn=Kernel.String>]>
```

### Creating Your First Type
### Creating your first type

1. Define a struct's types by passing the name & type to the `attribute` method:

Expand All @@ -31,15 +29,16 @@ name: dry-types
end
```

2. Define [Custom Types](docs::custom-types) in the `Types` module, then pass the name & type to `attribute`:
2. Define [Custom Types](docs::custom-types) in your types module, then pass the name & type to `attribute`:

```ruby
Types = Dry.Types()

module Types
include Dry.Types()

Email = String.constrained(format: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i)
Age = Integer.constrained(gt: 18)
end

class User < Dry::Struct
attribute :name, Types::String
attribute :email, Types::Email
Expand Down
14 changes: 14 additions & 0 deletions docsite/source/hash-schemas.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ name: dry-types
It is possible to define a type for a hash with a known set of keys and corresponding value types. Let's say you want to describe a hash containing the name and the age of a user:

```ruby
Types = Dry.Types()

# using simple kernel coercions
user_hash = Types::Hash.schema(name: Types::String, age: Types::Coercible::Integer)

Expand Down Expand Up @@ -78,6 +80,8 @@ The process of converting types to constructors like that can be automated, see
By default, all keys are required to present in the input. You can mark a key as optional by adding `?` to its name:

```ruby
Types = Dry.Types()

user_hash = Types::Hash.schema(name: Types::String, age?: Types::Integer)

user_hash[name: 'Jane']
Expand All @@ -89,6 +93,8 @@ user_hash[name: 'Jane']
All keys not declared in the schema are silently ignored. This behavior can be changed by calling `.strict` on the schema:

```ruby
Types = Dry.Types()

user_hash = Types::Hash.schema(name: Types::String).strict
user_hash[name: 'Jane', age: 21]
# => Dry::Types::UnknownKeysError: unexpected keys [:age] in Hash input
Expand All @@ -99,6 +105,8 @@ user_hash[name: 'Jane', age: 21]
Keys are supposed to be symbols but you can attach a key tranformation to a schema, e.g. for converting strings into symbols:

```ruby
Types = Dry.Types()

user_hash = Types::Hash.schema(name: Types::String).with_key_transform(&:to_sym)
user_hash['name' => 'Jane']

Expand All @@ -110,6 +118,8 @@ user_hash['name' => 'Jane']
Hash schemas can be inherited in a sense you can define a new schema based on an existing one. Declared keys will be merged, key and type transformations will be preserved. The `strict` option is also passed to the new schema if present.

```ruby
Types = Dry.Types()

# Building an empty base schema
StrictSymbolizingHash = Types::Hash.schema({}).strict.with_key_transform(&:to_sym)

Expand All @@ -131,6 +141,8 @@ The resulting schema will have the sum of both sets
of attributes.

```ruby
Types = Dry.Types()

user_hash = Types::Hash.schema(
name: Types::String
)
Expand All @@ -153,6 +165,8 @@ while each attribute keeps the type transformations from its original hash.
A schema can transform types with a block. For example, the following code makes all keys optional:

```ruby
Types = Dry.Types()

user_hash = Types::Hash.with_type_transform { |type| type.required(false) }.schema(
name: Types::String,
age: Types::Integer
Expand Down
12 changes: 5 additions & 7 deletions docsite/source/index.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,15 @@ sections:
- extensions
---

`dry-types` is a simple and extendable type system for Ruby; useful for value coercions, applying constraints, defining complex structs or value objects and more. It was created as a successor to [Virtus](https://github.com/solnic/virtus).
dry-types is a simple and extendable type system for Ruby; useful for value coercions, applying constraints, defining complex structs or value objects and more. It was created as a successor to [Virtus](https://github.com/solnic/virtus).

### Example usage

```ruby
require 'dry-types'
require 'dry-struct'

module Types
include Dry.Types()
end
Types = Dry.Types()

User = Dry.Struct(name: Types::String, age: Types::Integer)

Expand Down Expand Up @@ -111,7 +109,7 @@ User.schema.key(:age).meta
# => {:info=>"extra info about age"}
```

- Pass values directly to `Dry::Types` without creating an object using `[]`:
- Pass values directly to types without creating an object using `[]`:

```ruby
Types::Strict::String["foo"]
Expand Down Expand Up @@ -142,7 +140,7 @@ Types::Strict::String[10000]

### Use cases

`dry-types` is suitable for many use-cases, for example:
dry-types is suitable for many use-cases, for example:

* Value coercions
* Processing arrays
Expand All @@ -152,7 +150,7 @@ Types::Strict::String[10000]

### Other gems using dry-types

`dry-types` is often used as a low-level abstraction. The following gems use it already:
dry-types is often used as a low-level abstraction. The following gems use it already:

* [dry-struct](/gems/dry-struct)
* [dry-initializer](/gems/dry-initializer)
Expand Down
2 changes: 2 additions & 0 deletions docsite/source/map.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ name: dry-types
`Map` describes a homogeneous hashmap. This means only types of keys and values are known. You can simply imagine a map input as a list of key-value pairs.

```ruby
Types = Dry.Types()

int_float_hash = Types::Hash.map(Types::Integer, Types::Float)
int_float_hash[100 => 300.0, 42 => 70.0]
# => {100=>300.0, 42=>70.0}
Expand Down
8 changes: 5 additions & 3 deletions docsite/source/optional-values.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ name: dry-types

Types themselves have optional attributes you can apply to get further functionality.

### Append `.optional` to a _Type_ to allow `nil`
### Append `.optional` to a type to allow `nil`

By default, nil values raise an error:

``` ruby
Types = Dry.Types()

Types::Strict::String[nil]
# => raises Dry::Types::ConstraintError
```
Expand All @@ -30,6 +32,6 @@ optional_string[123]

`Types::String.optional` is just syntactic sugar for `Types::Strict::Nil | Types::Strict::String`.

### Handle optional values using Monads
### Handle optional values using monads

See [Maybe](docs::extensions/maybe) extension for another approach to handling optional values by returning a [_Monad_](/gems/dry-monads/) object.
See the [Maybe](docs::extensions/maybe) extension for another approach to handling optional values by returning a [_monad_](/gems/dry-monads/) object.
4 changes: 3 additions & 1 deletion docsite/source/sum.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ order: 7

You can specify sum types using `|` operator, it is an explicit way of defining what the valid types of a value are.

For example `dry-types` defines the `Bool` type which is a sum consisting of the `True` and `False` types, expressed as `Types::True | Types::False`.
For example dry-types defines the `Bool` type which is a sum consisting of the `True` and `False` types, expressed as `Types::True | Types::False`.

Another common case is defining that something can be either `nil` or something else:

``` ruby
Types = Dry.Types()

nil_or_string = Types::Nil | Types::String

nil_or_string[nil] # => nil
Expand Down