well_formed-dry_types 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/README.md +107 -0
- data/Rakefile +10 -0
- data/lib/well_formed/dry_types/version.rb +7 -0
- data/lib/well_formed/dry_types.rb +57 -0
- data/lib/well_formed-dry_types.rb +8 -0
- metadata +73 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 9f1ac7db352c20975d977aa5b0be90fab517cf99b1a13a90777582192d687796
|
|
4
|
+
data.tar.gz: fbb5c1c872a390905b58f20e8cafd99033125e94eebf19717cd81b965b85570a
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: d554e08c5e9a3ff9da0ec183552884fbf5b84a0c4a063d7302147f3d1347e64fd66362b85e9d3221bbebe6c2cf05582e4a83a665a2f94dd138f3fe77bd5eec83
|
|
7
|
+
data.tar.gz: 5c15062aba57a466f85b4e89a56f359d5c5962d34c8ae3cfc7f427e6a836abe3cf9d78e9508db80e2af5efcc78fa352ceb2587de218df8f026fe1a3dd32ad861
|
data/.rspec
ADDED
data/README.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# well_formed-dry_types
|
|
2
|
+
|
|
3
|
+
[dry-types](https://dry-rb.org/gems/dry-types) coercion integration for [WellFormed](https://github.com/bmorrall/well_formed) form objects.
|
|
4
|
+
|
|
5
|
+
Declare typed attributes with `dry_attribute` and coercion runs automatically before validation — keeping your forms free of manual casting logic.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
bundle add well_formed-dry_types
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
Require the gem in your application:
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
require "well_formed-dry_types"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Include `WellFormed::DryTypes` in any form, then use `dry_attribute` in place of `attribute` for fields that need type coercion:
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
module Types
|
|
25
|
+
include Dry.Types()
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class CreateOrderForm < WellFormed::ResourceForm
|
|
29
|
+
include WellFormed::DryTypes
|
|
30
|
+
|
|
31
|
+
resource_alias :order
|
|
32
|
+
|
|
33
|
+
dry_attribute :quantity, Types::Params::Integer
|
|
34
|
+
dry_attribute :amount, Types::Params::Decimal
|
|
35
|
+
dry_attribute :status, Types::String.enum("pending", "confirmed")
|
|
36
|
+
|
|
37
|
+
validates :quantity, presence: true, numericality: {greater_than: 0}
|
|
38
|
+
validates :amount, presence: true
|
|
39
|
+
validates :status, presence: true
|
|
40
|
+
end
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Coercion runs before validation. If coercion fails, an error is added to the attribute and validation is skipped for that field:
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
form = CreateOrderForm.new(order, current_user, {quantity: "abc", amount: "9.99", status: "pending"})
|
|
47
|
+
form.valid?
|
|
48
|
+
# => false
|
|
49
|
+
form.errors[:quantity]
|
|
50
|
+
# => ["must be Params::Integer"] # dry-types error message
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Custom error messages
|
|
54
|
+
|
|
55
|
+
Pass `message:` to override the default dry-types error message.
|
|
56
|
+
|
|
57
|
+
#### I18n key
|
|
58
|
+
|
|
59
|
+
```ruby
|
|
60
|
+
dry_attribute :status, Types::String.enum("pending", "confirmed"), message: :inclusion
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Passes the symbol to `errors.add`, which resolves it through your I18n translations.
|
|
64
|
+
|
|
65
|
+
#### Literal string
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
dry_attribute :amount, Types::Params::Decimal, message: "must be a number"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
#### Default (no message option)
|
|
72
|
+
|
|
73
|
+
```ruby
|
|
74
|
+
dry_attribute :quantity, Types::Params::Integer
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Falls back to the error message from dry-types itself.
|
|
78
|
+
|
|
79
|
+
## Inheritance
|
|
80
|
+
|
|
81
|
+
`_dry_attributes` merges up the superclass chain, so subclass forms inherit parent coercions and can add their own:
|
|
82
|
+
|
|
83
|
+
```ruby
|
|
84
|
+
class BaseOrderForm < WellFormed::ResourceForm
|
|
85
|
+
include WellFormed::DryTypes
|
|
86
|
+
dry_attribute :amount, Types::Params::Decimal
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
class CreateOrderForm < BaseOrderForm
|
|
90
|
+
dry_attribute :quantity, Types::Params::Integer
|
|
91
|
+
# inherits :amount coercion from BaseOrderForm
|
|
92
|
+
end
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## API
|
|
96
|
+
|
|
97
|
+
| Class macro | Description |
|
|
98
|
+
|---|---|
|
|
99
|
+
| `dry_attribute(name, type, message: nil)` | Declares a coerced attribute. Coercion runs before validation via a `before_validate` callback. |
|
|
100
|
+
|
|
101
|
+
## How it works
|
|
102
|
+
|
|
103
|
+
When `WellFormed::DryTypes` is included, a `before_validate` callback (`_coerce_dry_attributes`) is registered. Before each validation run, every `dry_attribute` is coerced in declaration order. On `Dry::Types::CoercionError`, an error is added and the raw value is left in place. Subsequent validators can still run against the failing attribute (e.g. a `presence` check on a nil optional).
|
|
104
|
+
|
|
105
|
+
## License
|
|
106
|
+
|
|
107
|
+
MIT
|
data/Rakefile
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WellFormed
|
|
4
|
+
module DryTypes
|
|
5
|
+
def self.included(base)
|
|
6
|
+
base.extend(ClassMethods)
|
|
7
|
+
base.set_callback(:validate, :before, :_coerce_dry_attributes)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module ClassMethods
|
|
11
|
+
# Declares an attribute whose value is coerced via a dry-types type before
|
|
12
|
+
# validation runs. Any dry-types type is accepted, including constrained,
|
|
13
|
+
# optional, and sum types.
|
|
14
|
+
#
|
|
15
|
+
# dry_attribute :amount, Types::Params::Decimal
|
|
16
|
+
# dry_attribute :status, Types::String.enum("draft", "published")
|
|
17
|
+
# dry_attribute :tags, Types::Strict::Array.of(Types::Strict::String)
|
|
18
|
+
# dry_attribute :score, Types::Params::Integer.optional
|
|
19
|
+
#
|
|
20
|
+
# If coercion fails, an error is added to the attribute. Pass +message:+ to
|
|
21
|
+
# use a specific error message or I18n key instead of the dry-types error:
|
|
22
|
+
#
|
|
23
|
+
# dry_attribute :status, Types::String.enum("draft", "published"), message: :inclusion
|
|
24
|
+
# dry_attribute :amount, Types::Params::Decimal, message: "must be a number"
|
|
25
|
+
#
|
|
26
|
+
# Coercion runs before validation, so +validates :amount, presence: true+ will
|
|
27
|
+
# still fire for nil values on optional attributes.
|
|
28
|
+
def dry_attribute(name, type, message: nil)
|
|
29
|
+
_dry_attribute_registry[name] = {type: type, message: message}
|
|
30
|
+
attribute name
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Returns the merged dry-types attribute registry for this class and all ancestors.
|
|
34
|
+
def _dry_attributes
|
|
35
|
+
parent = superclass.respond_to?(:_dry_attributes) ? superclass._dry_attributes : {}
|
|
36
|
+
parent.merge(_dry_attribute_registry)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def _dry_attribute_registry
|
|
42
|
+
@_dry_attribute_registry ||= {}
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def _coerce_dry_attributes
|
|
49
|
+
self.class._dry_attributes.each do |name, options|
|
|
50
|
+
coerced = options[:type].call(public_send(name))
|
|
51
|
+
public_send(:"#{name}=", coerced)
|
|
52
|
+
rescue ::Dry::Types::CoercionError => e
|
|
53
|
+
errors.add(name, options[:message] || e.message)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: well_formed-dry_types
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Ben Morrall
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: well_formed
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 0.1.0
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 0.1.0
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: dry-types
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '1.0'
|
|
40
|
+
email:
|
|
41
|
+
- bemo56@hotmail.com
|
|
42
|
+
executables: []
|
|
43
|
+
extensions: []
|
|
44
|
+
extra_rdoc_files: []
|
|
45
|
+
files:
|
|
46
|
+
- ".rspec"
|
|
47
|
+
- README.md
|
|
48
|
+
- Rakefile
|
|
49
|
+
- lib/well_formed-dry_types.rb
|
|
50
|
+
- lib/well_formed/dry_types.rb
|
|
51
|
+
- lib/well_formed/dry_types/version.rb
|
|
52
|
+
homepage: https://github.com/bmorrall/well_formed
|
|
53
|
+
licenses:
|
|
54
|
+
- MIT
|
|
55
|
+
metadata: {}
|
|
56
|
+
rdoc_options: []
|
|
57
|
+
require_paths:
|
|
58
|
+
- lib
|
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
60
|
+
requirements:
|
|
61
|
+
- - ">="
|
|
62
|
+
- !ruby/object:Gem::Version
|
|
63
|
+
version: 3.1.0
|
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
requirements: []
|
|
70
|
+
rubygems_version: 3.6.9
|
|
71
|
+
specification_version: 4
|
|
72
|
+
summary: dry-types attribute coercion integration for well_formed
|
|
73
|
+
test_files: []
|