zod_rails 0.3.0 → 0.3.2

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: f8e1e956f2954b2200c39a4b467529911806337883bd2674b31b7a271ef560a8
4
- data.tar.gz: 681139d70905f6c34ee190f629f437e015a2eb1313a76940d42b0eaf6a8ffabe
3
+ metadata.gz: 1163af9def212c75de8a25cc2e35fda2bbef62b372b239cd7bface8a3ae56098
4
+ data.tar.gz: 9d397ad7adcd549b35d059157b209a2214c2a558310ff9e860f663cad1849c47
5
5
  SHA512:
6
- metadata.gz: 30a4ce819ee99ac5bcdd83e482c16cd2029136af74e6e64c04a03960c8640991cef144eab6562eb431a0205c655c857687e78f023ffe66aaa9d8c53a953a7b1e
7
- data.tar.gz: 7744055379a755592beadb1010992deeba5ef0fac4d0ea5719bd67d98191264d9671d0a4ee6806e2c62a7e97e0a69997d0bd8f80eca7ed6002c52d59ae7c236c
6
+ metadata.gz: d1bd6a4631656247e5a0a15faf4b2928cbc1b3e9d982086b661757b3ec8eed4eecaa0fa421561165238ba997afa9fe3f8788b616d407169da281a1b6cb5821e4
7
+ data.tar.gz: 10edec80377e583661f8741bac4bff3647992fbc35d5dd16117564eb1d802e7f955b731259d861458d2a8137fb3ea60d8f2837f3fdc1faf1b4530e24fad1c92b
data/CHANGELOG.md CHANGED
@@ -4,6 +4,37 @@ All notable changes to ZodRails are documented here.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.3.2 - 2026-08-03
8
+
9
+ ### Upgrade Notes
10
+
11
+ - Upgrade from `0.3.0` or `0.3.1` immediately. Those releases omitted the Railtie's Rake task file from the packaged
12
+ gem, causing `Rails.application.load_tasks` and unrelated Rails tasks such as `db:migrate` and `assets:precompile`
13
+ to fail with `LoadError`.
14
+
15
+ ### Fixed
16
+
17
+ - Include `lib/tasks/zod_rails.rake` in the published gem.
18
+ - Verify the runtime manifest and task loading in the test suite.
19
+
20
+ ## 0.3.1 - 2026-08-03
21
+
22
+ ### Upgrade Notes
23
+
24
+ - Regenerate committed schemas after upgrading if your models contain PostgreSQL arrays, adapter-reported `:bigint`
25
+ columns, or `time` columns. Their generated wire schemas now match Rails payload shapes more closely.
26
+
27
+ ### Fixed
28
+
29
+ - Generate `z.array(...)` schemas for PostgreSQL array columns, with inclusion constraints on elements and
30
+ presence, length, default, and nullability constraints on the outer array.
31
+ - Map adapter-reported `:bigint` columns to the JSON number shape emitted by Rails.
32
+ - Validate Rails `time` payloads as ISO datetimes with timezone-offset support.
33
+
34
+ ### Changed
35
+
36
+ - Run a generated-schema runtime contract against the pinned Zod version in CI.
37
+
7
38
  ## 0.3.0 - 2026-08-03
8
39
 
9
40
  ### Upgrade Notes
data/README.md CHANGED
@@ -45,6 +45,9 @@ Add to your Gemfile:
45
45
  gem "zod_rails"
46
46
  ```
47
47
 
48
+ Use `0.3.2` or newer. Versions `0.3.0` and `0.3.1` omitted the Railtie's Rake task file from the published gem and can
49
+ break Rails task loading.
50
+
48
51
  Then run:
49
52
 
50
53
  ```bash
@@ -112,19 +115,24 @@ end
112
115
  | Rails/DB Type | Zod Type |
113
116
  |---------------|----------|
114
117
  | `string`, `text` | `z.string()` |
