konstruo 1.0.2 → 1.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 99a8367fd1ff2d8dd01183376df49a1209708ef0df033e2d7033294f1a1991d5
4
- data.tar.gz: 6b94c45951ad489563412dff3d1e626434d8da373f11a07f56e48ad7cec935bd
3
+ metadata.gz: 54c20c9b4279be4b434685e57738654da55fbb75fe21e5bc8d4286bdd292aaf4
4
+ data.tar.gz: e924f4facbf95273bc16585e994c455877d28723763c05ce223e38e05a451ece
5
5
  SHA512:
6
- metadata.gz: 4390eb0a4b2cacdad3b73e3f8bfdd31e37685d903bd665c816e69e58d7d2a560975a7304f5768e9fac10b2a939227aebe67ceb85c4d7c337e69aedae7094ccc8
7
- data.tar.gz: dd22e4f912cb0edeaa9f9854ab6d1bbc8695164fe4ed4516a9a7ea3dc06b15e28d79ce86ac4e9cb5160a08fc68099ce31a4555b003eab6b91d958eb775e1ed1a
6
+ metadata.gz: c04a03b2187dc910e08e38fa4a27619d9b0d935a7fb518af14061c8780773c768b3dc435194c3aa14abc60ff94afca2bcdc448e25c2ae14bf0d0fcf052fe484d
7
+ data.tar.gz: 0f03affbf375f28e6fdaa2b7b17638df508cdd7d384631130677ba2e6de57e8b49fd81be59c476974d236391974f40956ea5498386ab0ea16d25ab1a9026ded6
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.0.3](https://github.com/DashBrains/konstruo/compare/v1.0.2...v1.0.3) (2026-04-01)
4
+
5
+
6
+ ### Features
7
+
8
+ * add nilable field ([c3422ea](https://github.com/DashBrains/konstruo/commit/c3422ea68611223f77bfcdee2b6497a302bfc975))
9
+
10
+
11
+ ### Miscellaneous Chores
12
+
13
+ * **release:** 1.0.3 ([86ae552](https://github.com/DashBrains/konstruo/commit/86ae552f0aff66bbdbdda20a06e549f5e38bff51))
14
+
3
15
  ## [1.0.2](https://github.com/DashBrains/konstruo/compare/v1.0.1...v1.0.2) (2026-03-31)
4
16
 
5
17
 
data/README.md CHANGED
@@ -96,7 +96,7 @@ payload.name # => "Jane"
96
96
  Field API:
97
97
 
98
98
  ```ruby
99
- field(name, type, required: false, custom_name: nil, mapper: nil, error_message: nil)
99
+ field(name, type, required: false, nullable: nil, custom_name: nil, mapper: nil, error_message: nil)
100
100
  ```
101
101
 
102
102
  Options:
@@ -104,6 +104,7 @@ Options:
104
104
  - `name` (`Symbol`): Ruby attribute name.
105
105
  - `type` (`Class` or `[Class]`): Expected value type.
106
106
  - `required` (`Boolean`): Raises `Konstruo::ValidationError` when missing.
107
+ - `nullable` (`Boolean`, optional): Controls whether a present key can have `nil` value.
107
108
  - `custom_name` (`String`): External key name to read from input.
108
109
  - `mapper` (`Proc`): Converts raw input value before assignment.
109
110
  - `error_message` (`String`): Custom validation error message.
@@ -118,6 +119,11 @@ Supported type patterns:
118
119
 
119
120
  Array type declarations must contain exactly one element class (for example `[String]`).
120
121
 
122
+ `nullable` default behavior:
123
+
124
+ - If `required: true` and `nullable` is omitted, `nullable` defaults to `false`.
125
+ - If `required: false` and `nullable` is omitted, `nullable` defaults to `true`.
126
+
121
127
  ## Parsing Input
122
128
 
123
129
  Konstruo supports three entry points:
@@ -158,6 +164,17 @@ Default message format:
158
164
  Missing required field: field_name
159
165
  ```
160
166
 
167
+ ### Nullability
168
+
169
+ When a key is present with `nil` value:
170
+
171
+ - `nullable: true` allows it.
172
+ - `nullable: false` raises:
173
+
174
+ ```text
175
+ Field cannot be nil: field_name
176
+ ```
177
+
161
178
  ### Type errors
162
179
 
163
180
  Type mismatches raise `Konstruo::ValidationError` with details like:
@@ -16,6 +16,7 @@ module Konstruo
16
16
  const :name, Symbol
17
17
  const :type, FieldType
18
18
  const :required, T::Boolean
19
+ const :nullable, T::Boolean
19
20
  const :custom_name, String
20
21
  const :mapper, T.nilable(T.proc.params(value: T.untyped).returns(T.untyped))
21
22
  const :error_message, T.nilable(String)
@@ -52,20 +53,23 @@ module Konstruo
52
53
  name: Symbol,
53
54
  type: FieldType,
54
55
  required: T::Boolean,
56
+ nullable: T.nilable(T::Boolean),
55
57
  custom_name: T.nilable(String),
56
58
  mapper: T.nilable(T.proc.params(value: T.untyped).returns(T.untyped)),
57
59
  error_message: T.nilable(String)
58
60
  ).void
59
61
  end
60
- def field(name, type, required: false, custom_name: nil, mapper: nil, error_message: nil)
62
+ def field(name, type, required: false, nullable: nil, custom_name: nil, mapper: nil, error_message: nil)
61
63
  attr_accessor name unless method_defined?(name)
62
64
 
63
65
  validate_field_type!(type)
66
+ resolved_nullable = nullable.nil? ? !required : nullable
64
67
 
65
68
  fields << FieldDefinition.new(
66
69
  name: name,
67
70
  type: type,
68
71
  required: required,
72
+ nullable: resolved_nullable,
69
73
  custom_name: custom_name || name.to_s,
70
74
  mapper: mapper,
71
75
  error_message: error_message
@@ -146,7 +150,9 @@ module Konstruo
146
150
  value = has_string_key ? hash[key] : hash[symbol_key]
147
151
 
148
152
  if value.nil?
149
- raise Konstruo::ValidationError, (field.error_message || "Missing required field: #{key}") if field.required
153
+ raise Konstruo::ValidationError, (field.error_message || "Field cannot be nil: #{key}") unless field.nullable
154
+
155
+ send(:"#{field.name}=", nil)
150
156
  else
151
157
  assign_value(field.name, field.type, value, field.mapper, field.error_message)
152
158
  end
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Konstruo
5
- VERSION = '1.0.2'
5
+ VERSION = '1.0.3'
6
6
  end
@@ -34,7 +34,7 @@ module Tapioca
34
34
  root.create_path(constant) do |klass|
35
35
  constant.fields.each do |field|
36
36
  field_type = as_type_string(field.type)
37
- accessor_type = field.required ? field_type : "T.nilable(#{field_type})"
37
+ accessor_type = (field.required && !field.nullable) ? field_type : "T.nilable(#{field_type})"
38
38
  field_name = field.name.to_s
39
39
 
40
40
  klass.create_method(field_name, return_type: accessor_type)
@@ -0,0 +1,38 @@
1
+ # typed: true
2
+
3
+ # DO NOT EDIT MANUALLY
4
+ # This is an autogenerated file for dynamic methods in `NullableMapper`.
5
+ # Please instead update this file by running `bin/tapioca dsl NullableMapper`.
6
+
7
+
8
+ class NullableMapper
9
+ sig { returns(T.nilable(String)) }
10
+ def optional_non_nullable; end
11
+
12
+ sig { params(value: T.nilable(String)).returns(T.nilable(String)) }
13
+ def optional_non_nullable=(value); end
14
+
15
+ sig { returns(T.nilable(String)) }
16
+ def optional_nullable_default; end
17
+
18
+ sig { params(value: T.nilable(String)).returns(T.nilable(String)) }
19
+ def optional_nullable_default=(value); end
20
+
21
+ sig { returns(String) }
22
+ def required_non_nullable; end
23
+
24
+ sig { params(value: String).returns(String) }
25
+ def required_non_nullable=(value); end
26
+
27
+ sig { returns(String) }
28
+ def required_non_nullable_default; end
29
+
30
+ sig { params(value: String).returns(String) }
31
+ def required_non_nullable_default=(value); end
32
+
33
+ sig { returns(T.nilable(String)) }
34
+ def required_nullable; end
35
+
36
+ sig { params(value: T.nilable(String)).returns(T.nilable(String)) }
37
+ def required_nullable=(value); end
38
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: konstruo
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - DashBrains
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-03-31 00:00:00.000000000 Z
11
+ date: 2026-04-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -90,6 +90,7 @@ files:
90
90
  - sorbet/rbi/dsl/inheritance_child_mapper.rbi
91
91
  - sorbet/rbi/dsl/invalid_field_definition_mapper.rbi
92
92
  - sorbet/rbi/dsl/loose_person_mapper.rbi
93
+ - sorbet/rbi/dsl/nullable_mapper.rbi
93
94
  - sorbet/rbi/dsl/person.rbi
94
95
  - sorbet/rbi/dsl/strict_person_mapper.rbi
95
96
  - sorbet/rbi/gems/.gitattributes