Skip to content

Commit

Permalink
Improve UPGRADING.md instructions for custom types
Browse files Browse the repository at this point in the history
* Provide an example for taking an existing type and porting it to
  the dry-types. This would have saved me some time.

* Removed examples of using `Virtus.model`. It seems confusing to
  support dry-types and Virtus at the same time.

Closes #2012
  • Loading branch information
stanhu committed Mar 27, 2020
1 parent 0219c75 commit ee1f29f
Showing 1 changed file with 36 additions and 17 deletions.
53 changes: 36 additions & 17 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,57 @@ After adding dry-types, Ruby 2.4 or newer is required.

#### Coercion

[Virtus](https://github.com/solnic/virtus) has been replaced by [dry-types](https://dry-rb.org/gems/dry-types/1.2/) for parameter coercion. If your project depends on Virtus, explicitly add it to your `Gemfile`. Also, if Virtus is used for defining custom types
[Virtus](https://github.com/solnic/virtus) has been replaced by
[dry-types](https://dry-rb.org/gems/dry-types/1.2/) for parameter
coercion. If your project depends on Virtus outside of Grape, explicitly
add it to your `Gemfile`.

Here's an example of how to migrate a custom type from Virtus to dry-types:

```ruby
class User
include Virtus.model
# Legacy Grape parser
class SecureUriType < Virtus::Attribute
def coerce(input)
URI.parse value
end

attribute :id, Integer
attribute :name, String
def value_coerced?(input)
value.is_a? String
end
end

# somewhere in your API
params do
requires :user, type: User
requires :secure_uri, type: SecureUri
end
```

Add a class-level `parse` method to the model:
To use dry-types, we need to:

```ruby
class User
include Virtus.model
1. Remove the inheritance of `Virtus::Attribute`
1. Rename `coerce` to `self.parse`
1. Rename `value_coerced?` to `self.parsed?`

attribute :id, Integer
attribute :name, String
The custom type must have a class-level `parse` method to the model. A
class-level `parsed?` is needed if the parsed type differs from the
defined type. In the example below, since `SecureUri` is not the same
as `URI::HTTPS`, `self.parsed?` is needed:

def self.parse(attrs)
new(attrs)
```ruby
# New dry-types parser
class SecureUri
def self.parse(value)
URI.parse value
end

def self.parsed?(value)
value.is_a? URI::HTTPS
end
end
```

Custom types which don't depend on Virtus don't require any changes.
params do
requires :secure_uri, type: SecureUri
end
```

#### Ensure that Array types have explicit coercions

Expand Down

0 comments on commit ee1f29f

Please sign in to comment.