115
- | `integer` | `z.int()` |
118
+ | `integer`, `bigint` | `z.int()` |
116
119
  | `float` | `z.number()` |
117
- | `bigint` | `z.string()` (avoids JS `Number` overflow) |
118
120
  | `decimal` | `z.string()` (preserves `BigDecimal` precision) |
119
121
  | `boolean` | `z.boolean()` |
120
122
  | `date` | `z.iso.date()` |
121
123
  | `datetime`, `timestamp` | `z.iso.datetime({ offset: true })` |
122
124
  | `json`, `jsonb` | `z.json()` |
123
125
  | `uuid` | `z.uuid()` |
124
- | `time` | `z.string()` |
126
+ | `time` | `z.iso.datetime({ offset: true })` |
125
127
  | `binary` | `z.string()` |
126
128
  | `enum` | `z.enum([...])` |
127
129
 
130
+ PostgreSQL array columns wrap the element mapping with `z.array(...)`. Nullability and input optionality apply to the
131
+ array itself, so a nullable `string[]` becomes `z.array(z.string()).nullable()`, while a non-null array with a database
132
+ default becomes `z.array(z.string())` in the response schema and `z.array(z.string()).optional()` in the input schema.
133
+ Rails inclusion validators on array attributes constrain each element, so `inclusion: { in: %w[a b] }` produces an
134
+ element enum inside the array.
135
+
128
136
  ## Validation Mappings
129
137
 
130
138
  ZodRails introspects your model validations and maps them to Zod constraints:
@@ -346,7 +354,15 @@ For custom types not in the mapping table, ZodRails falls back to `z.unknown()`.
346
354
 
347
355
  ZodRails generates a useful static approximation of database columns and unconditional model validations. It cannot reproduce validations that require database access, another attribute, or runtime model state. Conditional and context-specific validations, dynamic numericality values, uniqueness, and unsupported Ruby regular-expression modes are skipped.
348
356
 
349
- The generated wire types must match your serializers. In particular, `bigint` and `decimal` map to strings to avoid JavaScript precision loss; configure your API serializer to emit strings for those attributes. Review generated schemas when using adapter-specific or custom ActiveRecord types.
357
+ The generated wire types must match your serializers. Rails emits integer and bigint attributes as JSON numbers, so
358
+ ZodRails maps both to `z.int()`. Zod intentionally rejects integers outside JavaScript's safe integer range; serialize
359
+ large identifiers as strings and provide an application-owned schema when values can exceed that range. Decimal values
360
+ map to strings to preserve `BigDecimal` precision. Review generated schemas when using custom serializers,
361
+ adapter-specific types, or custom ActiveRecord types.
362
+
363
+ PostgreSQL permits null elements and multidimensional values without exposing either constraint through ordinary
364
+ column metadata. ZodRails currently generates one-dimensional arrays with non-null elements. Use an application-owned
365
+ schema when a column intentionally stores null elements or nested arrays.
350
366
 
351
367
  ### Misconfigured model names
352
368
 
@@ -380,8 +396,13 @@ After checking out the repo:
380
396
  ```bash
381
397
  bundle install
382
398
  bundle exec rspec
399
+ bun install --frozen-lockfile
400
+ bun run test:contracts
383
401
  ```
384
402
 
403
+ The Bun contract test generates a TypeScript schema and parses representative Rails payload values with the pinned Zod
404
+ version. This complements the Ruby unit and golden tests by exercising the generated code at runtime.
405
+
385
406
  ## Contributing
386
407
 
