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 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
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
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,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new(:rubocop)
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WellFormed
4
+ module DryTypes
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -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
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "well_formed"
4
+ require "dry-types"
5
+ require_relative "well_formed/dry_types/version"
6
+ require_relative "well_formed/dry_types"
7
+
8
+ WellFormed::Extensions.register_extension(WellFormed::DryTypes)
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: []