cmdx 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/.DS_Store +0 -0
- data/.rspec +4 -0
- data/.rubocop.yml +64 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE.txt +21 -0
- data/README.md +76 -0
- data/Rakefile +12 -0
- data/docs/basics/call.md +31 -0
- data/docs/basics/context.md +67 -0
- data/docs/basics/run.md +34 -0
- data/docs/basics/setup.md +32 -0
- data/docs/batch.md +53 -0
- data/docs/configuration.md +57 -0
- data/docs/example.md +82 -0
- data/docs/getting_started.md +47 -0
- data/docs/hooks.md +59 -0
- data/docs/interruptions/exceptions.md +29 -0
- data/docs/interruptions/faults.md +89 -0
- data/docs/interruptions/halt.md +80 -0
- data/docs/logging.md +102 -0
- data/docs/outcomes/result.md +17 -0
- data/docs/outcomes/states.md +31 -0
- data/docs/outcomes/statuses.md +33 -0
- data/docs/parameters/coercions.md +52 -0
- data/docs/parameters/defaults.md +47 -0
- data/docs/parameters/definitions.md +123 -0
- data/docs/parameters/namespacing.md +57 -0
- data/docs/parameters/validations.md +312 -0
- data/docs/tips_and_tricks.md +79 -0
- data/lib/cmdx/.DS_Store +0 -0
- data/lib/cmdx/batch.rb +43 -0
- data/lib/cmdx/coercions/array.rb +15 -0
- data/lib/cmdx/coercions/big_decimal.rb +23 -0
- data/lib/cmdx/coercions/boolean.rb +27 -0
- data/lib/cmdx/coercions/complex.rb +21 -0
- data/lib/cmdx/coercions/date.rb +26 -0
- data/lib/cmdx/coercions/date_time.rb +26 -0
- data/lib/cmdx/coercions/float.rb +21 -0
- data/lib/cmdx/coercions/hash.rb +31 -0
- data/lib/cmdx/coercions/integer.rb +21 -0
- data/lib/cmdx/coercions/rational.rb +21 -0
- data/lib/cmdx/coercions/string.rb +15 -0
- data/lib/cmdx/coercions/time.rb +26 -0
- data/lib/cmdx/coercions/virtual.rb +15 -0
- data/lib/cmdx/configuration.rb +25 -0
- data/lib/cmdx/context.rb +15 -0
- data/lib/cmdx/core_ext/hash.rb +36 -0
- data/lib/cmdx/core_ext/module.rb +48 -0
- data/lib/cmdx/core_ext/object.rb +55 -0
- data/lib/cmdx/error.rb +23 -0
- data/lib/cmdx/errors.rb +92 -0
- data/lib/cmdx/fault.rb +47 -0
- data/lib/cmdx/faults.rb +11 -0
- data/lib/cmdx/immutator.rb +21 -0
- data/lib/cmdx/lazy_struct.rb +79 -0
- data/lib/cmdx/log_formatters/json.rb +13 -0
- data/lib/cmdx/log_formatters/key_value.rb +13 -0
- data/lib/cmdx/log_formatters/line.rb +14 -0
- data/lib/cmdx/log_formatters/logstash.rb +18 -0
- data/lib/cmdx/log_formatters/raw.rb +13 -0
- data/lib/cmdx/logger.rb +16 -0
- data/lib/cmdx/parameter.rb +101 -0
- data/lib/cmdx/parameter_inspector.rb +23 -0
- data/lib/cmdx/parameter_serializer.rb +20 -0
- data/lib/cmdx/parameter_validator.rb +19 -0
- data/lib/cmdx/parameter_value.rb +136 -0
- data/lib/cmdx/parameters.rb +34 -0
- data/lib/cmdx/parameters_inspector.rb +13 -0
- data/lib/cmdx/parameters_serializer.rb +13 -0
- data/lib/cmdx/railtie.rb +32 -0
- data/lib/cmdx/result.rb +170 -0
- data/lib/cmdx/result_inspector.rb +31 -0
- data/lib/cmdx/result_logger.rb +22 -0
- data/lib/cmdx/result_serializer.rb +38 -0
- data/lib/cmdx/run.rb +33 -0
- data/lib/cmdx/run_inspector.rb +21 -0
- data/lib/cmdx/run_serializer.rb +16 -0
- data/lib/cmdx/task.rb +151 -0
- data/lib/cmdx/task_hook.rb +18 -0
- data/lib/cmdx/utils/datetime_formatter.rb +17 -0
- data/lib/cmdx/utils/method_name.rb +24 -0
- data/lib/cmdx/utils/runtime.rb +19 -0
- data/lib/cmdx/validators/custom.rb +20 -0
- data/lib/cmdx/validators/exclusion.rb +51 -0
- data/lib/cmdx/validators/format.rb +27 -0
- data/lib/cmdx/validators/inclusion.rb +51 -0
- data/lib/cmdx/validators/length.rb +114 -0
- data/lib/cmdx/validators/numeric.rb +114 -0
- data/lib/cmdx/validators/presence.rb +27 -0
- data/lib/cmdx/version.rb +7 -0
- data/lib/cmdx.rb +80 -0
- data/lib/generators/cmdx/batch_generator.rb +30 -0
- data/lib/generators/cmdx/install_generator.rb +15 -0
- data/lib/generators/cmdx/task_generator.rb +30 -0
- data/lib/generators/cmdx/templates/batch.rb.tt +7 -0
- data/lib/generators/cmdx/templates/install.rb +23 -0
- data/lib/generators/cmdx/templates/task.rb.tt +9 -0
- data/lib/locales/en.yml +36 -0
- data/lib/locales/es.yml +36 -0
- metadata +288 -0
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CMDx
|
4
|
+
module Validators
|
5
|
+
module Numeric
|
6
|
+
|
7
|
+
extend self
|
8
|
+
|
9
|
+
def call(value, options = {})
|
10
|
+
case options[:numeric]
|
11
|
+
in { within: within }
|
12
|
+
raise_within_validation_error!(within.begin, within.end, options) unless within.cover?(value)
|
13
|
+
in { not_within: not_within }
|
14
|
+
raise_not_within_validation_error!(not_within.begin, not_within.end, options) if not_within.cover?(value)
|
15
|
+
in { in: yn }
|
16
|
+
raise_within_validation_error!(yn.begin, yn.end, options) unless yn.cover?(value)
|
17
|
+
in { not_in: not_in }
|
18
|
+
raise_not_within_validation_error!(not_in.begin, not_in.end, options) if not_in.cover?(value)
|
19
|
+
in { min: min, max: max }
|
20
|
+
raise_within_validation_error!(min, max, options) unless value.between?(min, max)
|
21
|
+
in { min: min }
|
22
|
+
raise_min_validation_error!(min, options) unless min <= value
|
23
|
+
in { max: max }
|
24
|
+
raise_max_validation_error!(max, options) unless value <= max
|
25
|
+
in { is: is }
|
26
|
+
raise_is_validation_error!(is, options) unless value == is
|
27
|
+
in { is_not: is_not }
|
28
|
+
raise_is_not_validation_error!(is_not, options) if value == is_not
|
29
|
+
else
|
30
|
+
raise ArgumentError, "no known numeric validator options given"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def raise_within_validation_error!(min, max, options)
|
37
|
+
message = options.dig(:numeric, :within_message) ||
|
38
|
+
options.dig(:numeric, :in_message) ||
|
39
|
+
options.dig(:numeric, :message)
|
40
|
+
message %= { min:, max: } unless message.nil?
|
41
|
+
|
42
|
+
raise ValidationError, message || I18n.t(
|
43
|
+
"cmdx.validators.numeric.within",
|
44
|
+
min:,
|
45
|
+
max:,
|
46
|
+
default: "must be within #{min} and #{max}"
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
def raise_not_within_validation_error!(min, max, options)
|
51
|
+
message = options.dig(:numeric, :not_within_message) ||
|
52
|
+
options.dig(:numeric, :not_in_message) ||
|
53
|
+
options.dig(:numeric, :message)
|
54
|
+
message %= { min:, max: } unless message.nil?
|
55
|
+
|
56
|
+
raise ValidationError, message || I18n.t(
|
57
|
+
"cmdx.validators.numeric.not_within",
|
58
|
+
min:,
|
59
|
+
max:,
|
60
|
+
default: "must not be within #{min} and #{max}"
|
61
|
+
)
|
62
|
+
end
|
63
|
+
|
64
|
+
def raise_min_validation_error!(min, options)
|
65
|
+
message = options.dig(:numeric, :min_message) ||
|
66
|
+
options.dig(:numeric, :message)
|
67
|
+
message %= { min: } unless message.nil?
|
68
|
+
|
69
|
+
raise ValidationError, message || I18n.t(
|
70
|
+
"cmdx.validators.numeric.min",
|
71
|
+
min:,
|
72
|
+
default: "must be at least #{min}"
|
73
|
+
)
|
74
|
+
end
|
75
|
+
|
76
|
+
def raise_max_validation_error!(max, options)
|
77
|
+
message = options.dig(:numeric, :max_message) ||
|
78
|
+
options.dig(:numeric, :message)
|
79
|
+
message %= { max: } unless message.nil?
|
80
|
+
|
81
|
+
raise ValidationError, message || I18n.t(
|
82
|
+
"cmdx.validators.numeric.max",
|
83
|
+
max:,
|
84
|
+
default: "must be at most #{max}"
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
def raise_is_validation_error!(is, options)
|
89
|
+
message = options.dig(:numeric, :is_message) ||
|
90
|
+
options.dig(:numeric, :message)
|
91
|
+
message %= { is: } unless message.nil?
|
92
|
+
|
93
|
+
raise ValidationError, message || I18n.t(
|
94
|
+
"cmdx.validators.numeric.is",
|
95
|
+
is:,
|
96
|
+
default: "must be #{is}"
|
97
|
+
)
|
98
|
+
end
|
99
|
+
|
100
|
+
def raise_is_not_validation_error!(is_not, options)
|
101
|
+
message = options.dig(:numeric, :is_not_message) ||
|
102
|
+
options.dig(:numeric, :message)
|
103
|
+
message %= { is_not: } unless message.nil?
|
104
|
+
|
105
|
+
raise ValidationError, message || I18n.t(
|
106
|
+
"cmdx.validators.numeric.is_not",
|
107
|
+
is_not:,
|
108
|
+
default: "must not be #{is_not}"
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CMDx
|
4
|
+
module Validators
|
5
|
+
module Presence
|
6
|
+
|
7
|
+
module_function
|
8
|
+
|
9
|
+
def call(value, options = {})
|
10
|
+
return if if value.is_a?(String)
|
11
|
+
/\S/.match?(value)
|
12
|
+
elsif value.respond_to?(:empty?)
|
13
|
+
!value.empty?
|
14
|
+
else
|
15
|
+
!value.nil?
|
16
|
+
end
|
17
|
+
|
18
|
+
message = options.dig(:presence, :message) if options[:presence].is_a?(Hash)
|
19
|
+
raise ValidationError, message || I18n.t(
|
20
|
+
"cmdx.validators.presence",
|
21
|
+
default: "cannot be empty"
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/cmdx/version.rb
ADDED
data/lib/cmdx.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bigdecimal"
|
4
|
+
require "date"
|
5
|
+
require "i18n"
|
6
|
+
require "json"
|
7
|
+
require "logger"
|
8
|
+
require "securerandom"
|
9
|
+
require "time"
|
10
|
+
require "timeout"
|
11
|
+
|
12
|
+
require_relative "cmdx/version"
|
13
|
+
require_relative "cmdx/core_ext/object"
|
14
|
+
require_relative "cmdx/core_ext/hash"
|
15
|
+
require_relative "cmdx/core_ext/module"
|
16
|
+
require_relative "cmdx/log_formatters/json"
|
17
|
+
require_relative "cmdx/log_formatters/key_value"
|
18
|
+
require_relative "cmdx/log_formatters/line"
|
19
|
+
require_relative "cmdx/log_formatters/logstash"
|
20
|
+
require_relative "cmdx/log_formatters/raw"
|
21
|
+
require_relative "cmdx/coercions/array"
|
22
|
+
require_relative "cmdx/coercions/big_decimal"
|
23
|
+
require_relative "cmdx/coercions/boolean"
|
24
|
+
require_relative "cmdx/coercions/complex"
|
25
|
+
require_relative "cmdx/coercions/date"
|
26
|
+
require_relative "cmdx/coercions/date_time"
|
27
|
+
require_relative "cmdx/coercions/float"
|
28
|
+
require_relative "cmdx/coercions/hash"
|
29
|
+
require_relative "cmdx/coercions/integer"
|
30
|
+
require_relative "cmdx/coercions/rational"
|
31
|
+
require_relative "cmdx/coercions/string"
|
32
|
+
require_relative "cmdx/coercions/time"
|
33
|
+
require_relative "cmdx/coercions/virtual"
|
34
|
+
require_relative "cmdx/validators/custom"
|
35
|
+
require_relative "cmdx/validators/exclusion"
|
36
|
+
require_relative "cmdx/validators/format"
|
37
|
+
require_relative "cmdx/validators/inclusion"
|
38
|
+
require_relative "cmdx/validators/length"
|
39
|
+
require_relative "cmdx/validators/numeric"
|
40
|
+
require_relative "cmdx/validators/presence"
|
41
|
+
require_relative "cmdx/utils/datetime_formatter"
|
42
|
+
require_relative "cmdx/utils/method_name"
|
43
|
+
require_relative "cmdx/utils/runtime"
|
44
|
+
require_relative "cmdx/error"
|
45
|
+
require_relative "cmdx/errors"
|
46
|
+
require_relative "cmdx/fault"
|
47
|
+
require_relative "cmdx/faults"
|
48
|
+
require_relative "cmdx/logger"
|
49
|
+
require_relative "cmdx/lazy_struct"
|
50
|
+
require_relative "cmdx/configuration"
|
51
|
+
require_relative "cmdx/context"
|
52
|
+
require_relative "cmdx/run"
|
53
|
+
require_relative "cmdx/run_serializer"
|
54
|
+
require_relative "cmdx/run_inspector"
|
55
|
+
require_relative "cmdx/parameter"
|
56
|
+
require_relative "cmdx/parameter_value"
|
57
|
+
require_relative "cmdx/parameter_validator"
|
58
|
+
require_relative "cmdx/parameter_serializer"
|
59
|
+
require_relative "cmdx/parameter_inspector"
|
60
|
+
require_relative "cmdx/parameters"
|
61
|
+
require_relative "cmdx/parameters_serializer"
|
62
|
+
require_relative "cmdx/parameters_inspector"
|
63
|
+
require_relative "cmdx/result"
|
64
|
+
require_relative "cmdx/result_serializer"
|
65
|
+
require_relative "cmdx/result_inspector"
|
66
|
+
require_relative "cmdx/result_logger"
|
67
|
+
require_relative "cmdx/task"
|
68
|
+
require_relative "cmdx/task_hook"
|
69
|
+
require_relative "cmdx/batch"
|
70
|
+
require_relative "cmdx/immutator"
|
71
|
+
|
72
|
+
if defined?(Rails::Generators)
|
73
|
+
require_relative "generators/cmdx/install_generator"
|
74
|
+
require_relative "generators/cmdx/task_generator"
|
75
|
+
require_relative "generators/cmdx/batch_generator"
|
76
|
+
end
|
77
|
+
|
78
|
+
# Load the Railtie last after everything else is required so we don't
|
79
|
+
# need to load any CMDx components when we use this Railtie.
|
80
|
+
require_relative "cmdx/railtie" if defined?(Rails::Railtie)
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cmdx
|
4
|
+
class BatchGenerator < Rails::Generators::NamedBase
|
5
|
+
|
6
|
+
source_root File.expand_path("templates", __dir__)
|
7
|
+
check_class_collision prefix: "Batch"
|
8
|
+
|
9
|
+
desc "Generates a batch task with the given NAME (if one does not exist)."
|
10
|
+
|
11
|
+
def copy_files
|
12
|
+
name = file_name.sub(/^batch_?/i, "")
|
13
|
+
path = File.join("app/cmds", class_path, "batch_#{name}.rb")
|
14
|
+
template("batch.rb.tt", path)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def class_name
|
20
|
+
@class_name ||= super.delete_prefix("Batch")
|
21
|
+
end
|
22
|
+
|
23
|
+
def parent_class_name
|
24
|
+
ApplicationBatch
|
25
|
+
rescue StandardError
|
26
|
+
CMDx::Batch
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cmdx
|
4
|
+
class InstallGenerator < Rails::Generators::Base
|
5
|
+
|
6
|
+
source_root File.expand_path("templates", __dir__)
|
7
|
+
|
8
|
+
desc "Generates a CMDx configurations files for global settings."
|
9
|
+
|
10
|
+
def copy_initializer_file
|
11
|
+
copy_file("install.rb", "config/initializers/cmdx.rb")
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cmdx
|
4
|
+
class TaskGenerator < Rails::Generators::NamedBase
|
5
|
+
|
6
|
+
source_root File.expand_path("templates", __dir__)
|
7
|
+
check_class_collision suffix: "Task"
|
8
|
+
|
9
|
+
desc "Generates a task with the given NAME (if one does not exist)."
|
10
|
+
|
11
|
+
def copy_files
|
12
|
+
name = file_name.sub(/_?task$/i, "")
|
13
|
+
path = File.join("app/cmds", class_path, "#{name}_task.rb")
|
14
|
+
template("task.rb.tt", path)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def class_name
|
20
|
+
@class_name ||= super.end_with?("Task") ? super : "#{super}Task"
|
21
|
+
end
|
22
|
+
|
23
|
+
def parent_class_name
|
24
|
+
ApplicationTask
|
25
|
+
rescue StandardError
|
26
|
+
CMDx::Task
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
CMDx.configure do |config|
|
4
|
+
# Define which statuses a bang `call!` will halt and raise a fault.
|
5
|
+
# This option can accept an array of statuses.
|
6
|
+
config.task_halt = CMDx::Result::FAILED
|
7
|
+
|
8
|
+
# Enable task timeouts to prevent call execution beyond a defined threshold.
|
9
|
+
config.task_timeout = nil
|
10
|
+
|
11
|
+
# Define which statuses a batch task will halt execution from proceeding to the next step.
|
12
|
+
# By default skipped tasks are treated as a NOOP so processing is continued.
|
13
|
+
# This option can accept an array of statuses.
|
14
|
+
config.batch_halt = CMDx::Result::FAILED
|
15
|
+
|
16
|
+
# Enable batch timeouts to prevent call execution beyond a defined threshold.
|
17
|
+
# TIP: remember to account for all defined tasks when setting this value
|
18
|
+
config.batch_timeout = nil
|
19
|
+
|
20
|
+
# A list of available log formatter can be found at:
|
21
|
+
# https://github.com/drexed/cmdx/tree/main/lib/cmdx/log_formatters
|
22
|
+
config.logger = Logger.new($stdout, formatter: CMDx::LogFormatters::Line.new)
|
23
|
+
end
|
data/lib/locales/en.yml
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
en:
|
2
|
+
cmdx:
|
3
|
+
coercions:
|
4
|
+
into_a: "could not coerce into a %{type}"
|
5
|
+
into_an: "could not coerce into an %{type}"
|
6
|
+
into_any: "could not coerce into one of: %{values}"
|
7
|
+
unknown: "unknown %{type} coercion type"
|
8
|
+
faults:
|
9
|
+
unspecified: no reason given
|
10
|
+
parameters:
|
11
|
+
required: is a required parameter
|
12
|
+
undefined: delegates to undefined method %{source}
|
13
|
+
validators:
|
14
|
+
custom: is not valid
|
15
|
+
exclusion:
|
16
|
+
of: "must not be one of: %{values}"
|
17
|
+
within: must not be within %{min} and %{max}
|
18
|
+
format: is an invalid format
|
19
|
+
inclusion:
|
20
|
+
of: "must be one of: %{values}"
|
21
|
+
within: must be within %{min} and %{max}
|
22
|
+
length:
|
23
|
+
is: length must be %{is}
|
24
|
+
is_not: length must not be %{is_not}
|
25
|
+
min: length must be at least %{min}
|
26
|
+
max: length must be at most %{max}
|
27
|
+
not_within: length must not be within %{min} and %{max}
|
28
|
+
within: length must be within %{min} and %{max}
|
29
|
+
numeric:
|
30
|
+
is: must be %{is}
|
31
|
+
is_not: must not be %{is_not}
|
32
|
+
min: must be at least %{min}
|
33
|
+
max: must be at most %{max}
|
34
|
+
not_within: must not be within %{min} and %{max}
|
35
|
+
within: must be within %{min} and %{max}
|
36
|
+
presence: cannot be empty
|
data/lib/locales/es.yml
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
es:
|
2
|
+
cmdx:
|
3
|
+
coercions:
|
4
|
+
into_a: "no podía coacciona el valor a un %{type}"
|
5
|
+
into_an: "no podía coacciona el valor a un %{type}"
|
6
|
+
into_any: "no podía coacciona el valor a un: %{values}"
|
7
|
+
unknown: "%{type} tipo de coacciona desconocido"
|
8
|
+
faults:
|
9
|
+
unspecified: ninguna razón dada
|
10
|
+
parameters:
|
11
|
+
required: es un parámetro requerido
|
12
|
+
undefined: delegado al método indefinido %{source}
|
13
|
+
validators:
|
14
|
+
custom: no es válida
|
15
|
+
exclusion:
|
16
|
+
of: "no debe ser uno de: %{values}"
|
17
|
+
within: no debe estar dentro %{min} y %{max}
|
18
|
+
format: es un formato inválido
|
19
|
+
inclusion:
|
20
|
+
of: "debe ser uno de: %{values}"
|
21
|
+
within: debe estar dentro %{min} y %{max}
|
22
|
+
length:
|
23
|
+
is: tiene una longitud que debe ser %{is}
|
24
|
+
is_not: tiene una longitud no debe ser %{is}
|
25
|
+
min: tiene una longitud que debe ser menos de %{min}
|
26
|
+
max: tiene una longitud que debe ser mas de %{max}
|
27
|
+
not_within: tiene una longitud que no debe estar dentro %{min} y %{max}
|
28
|
+
within: tiene una longitud que debe estar dentro %{min} y %{max}
|
29
|
+
numeric:
|
30
|
+
is: debe ser %{is}
|
31
|
+
is_not: no debe ser %{is}
|
32
|
+
min: debe ser %{min} como minimo
|
33
|
+
max: debe ser %{max} como máximo
|
34
|
+
not_within: no debe estar dentro %{min} y %{max}
|
35
|
+
within: debe estar dentro %{min} y %{max}
|
36
|
+
presence: no puede estar vacío
|