387
408
  1. Fork it
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :zod_rails do
4
+ desc "Generate Zod schemas for configured models (DRY_RUN=1 to preview only)"
5
+ task generate: :environment do
6
+ config = ZodRails.configuration
7
+ generator = ZodRails::Generator.new(output_dir: config.output_dir)
8
+
9
+ if config.models.empty?
10
+ puts "No models configured. Add models to ZodRails.configure { |c| c.models = ['User', 'Article'] }"
11
+ exit 1
12
+ end
13
+
14
+ resolution = ZodRails::ModelResolver.resolve(config.models)
15
+
16
+ unless resolution[:missing].empty?
17
+ puts "ZodRails: #{resolution[:missing].size} model(s) in config.models could not be loaded:"
18
+ resolution[:missing].each { |m| puts " - #{m}" }
19
+ puts ""
20
+ puts "Check the model names in config/initializers/zod_rails.rb."
21
+ exit 1
22
+ end
23
+
24
+ unless resolution[:invalid].empty?
25
+ puts "ZodRails: configured constants must be concrete ActiveRecord models:"
26
+ resolution[:invalid].each { |model| puts " - #{model}" }
27
+ exit 1
28
+ end
29
+
30
+ models = resolution[:resolved]
31
+
32
+ if ENV["DRY_RUN"] == "1"
33
+ drift = generator.check(models)
34
+ if drift.empty?
35
+ puts "ZodRails (dry run): no changes."
36
+ else
37
+ puts "ZodRails (dry run): would update #{drift.size} file(s):"
38
+ drift.each { |d| puts " - #{d[:filename]} (#{d[:status]})" }
39
+ end
40
+ else
41
+ generated = generator.generate_all(models)
42
+ puts "Generated #{generated.size} schema file(s):"
43
+ generated.each { |f| puts " - #{f}" }
44
+ end
45
+ end
46
+
47
+ desc "Check whether generated schemas match the current models (exits nonzero on drift)"
48
+ task check: :environment do
49
+ config = ZodRails.configuration
50
+ generator = ZodRails::Generator.new(output_dir: config.output_dir)
51
+
52
+ if config.models.empty?
53
+ puts "No models configured. Nothing to check."
54
+ exit 0
55
+ end
56
+
57
+ resolution = ZodRails::ModelResolver.resolve(config.models)
58
+
59
+ unless resolution[:missing].empty?
60
+ puts "ZodRails: #{resolution[:missing].size} model(s) in config.models could not be loaded:"
61
+ resolution[:missing].each { |m| puts " - #{m}" }
62
+ exit 1
63
+ end
64
+
65
+ unless resolution[:invalid].empty?
66
+ puts "ZodRails: configured constants must be concrete ActiveRecord models:"
67
+ resolution[:invalid].each { |model| puts " - #{model}" }
68
+ exit 1
69
+ end
70
+
71
+ drift = generator.check(resolution[:resolved])
72
+
73
+ if drift.empty?
74
+ puts "ZodRails: generated schemas are up to date."
75
+ exit 0
76
+ end
77
+
78
+ puts "ZodRails: #{drift.size} file(s) out of date:"
79
+ drift.each { |d| puts " - #{d[:filename]} (#{d[:status]})" }
80
+ puts ""
81
+ puts "Run `bin/rails zod_rails:generate` to update."
82
+ exit 1
83
+ end
84
+
85
+ desc "Generate Zod schema for a specific model"
86
+ task :generate_model, [:model_name] => :environment do |_t, args|
87
+ model_name = args[:model_name]
88
+ unless model_name
89
+ puts "Usage: rails zod_rails:generate_model[ModelName]"
90
+ exit 1
91
+ end
92
+
93
+ config = ZodRails.configuration
94
+ generator = ZodRails::Generator.new(output_dir: config.output_dir)
95
+
96
+ resolution = ZodRails::ModelResolver.resolve([model_name])
97
+ if resolution[:missing].any?
98
+ puts "ZodRails: model '#{model_name}' could not be loaded. Check the spelling."
99
+ exit 1
100
+ end
101
+
102
+ if resolution[:invalid].any?
103
+ puts "ZodRails: '#{model_name}' is not a concrete ActiveRecord model."
104
+ exit 1
105
+ end
106
+
107
+ filename = generator.generate_all(resolution[:resolved]).first
108
+ puts "Generated: #{filename}"
109
+ end
110
+ end
@@ -59,7 +59,7 @@ module ZodRails
59
59
  end
