Module: Clack::Validators

Defined in:
lib/clack/validators.rb

Overview

Built-in validators for common validation patterns. Use these with the ‘validate:` option on prompts.

Validation procs can perform any operation including slow I/O (database lookups, API calls, etc.) - they simply block until complete.

Examples:

Using built-in validators

Clack.text(message: "Name?", validate: Clack::Validators.required)
Clack.text(message: "Email?", validate: Clack::Validators.format(/@/, "Must be an email"))
Clack.password(message: "Password?", validate: Clack::Validators.min_length(8))

Database validation (blocking I/O)

Clack.text(
  message: "Email?",
  validate: ->(email) {
    "Already taken" if User.exists?(email: email)
  }
)

Combining validators

Clack.text(
  message: "Username?",
  validate: Clack::Validators.combine(
    Clack::Validators.required("Username is required"),
    Clack::Validators.min_length(3, "Must be at least 3 characters"),
    Clack::Validators.max_length(20, "Must be at most 20 characters"),
    Clack::Validators.format(/\A[a-z0-9_]+\z/i, "Only letters, numbers, and underscores")
  )
)

Class Method Summary collapse

Class Method Details

.as_warning(validator) ⇒ Proc

Convert any validator to return a warning instead of an error. Warnings allow the user to proceed with confirmation.

Examples:

# Make max_length a warning instead of error
Clack.text(
  message: "Bio?",
  validate: Clack::Validators.as_warning(
    Clack::Validators.max_length(100, "Bio is quite long")
  )
)

Parameters:

  • validator (Proc)

    Original validator

Returns:

  • (Proc)

    Validator that returns Warning instead of String



199
200
201
202
203
204
205
206
# File 'lib/clack/validators.rb', line 199

def as_warning(validator)
  lambda do |value|
    result = validator.call(value)
    next if result.nil?

    result.is_a?(Clack::Warning) ? result : Clack::Warning.new(result)
  end
end

.combine(*validators) ⇒ Proc

Combines multiple validators. Returns the first error message, or nil if all pass.

Parameters:

  • validators (Array<Proc>)

    Validators to combine

Returns:

  • (Proc)

    Combined validator proc



109
110
111
# File 'lib/clack/validators.rb', line 109

def combine(*validators)
  ->(value) { first_failing_validation(validators, value) }
end

.date_range(min:, max:, message: nil) ⇒ Proc

Validates that the date is within a given range.

Parameters:

  • min (Date)

    Minimum date

  • max (Date)

    Maximum date

  • message (String, nil) (defaults to: nil)

    Custom error message

Returns:

  • (Proc)

    Validator proc



169
170
171
172
# File 'lib/clack/validators.rb', line 169

def date_range(min:, max:, message: nil)
  msg = message || "Date must be between #{min} and #{max}"
  ->(date) { msg unless (min..max).cover?(date) }
end

.directory_exists(message = "Directory does not exist") ⇒ Proc

Validates directory path exists.

Parameters:

  • message (String) (defaults to: "Directory does not exist")

    Error message

Returns:

  • (Proc)

    Validator proc



141
142
143
# File 'lib/clack/validators.rb', line 141

def directory_exists(message = "Directory does not exist")
  ->(value) { message unless File.directory?(value.to_s) }
end

.email(message = "Must be a valid email address") ⇒ Proc

Common email format validator.

Parameters:

  • message (String) (defaults to: "Must be a valid email address")

    Error message

Returns:

  • (Proc)

    Validator proc



117
118
119
# File 'lib/clack/validators.rb', line 117

def email(message = "Must be a valid email address")
  format(/\A[^@\s]+@[^@\s]+\.[^@\s]+\z/, message)
end

.file_exists_warning(message = "File already exists. Overwrite?") ⇒ Proc

Warning if file exists. Allows user to confirm overwrite.

Examples:

Clack.text(message: "Output file?", validate: Clack::Validators.file_exists_warning)

Parameters:

  • message (String) (defaults to: "File already exists. Overwrite?")

    Warning message

Returns:

  • (Proc)

    Validator proc returning Warning



181
182
183
# File 'lib/clack/validators.rb', line 181

def file_exists_warning(message = "File already exists. Overwrite?")
  ->(value) { Clack::Warning.new(message) if File.exist?(value.to_s) }
end

.format(pattern, message = "Invalid format") ⇒ Proc

Validates that input matches a regular expression.

Parameters:

  • pattern (Regexp)

    Pattern to match

  • message (String) (defaults to: "Invalid format")

    Error message if pattern doesn’t match

Returns:

  • (Proc)

    Validator proc



69
70
71
# File 'lib/clack/validators.rb', line 69

def format(pattern, message = "Invalid format")
  ->(value) { message unless pattern.match?(value.to_s) }
end

.future_date(message = "Date must be in the future") ⇒ Proc

Validates that the date is strictly after today. Today itself is not considered “future” and will fail validation.

Parameters:

  • message (String) (defaults to: "Date must be in the future")

    Error message

Returns:

  • (Proc)

    Validator proc



150
151
152
# File 'lib/clack/validators.rb', line 150

def future_date(message = "Date must be in the future")
  ->(date) { message if date <= Date.today }
end

.in_range(range, message = nil) ⇒ Proc

Validates that input is within a numeric range. Note: Parses value as integer for comparison.

Parameters:

  • range (Range)

    Allowed range

  • message (String, nil) (defaults to: nil)

    Custom error message

Returns:

  • (Proc)

    Validator proc



97
98
99
100
101
102
103
# File 'lib/clack/validators.rb', line 97

def in_range(range, message = nil)
  msg = message || "Must be between #{range.first} and #{range.last}"
  lambda do |value|
    int_val = value.to_s.to_i
    msg unless range.cover?(int_val) && value.to_s.match?(/\A-?\d+\z/)
  end
end

.integer(message = "Must be a number") ⇒ Proc

Validates that input is a valid integer.

Parameters:

  • message (String) (defaults to: "Must be a number")

    Error message

Returns:

  • (Proc)

    Validator proc



87
88
89
# File 'lib/clack/validators.rb', line 87

def integer(message = "Must be a number")
  ->(value) { message unless value.to_s.match?(/\A-?\d+\z/) }
end

.max_length(length, message = nil) ⇒ Proc

Validates maximum length.

Parameters:

  • length (Integer)

    Maximum length

  • message (String, nil) (defaults to: nil)

    Custom error message

Returns:

  • (Proc)

    Validator proc



59
60
61
62
# File 'lib/clack/validators.rb', line 59

def max_length(length, message = nil)
  msg = message || "Must be at most #{length} characters"
  ->(value) { msg if value.to_s.length > length }
end

.min_length(length, message = nil) ⇒ Proc

Validates minimum length.

Parameters:

  • length (Integer)

    Minimum length

  • message (String, nil) (defaults to: nil)

    Custom error message

Returns:

  • (Proc)

    Validator proc



49
50
51
52
# File 'lib/clack/validators.rb', line 49

def min_length(length, message = nil)
  msg = message || "Must be at least #{length} characters"
  ->(value) { msg if value.to_s.length < length }
end

.one_of(allowed, message = nil) ⇒ Proc

Validates that input is in a list of allowed values.

Parameters:

  • allowed (Array)

    Allowed values

  • message (String, nil) (defaults to: nil)

    Custom error message

Returns:

  • (Proc)

    Validator proc



78
79
80
81
# File 'lib/clack/validators.rb', line 78

def one_of(allowed, message = nil)
  msg = message || "Must be one of: #{allowed.join(", ")}"
  ->(value) { msg unless allowed.include?(value) }
end

.past_date(message = "Date must be in the past") ⇒ Proc

Validates that the date is strictly before today. Today itself is not considered “past” and will fail validation.

Parameters:

  • message (String) (defaults to: "Date must be in the past")

    Error message

Returns:

  • (Proc)

    Validator proc



159
160
161
# File 'lib/clack/validators.rb', line 159

def past_date(message = "Date must be in the past")
  ->(date) { message if date >= Date.today }
end

.path_exists(message = "Path does not exist") ⇒ Proc

Validates file path exists.

Parameters:

  • message (String) (defaults to: "Path does not exist")

    Error message

Returns:

  • (Proc)

    Validator proc



133
134
135
# File 'lib/clack/validators.rb', line 133

def path_exists(message = "Path does not exist")
  ->(value) { message unless File.exist?(value.to_s) }
end

.required(message = "This field is required") ⇒ Proc

Validates that the input is not empty.

Parameters:

  • message (String) (defaults to: "This field is required")

    Custom error message

Returns:

  • (Proc)

    Validator proc



40
41
42
# File 'lib/clack/validators.rb', line 40

def required(message = "This field is required")
  ->(value) { message if value.to_s.strip.empty? }
end

.url(message = "Must be a valid URL") ⇒ Proc

Common URL format validator.

Parameters:

  • message (String) (defaults to: "Must be a valid URL")

    Error message

Returns:

  • (Proc)

    Validator proc



125
126
127
# File 'lib/clack/validators.rb', line 125

def url(message = "Must be a valid URL")
  format(%r{\Ahttps?://\S+\z}, message)
end