60
60
 
61
61
  def string_array_inclusion(column, validations)
62
- return nil unless STRING_TYPES.include?(column.type)
62
+ return nil if column.array || !STRING_TYPES.include?(column.type)
63
63
 
64
64
  validations.find { |validation| string_array_inclusion?(validation) }
65
65
  end
@@ -75,10 +75,11 @@ module ZodRails
75
75
  remaining = validations.reject { |validation| validation.equal?(inclusion) }
76
76
  Mapping::EnumMapper.call(
77
77
  inclusion.options[:in],
78
- validation_chain: Mapping::ValidationMapper.call_all(remaining, base_type: :string),
78
+ validation_chain: Mapping::ValidationMapper.call_all(remaining, base_type: :string, array: column.array),
79
79
  nullable: nullable?(column, validations),
80
80
  input_schema: input_schema,
81
- has_default: column.has_default
81
+ has_default: column.has_default,
82
+ array: column.array
82
83
  )
83
84
  end
84
85
 
@@ -86,10 +87,12 @@ module ZodRails
86
87
  values = inspector.enums[column.name]
87
88
  Mapping::EnumMapper.call(
88
89
  values,
89
- validation_chain: Mapping::ValidationMapper.call_all(validations, base_type: :string),
90
+ validation_chain: Mapping::ValidationMapper.call_all(validations, base_type: :string, array: column.array),
90
91
  nullable: nullable?(column, validations),
91
92
  input_schema: input_schema,
92
- has_default: column.has_default
93
+ has_default: column.has_default,
94
+ array: column.array,
95
+ element_validation_chain: array_element_validation_chain(column, validations)
93
96
  )
94
97
  end
95
98
 
@@ -98,10 +101,12 @@ module ZodRails
98
101
  column.type,
99
102
  nullable: nullable?(column, validations),
100
103
  input_schema: input_schema,
101
- has_default: column.has_default
104
+ has_default: column.has_default,
105
+ array: column.array,
106
+ element_validation_chain: array_element_validation_chain(column, validations)
102
107
  )
103
108
 
104
- validation_chain = Mapping::ValidationMapper.call_all(validations, base_type: column.type)
109
+ validation_chain = Mapping::ValidationMapper.call_all(validations, base_type: column.type, array: column.array)
105
110
  insert_validation_chain(base_type, validation_chain)
106
111
  end
107
112
 
@@ -126,6 +131,13 @@ module ZodRails
126
131
  end
127
132
  end
128
133
 
134
+ def array_element_validation_chain(column, validations)
135
+ return "" unless column.array
136
+
137
+ element_validations = validations.select { |validation| validation.kind == :inclusion }
138
+ Mapping::ValidationMapper.call_all(element_validations, base_type: column.type)
139
+ end
140
+
129
141
  def filtered_columns
130
142
  inspector.columns.reject { |col| excluded_columns.include?(col.name) }
131
143
  end
@@ -3,13 +3,14 @@
3
3
  module ZodRails
4
4
  module Introspection
5
5
  class ColumnInfo
6
- attr_reader :name, :type, :nullable, :has_default
6
+ attr_reader :name, :type, :nullable, :has_default, :array
7
7
 
8
- def initialize(name:, type:, nullable:, has_default:)
8
+ def initialize(name:, type:, nullable:, has_default:, array: false)
9
9
  @name = name
10
10
  @type = type
11
11
  @nullable = nullable
12
12
  @has_default = has_default
13
+ @array = array
13
14
  freeze
14
15
  end
15
16
 
@@ -19,7 +20,8 @@ module ZodRails
19
20
  type: column.type,
20
21
  nullable: column.null,
21
22
  has_default: !column.default.nil? ||
22
- (column.respond_to?(:default_function) && !column.default_function.nil?)
23
+ (column.respond_to?(:default_function) && !column.default_function.nil?),
24
+ array: column.respond_to?(:array?) && column.array?
23
25
  )
24
26
  end
25
27
 
@@ -28,12 +30,13 @@ module ZodRails
28
30
  name == other.name &&
29
31
  type == other.type &&
30
32
  nullable == other.nullable &&
31
- has_default == other.has_default
33
+ has_default == other.has_default &&
34
+ array == other.array
32
35
  end
33
36
  alias eql? ==
34
37
 
35
38
  def hash
36
- [self.class, name, type, nullable, has_default].hash
39
+ [self.class, name, type, nullable, has_default, array].hash
37
40
  end
38
41
  end
39
42
  end
@@ -3,16 +3,30 @@
3
3
  module ZodRails
4
4
  module Mapping
5
5
  class EnumMapper
6
- def self.call(values, nullable: false, input_schema: false, has_default: false, validation_chain: "")
6
+ def self.call(values, nullable: false, input_schema: false, array: false, **options)
7
+ has_default = options.fetch(:has_default, false)
8
+ validation_chain = options.fetch(:validation_chain, "")
9
+ element_validation_chain = options.fetch(:element_validation_chain, "")
7
10
  names = values.is_a?(Hash) ? values.keys : values
8
11
  quoted = names.map { |name| JSON.generate(name.to_s) }
9
12
  enum = "z.enum([#{quoted.join(", ")}])"
10
- base = validation_chain.empty? ? enum : "z.string()#{validation_chain}.pipe(#{enum})"
13
+ base = if array
14
+ array_schema(enum, element_validation_chain, validation_chain)
15
+ elsif validation_chain.empty?
16
+ enum
17
+ else
18
+ "z.string()#{validation_chain}.pipe(#{enum})"
19
+ end
11
20
 
12
21
  suffix = determine_suffix(nullable: nullable, input_schema: input_schema, has_default: has_default)
13
22
  "#{base}#{suffix}"
14
23
  end
15
24
 
25
+ def self.array_schema(enum, element_validation_chain, array_validation_chain)
26
+ element = element_validation_chain.empty? ? enum : "z.string()#{element_validation_chain}.pipe(#{enum})"
27
+ "z.array(#{element})#{array_validation_chain}"
28
+ end
29
+
16
30
  def self.determine_suffix(nullable:, input_schema:, has_default:)
17
31
  return "" unless nullable || has_default
18
32
 
@@ -23,7 +37,7 @@ module ZodRails
23
37
  end
24
38
  end
25
39
 
26
- private_class_method :determine_suffix
40
+ private_class_method :array_schema, :determine_suffix
27
41
  end
28
42
  end
29
43
  end
@@ -7,25 +7,29 @@ module ZodRails
7
7
  string: "z.string()",
8
8
  text: "z.string()",
9
9
  integer: "z.int()",
10
- bigint: "z.string()",
10
+ bigint: "z.int()",
11
11
  float: "z.number()",
12
12
  decimal: "z.string()",
13
13
  boolean: "z.boolean()",
14
14
  date: "z.iso.date()",
15
15
  datetime: "z.iso.datetime({ offset: true })",
16
16
  timestamp: "z.iso.datetime({ offset: true })",
17
- time: "z.string()",
17
+ time: "z.iso.datetime({ offset: true })",
18
18
  json: "z.json()",
19
19
  jsonb: "z.json()",
20
20
  uuid: "z.uuid()",
21
21
  binary: "z.string()"
22
22
  }.freeze
23
23
 
24
- def self.call(type, nullable: false, input_schema: false, has_default: false)
24
+ def self.call(type, nullable: false, input_schema: false, array: false, **options)
25
+ has_default = options.fetch(:has_default, false)
26
+ element_validation_chain = options.fetch(:element_validation_chain, "")
25
27
  base = TYPE_MAP.fetch(type.to_sym) do
26
28
  ZodRails.logger.warn("ZodRails: Unknown type '#{type}', falling back to z.unknown()")
27
29
  "z.unknown()"
28
30
  end
31
+ base = "#{base}#{element_validation_chain}"
32
+ base = "z.array(#{base})" if array
29
33
 
30
34
  suffix = determine_suffix(nullable: nullable, input_schema: input_schema, has_default: has_default)
31
35
  "#{base}#{suffix}"
@@ -13,35 +13,37 @@ module ZodRails
13
13
  NUMERIC_ZOD_TYPES = %i[integer float].freeze
14
14
  STRING_ZOD_TYPES = %i[string text].freeze
15
15
 
16
- def self.call(validation, base_type:)
16
+ def self.call(validation, base_type:, array: false)
17
17
  return "" if validation.conditional? || no_op_presence?(validation)
18
18
 
19
19
  case validation.kind
20
- when :presence then map_presence(validation, base_type)
21
- when :length then map_length(validation, base_type)
22
- when :numericality then map_numericality(validation, base_type)
23
- when :format then map_format(validation, base_type)
24
- when :inclusion then map_inclusion(validation, base_type)
20
+ when :presence then map_presence(validation, base_type, array)
21
+ when :length then map_length(validation, base_type, array)
22
+ when :numericality then map_numericality(validation, base_type, array)
23
+ when :format then map_format(validation, base_type, array)
24
+ when :inclusion then map_inclusion(validation, base_type, array)
25
25
  else ""
26
26
  end
27
27
  end
28
28
 
29
- def self.call_all(validations, base_type:)
29
+ def self.call_all(validations, base_type:, array: false)
30
30
  constraints = { min: nil, max: nil, length: nil, others: [] }
31
31
 
32
32
  validations.each do |v|
33
- collect_constraints(v, base_type, constraints)
33
+ collect_constraints(v, base_type, constraints, array)
34
34
  end
35
35
 
36
36
  build_chain(constraints)
37
37
  end
38
38
 
39
- def self.map_presence(_validation, base_type)
39
+ def self.map_presence(_validation, base_type, array)
40
+ return ".min(1)" if array
41
+
40
42
  STRING_ZOD_TYPES.include?(base_type) ? ".min(1)#{presence_suffix}" : ""
41
43
  end
42
44
 
43
- def self.map_length(validation, base_type)
44
- return "" unless STRING_ZOD_TYPES.include?(base_type)
45
+ def self.map_length(validation, base_type, array)
46
+ return "" unless array || STRING_ZOD_TYPES.include?(base_type)
45
47
 
46
48
  parts = []
47
49
  opts = validation.options
@@ -56,8 +58,8 @@ module ZodRails
56
58
  parts.join
57
59
  end
58
60
 
59
- def self.map_numericality(validation, base_type)
60
- return "" unless NUMERIC_ZOD_TYPES.include?(base_type)
61
+ def self.map_numericality(validation, base_type, array)
62
+ return "" if array || !NUMERIC_ZOD_TYPES.include?(base_type)
61
63
 
62
64
  validation.options.filter_map do |key, value|
63
65
  method = NUMERICALITY_MAP[key]
@@ -65,8 +67,8 @@ module ZodRails
65
67
  end.join
66
68
  end
67
69
 
68
- def self.map_format(validation, base_type)
69
- return "" unless STRING_ZOD_TYPES.include?(base_type)
70
+ def self.map_format(validation, base_type, array)
71
+ return "" if array || !STRING_ZOD_TYPES.include?(base_type)
70
72
 
71
73
  regex = validation.options[:with]
72
74
  return "" unless regex
@@ -74,7 +76,9 @@ module ZodRails
74
76
  RegexpMapper.call(regex)&.then { |expression| ".regex(#{expression})" } || ""
75
77
  end
76
78
 
77
- def self.map_inclusion(validation, base_type)
79
+ def self.map_inclusion(validation, base_type, array)
80
+ return "" if array
81
+
78
82
  values = validation.options[:in] || validation.options[:within]
79
83
  return "" unless values.is_a?(Array)
80
84
 
@@ -112,27 +116,35 @@ module ZodRails
112
116
  ".pipe(z.union([#{literals}]))"
113
117
  end
114
118
 
115
- def self.collect_constraints(validation, base_type, constraints)
119
+ def self.collect_constraints(validation, base_type, constraints, array)
116
120
  return if validation.conditional? || no_op_presence?(validation)
121
+ return collect_array_constraints(validation, base_type, constraints) if array
117
122
 
118
123
  case validation.kind
119
- when :presence then handle_presence_constraint(base_type, constraints)
120
- when :length then handle_length_constraint(validation, base_type, constraints)
124
+ when :presence then handle_presence_constraint(base_type, constraints, false)
125
+ when :length then handle_length_constraint(validation, base_type, constraints, false)
121
126
  when :numericality then handle_numericality_constraint(validation, base_type, constraints)
122
127
  when :format then handle_format_constraint(validation, base_type, constraints)
123
128
  when :inclusion then handle_inclusion_constraint(validation, base_type, constraints)
124
129
  end
125
130
  end
126
131
 
127
- def self.handle_presence_constraint(base_type, constraints)
128
- return unless STRING_ZOD_TYPES.include?(base_type)
132
+ def self.collect_array_constraints(validation, base_type, constraints)
133
+ case validation.kind
134
+ when :presence then handle_presence_constraint(base_type, constraints, true)
135
+ when :length then handle_length_constraint(validation, base_type, constraints, true)
136
+ end
137
+ end
138
+
139
+ def self.handle_presence_constraint(base_type, constraints, array)
140
+ return unless array || STRING_ZOD_TYPES.include?(base_type)
129
141
 
130
142
  constraints[:min] = [constraints[:min] || 0, 1].max
131
- constraints[:others] << presence_suffix
143
+ constraints[:others] << presence_suffix unless array
132
144
  end
133
145
 
134
- def self.handle_length_constraint(validation, base_type, constraints)
135
- return unless STRING_ZOD_TYPES.include?(base_type)
146
+ def self.handle_length_constraint(validation, base_type, constraints, array)
147
+ return unless array || STRING_ZOD_TYPES.include?(base_type)
136
148
 
137
149
  opts = validation.options
138
150
  constraints[:length] = opts[:is] if opts[:is]
@@ -213,14 +225,13 @@ module ZodRails
213
225
  value.is_a?(Numeric) && (!value.respond_to?(:finite?) || value.finite?)
214
226
  end
215
227
 
216
- def self.no_op_presence?(validation)
217
- validation.kind == :presence && validation.options[:allow_blank]
218
- end
228
+ def self.no_op_presence?(validation) = validation.kind == :presence && validation.options[:allow_blank]
219
229
 
220
230
  private_class_method :map_presence, :map_length, :map_numericality, :map_format, :map_inclusion,
221
231
  :build_array_inclusion_suffix, :string_array_for_string_type?,
222
232
  :numeric_array_for_numeric_type?, :build_string_enum_suffix,
223
- :build_numeric_literal_suffix, :collect_constraints, :build_chain,
233
+ :build_numeric_literal_suffix, :collect_constraints, :collect_array_constraints,
234
+ :build_chain,
224
235
  :handle_presence_constraint, :handle_length_constraint, :handle_format_constraint,
225
236
  :handle_inclusion_constraint, :apply_range_inclusion, :apply_array_inclusion,
226
237
  :handle_numericality_constraint, :apply_numeric_range, :presence_suffix, :static_number?,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ZodRails
4
- VERSION = "0.3.0"
4
+ VERSION = "0.3.2"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zod_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Kelly
@@ -75,6 +75,7 @@ files:
75
75
  - CHANGELOG.md
76
76
  - LICENSE
77
77
  - README.md
78
+ - lib/tasks/zod_rails.rake
78
79
  - lib/zod_rails.rb
79
80
  - lib/zod_rails/configuration.rb
80
81
  - lib/zod_rails/generation/file_writer.rb