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,123 @@
|
|
1
|
+
## Parameters - Definitions
|
2
|
+
|
3
|
+
Parameters provide a contract to verify that a task only executes if the arguments
|
4
|
+
of a call match.
|
5
|
+
|
6
|
+
## Basics
|
7
|
+
|
8
|
+
Parameters are defined based on methods that can be delegated to a source object
|
9
|
+
(default `:context`) or keys on a hash. Parameters are automatically defined as
|
10
|
+
attributes within the task instance. `optional` parameters that do not respond or
|
11
|
+
missing the hash key will still delegate but return `nil` as a value.
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
class DetermineBoxSizeTask < CMDx::Task
|
15
|
+
|
16
|
+
# Must be passed as call arguments
|
17
|
+
required :material
|
18
|
+
|
19
|
+
# Returns value if passed as a call arguments, else returns nil
|
20
|
+
optional :depth
|
21
|
+
|
22
|
+
# Define multiple parameters one line
|
23
|
+
optional :width, :height
|
24
|
+
|
25
|
+
def call
|
26
|
+
material #=> "cardboard"
|
27
|
+
depth #=> nil
|
28
|
+
height #=> 12
|
29
|
+
width #=> 24
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
# Initializes local variables matching the parameter name
|
35
|
+
DetermineBoxSizeTask.call(material: "cardboard", height: 12, width: 24)
|
36
|
+
```
|
37
|
+
|
38
|
+
## Source
|
39
|
+
|
40
|
+
Parameters will be delegated to the task context by default but any delegatable
|
41
|
+
object within the task will do.
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
class UpdateUserDetailsTask < CMDx::Task
|
45
|
+
|
46
|
+
# Default (:context)
|
47
|
+
required :user
|
48
|
+
|
49
|
+
# Defined parameter
|
50
|
+
required :email, source: :user
|
51
|
+
|
52
|
+
# Proc or lambda
|
53
|
+
optional :address, source: -> { user.address }
|
54
|
+
|
55
|
+
# Symbol or string
|
56
|
+
optional :name, source: :company
|
57
|
+
|
58
|
+
def call
|
59
|
+
user #=> <User #a1b2c3d>
|
60
|
+
email #=> "bill@bigcorp.com"
|
61
|
+
address #=> "123 Maple St, Miami, Fl 33023"
|
62
|
+
name #=> "Big Corp."
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def company
|
68
|
+
user.account.company
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
# Hash or delegatable object
|
74
|
+
user = User.new(email: "bill@bigcorp.com")
|
75
|
+
UpdateUserDetailsTask.call(user: user)
|
76
|
+
```
|
77
|
+
|
78
|
+
## Nesting
|
79
|
+
|
80
|
+
Nesting builds upon parameter source option. Build complex parameter blocks that
|
81
|
+
delegate to the parent parameter automatically.
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
class UpdateUserDetailsTask < CMDx::Task
|
85
|
+
|
86
|
+
required :address do
|
87
|
+
required :street1
|
88
|
+
optional :street2
|
89
|
+
end
|
90
|
+
|
91
|
+
optional :locality do
|
92
|
+
required :city, :state # Required if locality argument is passed
|
93
|
+
optional :zipcode
|
94
|
+
end
|
95
|
+
|
96
|
+
def call
|
97
|
+
address #=> { city: "Miami", state: "Fl" }
|
98
|
+
street1 #=> "123 Maple St."
|
99
|
+
street2 #=> nil
|
100
|
+
|
101
|
+
locality #=> { city: "Miami", state: "Fl" }
|
102
|
+
city #=> "Miami"
|
103
|
+
state #=> "Fl"
|
104
|
+
zipcode #=> nil
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
# Hash or delegatable object
|
110
|
+
address = Address.new(street1: "123 Maple St.")
|
111
|
+
locality = { city: "Miami", state: "Fl" }
|
112
|
+
UpdateUserDetailsTask.call(address: address, locality: locality)
|
113
|
+
```
|
114
|
+
|
115
|
+
> [!NOTE]
|
116
|
+
> Optional parent parameters that have required child parameters will only have
|
117
|
+
> the child parameters be required if the parent option is a delegatable source
|
118
|
+
> or default value.
|
119
|
+
|
120
|
+
---
|
121
|
+
|
122
|
+
- **Prev:** [Parameters](https://github.com/drexed/cmdx/blob/main/docs/parameters.md)
|
123
|
+
- **Next:** [Namespacing](https://github.com/drexed/cmdx/blob/main/docs/parameters/namespacing.md)
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# Parameters - Namespacing
|
2
|
+
|
3
|
+
Parameters can have the delegated method prefixed and/or suffixed to
|
4
|
+
prevent clashing where the source object have methods with the same name.
|
5
|
+
|
6
|
+
`:prefix` and `:suffix` can be used independently or both at the same time.
|
7
|
+
|
8
|
+
## With fixed value
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
class DetermineBoxSizeTask < CMDx::Task
|
12
|
+
|
13
|
+
required :width, prefix: :before_
|
14
|
+
required :height, suffix: "_after"
|
15
|
+
|
16
|
+
def call
|
17
|
+
before_width #=> 1
|
18
|
+
height_after #=> 2
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
# Call arguments match the parameter names
|
24
|
+
DetermineBoxSizeTask.call(width: 1, height: 2)
|
25
|
+
```
|
26
|
+
|
27
|
+
## With source name
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
class DetermineBoxSizeTask < CMDx::Task
|
31
|
+
|
32
|
+
# Default (:context) as source
|
33
|
+
optional :height, prefix: true
|
34
|
+
|
35
|
+
# Custom source
|
36
|
+
optional :width, source: :account, suffix: true
|
37
|
+
|
38
|
+
def call
|
39
|
+
context_height #=> 1
|
40
|
+
width_account #=> 2
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
# Call arguments match the parameter names
|
46
|
+
account = Account.new(width: 2)
|
47
|
+
DetermineBoxSizeTask.call(height: 1, account: account)
|
48
|
+
```
|
49
|
+
|
50
|
+
> [!NOTE]
|
51
|
+
> `:prefix` or `:suffix` with a custom source and a fixed value
|
52
|
+
> will always return the fixed value without the source.
|
53
|
+
|
54
|
+
---
|
55
|
+
|
56
|
+
- **Prev:** [Definitions](https://github.com/drexed/cmdx/blob/main/docs/parameters/definitions.md)
|
57
|
+
- **Next:** [Coercions](https://github.com/drexed/cmdx/blob/main/docs/parameters/coercions.md)
|
@@ -0,0 +1,312 @@
|
|
1
|
+
# Parameters - Validations
|
2
|
+
|
3
|
+
Parameter values can be validated using one of the built-in validators. Build custom validators
|
4
|
+
to check parameter values against your own business logic. `i18n` internalization is supported
|
5
|
+
out of the box.
|
6
|
+
|
7
|
+
Built-in validators are: [Presence](#presence), [Format](#format), [Exclusion](#exclusion),
|
8
|
+
[Inclusion](#inclusion), [Length](#length), [Numeric](#numeric), [Custom](#custom)
|
9
|
+
|
10
|
+
All validators support the following common options:
|
11
|
+
|
12
|
+
| Option | Description |
|
13
|
+
| ------------ | ----------- |
|
14
|
+
| `:allow_nil` | Skip validation if the parameter value is `nil`. |
|
15
|
+
| `:if` | Specifies a callable method, proc or string to determine if the validation should occur. |
|
16
|
+
| `:unless` | Specifies a callable method, proc, or string to determine if the validation should not occur. |
|
17
|
+
| `:message` | The error message to use for a violation. Fallback for any error key below that's not provided. |
|
18
|
+
|
19
|
+
> [!NOTE]
|
20
|
+
> Validators on `optional` parameters will only be executed if they are supplied as
|
21
|
+
> call arguments.
|
22
|
+
|
23
|
+
## Presence
|
24
|
+
|
25
|
+
Validates that the specified parameter value is not empty. If you want to validate the
|
26
|
+
presence of a boolean field (where the real values are true and false), you will
|
27
|
+
want to use `inclusion: { in: [true, false] }`.
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
class UpdateUserDetailsTask < CMDx::Task
|
31
|
+
|
32
|
+
# Boolean
|
33
|
+
required :username, presence: true
|
34
|
+
|
35
|
+
# With custom error message
|
36
|
+
optional :email, presence: { message: "must not be empty" }
|
37
|
+
|
38
|
+
def call
|
39
|
+
# Do work...
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
## Format
|
46
|
+
|
47
|
+
Validates whether the specified parameter value is of the correct form,
|
48
|
+
going by the regular expression provided.
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
class UpdateUserDetailsTask < CMDx::Task
|
52
|
+
|
53
|
+
# With
|
54
|
+
required :username, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i }
|
55
|
+
|
56
|
+
# Without (with proc if conditional)
|
57
|
+
optional :email, format: { without: /NOSPAM/, if: proc { Current.account.spam_check? } }
|
58
|
+
|
59
|
+
def call
|
60
|
+
# Do work...
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
```
|
65
|
+
|
66
|
+
Constraint options:
|
67
|
+
|
68
|
+
| Option | Description |
|
69
|
+
| ---------- | ----------- |
|
70
|
+
| `:with` | Regular expression that if the parameter value matches will result in a successful validation. |
|
71
|
+
| `:without` | Regular expression that if the parameter value does not match will result in a successful validation. |
|
72
|
+
|
73
|
+
## Exclusion
|
74
|
+
|
75
|
+
Validates that the specified parameter is not in a particular enumerable object.
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
class DetermineBoxSizeTask < CMDx::Task
|
79
|
+
|
80
|
+
# Array
|
81
|
+
required :width, exclusion: { in: [12, 24, 36] }
|
82
|
+
|
83
|
+
# Range (with allow nil)
|
84
|
+
optional :length, exclusion: { in: 12..36, allow_nil: true }
|
85
|
+
|
86
|
+
def call
|
87
|
+
# Do work...
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
```
|
92
|
+
|
93
|
+
Constraint options:
|
94
|
+
|
95
|
+
| Option | Description |
|
96
|
+
| ------------ | ----------- |
|
97
|
+
| `:in` | An enumerable object of unavailable items such as an array or range. |
|
98
|
+
| `:within` | A synonym (or alias) for `:in` |
|
99
|
+
|
100
|
+
Other options:
|
101
|
+
|
102
|
+
| Option | Description |
|
103
|
+
| ----------------- | ----------- |
|
104
|
+
| `:of_message` | The error message if the parameter value is in array. (default: "must not be one of: %{values}") |
|
105
|
+
| `:in_message` | The error message if the parameter value is in range. (default: "must not be within %{min} and %{max}") |
|
106
|
+
| `:within_message` | A synonym (or alias) for `:in_message` |
|
107
|
+
|
108
|
+
## Inclusion
|
109
|
+
|
110
|
+
Validates that the specified parameter value is in a particular enumerable object.
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
class DetermineBoxSizeTask < CMDx::Task
|
114
|
+
|
115
|
+
# Array
|
116
|
+
required :width, inclusion: { in: [12, 24, 36] }
|
117
|
+
|
118
|
+
# Range (with custom error message)
|
119
|
+
optional :length, inclusion: { in: 12..36, unless: :length_check? }
|
120
|
+
|
121
|
+
def call
|
122
|
+
# Do work...
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
def skip_length_check?
|
128
|
+
false
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
```
|
133
|
+
|
134
|
+
Constraint options:
|
135
|
+
|
136
|
+
| Option | Description |
|
137
|
+
| ------------ | ----------- |
|
138
|
+
| `:in` | An enumerable object of available items such as an array or range. |
|
139
|
+
| `:within` | A synonym (or alias) for `:in` |
|
140
|
+
|
141
|
+
Other options:
|
142
|
+
|
143
|
+
| Option | Description |
|
144
|
+
| ----------------- | ----------- |
|
145
|
+
| `:of_message` | The error message if the parameter value is not in array. (default: "must be one of: %{values}") |
|
146
|
+
| `:in_message` | The error message if the parameter value is not in range. (default: "must be within %{min} and %{max}") |
|
147
|
+
| `:within_message` | A synonym (or alias) for `:in_message` |
|
148
|
+
|
149
|
+
## Length
|
150
|
+
|
151
|
+
Validates that the specified parameter value matches the length restrictions supplied.
|
152
|
+
Only one constraint option can be used at a time apart from `:min` and `:max`
|
153
|
+
that can be combined together:
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
class UpdateUserDetailsTask < CMDx::Task
|
157
|
+
|
158
|
+
# Range (with custom error message)
|
159
|
+
required :email, length: { within: 12..36, message: "must be within range" }
|
160
|
+
required :username, length: { not_within: 48..96 }
|
161
|
+
|
162
|
+
# Boundary
|
163
|
+
optional :first_name, length: { min: 24 }
|
164
|
+
optional :middle_name, length: { max: 48 }
|
165
|
+
optional :last_name, length: { min: 24, max: 48 }
|
166
|
+
|
167
|
+
# Exact
|
168
|
+
required :title, length: { is: 24 }
|
169
|
+
required :count, length: { is_not: 48 }
|
170
|
+
|
171
|
+
def call
|
172
|
+
# Do work...
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
```
|
177
|
+
|
178
|
+
Constraint options:
|
179
|
+
|
180
|
+
| Option | Description |
|
181
|
+
| ------------- | ----------- |
|
182
|
+
| `:within` | A range specifying the minimum and maximum size of the parameter value. |
|
183
|
+
| `:not_within` | A range specifying the minimum and maximum size of the parameter value it's not to be. |
|
184
|
+
| `:in` | A synonym (or alias) for `:within` |
|
185
|
+
| `:not_in` | A synonym (or alias) for `:not_within` |
|
186
|
+
| `:min` | The minimum size of the parameter value. |
|
187
|
+
| `:max` | The maximum size of the parameter value. |
|
188
|
+
| `:is` | The exact size of the parameter value. |
|
189
|
+
| `:is_not` | The exact size of the parameter value it's not to be. |
|
190
|
+
|
191
|
+
Other options:
|
192
|
+
|
193
|
+
| Option | Description |
|
194
|
+
| --------------------- | ----------- |
|
195
|
+
| `:within_message` | The error message if the parameter value is within the value range. (default: "length must not be within %{min} and %{max}") |
|
196
|
+
| `:not_within_message` | The error message if the parameter value is not within the value range. (default: "length must be within %{min} and %{max}") |
|
197
|
+
| `:in_message` | A synonym (or alias) for `:within_message` |
|
198
|
+
| `:not_in_message` | A synonym (or alias) for `:not_within_message` |
|
199
|
+
| `:min_message` | The error message if the parameter value is below the min value. (default: "length must be at least %{min}") |
|
200
|
+
| `:max_message` | The error message if the parameter value is above the min value. (default: "length must be at most %{max}") |
|
201
|
+
| `:is_message` | The error message if the parameter value is the exact value. (default: "length must be %{is}") |
|
202
|
+
| `:is_not_message` | The error message if the parameter value is not the exact value. (default: "length must not be %{is_not}") |
|
203
|
+
|
204
|
+
## Numeric
|
205
|
+
|
206
|
+
Validates that the specified parameter value matches the numeric restrictions supplied.
|
207
|
+
Only one constraint option can be used at a time apart from `:min` and `:max`
|
208
|
+
that can be combined together:
|
209
|
+
|
210
|
+
```ruby
|
211
|
+
class UpdateUserDetailsTask < CMDx::Task
|
212
|
+
|
213
|
+
# Range (with custom error message)
|
214
|
+
required :height, numeric: { within: 36..196 }
|
215
|
+
required :weight, numeric: { not_within: 0..5 }
|
216
|
+
|
217
|
+
# Boundary
|
218
|
+
optional :dob_year, numeric: { min: 1900 }
|
219
|
+
optional :dob_day, numeric: { max: 31 }
|
220
|
+
optional :dob_month, numeric: { min: 1, max: 12 }
|
221
|
+
|
222
|
+
# Exact
|
223
|
+
required :age, numeric: { is: 18 }
|
224
|
+
required :parents, numeric: { is_not: 0 }
|
225
|
+
|
226
|
+
def call
|
227
|
+
# Do work...
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
231
|
+
```
|
232
|
+
|
233
|
+
Constraint options:
|
234
|
+
|
235
|
+
| Option | Description |
|
236
|
+
| ------------- | ----------- |
|
237
|
+
| `:within` | A range specifying the minimum and maximum size of the parameter value. |
|
238
|
+
| `:not_within` | A range specifying the minimum and maximum size of the parameter value it's not to be. |
|
239
|
+
| `:in` | A synonym (or alias) for `:within` |
|
240
|
+
| `:not_in` | A synonym (or alias) for `:not_within` |
|
241
|
+
| `:min` | The minimum size of the parameter value. |
|
242
|
+
| `:max` | The maximum size of the parameter value. |
|
243
|
+
| `:is` | The exact size of the parameter value. |
|
244
|
+
| `:is_not` | The exact size of the parameter value it's not to be. |
|
245
|
+
|
246
|
+
Other options:
|
247
|
+
|
248
|
+
| Option | Description |
|
249
|
+
| --------------------- | ----------- |
|
250
|
+
| `:within_message` | The error message if the parameter value is within the value range. (default: "must not be within %{min} and %{max}") |
|
251
|
+
| `:not_within_message` | The error message if the parameter value is not within the value range. (default: "must be within %{min} and %{max}") |
|
252
|
+
| `:in_message` | A synonym (or alias) for `:within_message` |
|
253
|
+
| `:not_in_message` | A synonym (or alias) for `:not_within_message` |
|
254
|
+
| `:min_message` | The error message if the parameter value is below the min value. (default: "must be at least %{min}") |
|
255
|
+
| `:max_message` | The error message if the parameter value is above the min value. (default: "must be at most %{max}") |
|
256
|
+
| `:is_message` | The error message if the parameter value is the exact value. (default: "must be %{is}") |
|
257
|
+
| `:is_not_message` | The error message if the parameter value is not the exact value. (default: "must not be %{is_not}") |
|
258
|
+
|
259
|
+
## Custom
|
260
|
+
|
261
|
+
Validate the specified parameter value using custom validators.
|
262
|
+
|
263
|
+
```ruby
|
264
|
+
class TLDValidator
|
265
|
+
def self.call(value, options)
|
266
|
+
tld = options.dig(:custom, :tld) || %w[com]
|
267
|
+
value.ends_with?(tld)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
class UpdateUserDetailsTask < CMDx::Task
|
272
|
+
|
273
|
+
# Basic
|
274
|
+
required :unconfirmed_email, custom: { validator: TLDValidator }
|
275
|
+
|
276
|
+
# Passable options (with custom error message)
|
277
|
+
optional :confirmed_email, custom: { validator: TLDValidator, tld: %w[com net org], message: "is not valid" }
|
278
|
+
|
279
|
+
def call
|
280
|
+
# Do work...
|
281
|
+
end
|
282
|
+
|
283
|
+
end
|
284
|
+
```
|
285
|
+
|
286
|
+
Constraint options:
|
287
|
+
|
288
|
+
| Option | Description |
|
289
|
+
| ------------ | ----------- |
|
290
|
+
| `:validator` | Callable class that returns true or false. |
|
291
|
+
|
292
|
+
## Results
|
293
|
+
|
294
|
+
The following represents a result output example of a failed validation.
|
295
|
+
|
296
|
+
```ruby
|
297
|
+
result = DetermineBoxSizeTask.call
|
298
|
+
result.state #=> "interrupted"
|
299
|
+
result.status #=> "failed"
|
300
|
+
result.metadata #=> {
|
301
|
+
#=> reason: "email format is not valid. username cannot be empty.",
|
302
|
+
#=> messages: {
|
303
|
+
#=> email: ["format is not valid"],
|
304
|
+
#=> username: ["cannot be empty"]
|
305
|
+
#=> }
|
306
|
+
#=> }
|
307
|
+
```
|
308
|
+
|
309
|
+
---
|
310
|
+
|
311
|
+
- **Prev:** [Coercions](https://github.com/drexed/cmdx/blob/main/docs/parameters/coercions.md)
|
312
|
+
- **Next:** [Defaults](https://github.com/drexed/cmdx/blob/main/docs/parameters/defaults.md)
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# Tips & Tricks
|
2
|
+
|
3
|
+
## Configuration
|
4
|
+
|
5
|
+
Configure `CMDx` to get the most out of your Rails application.
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
CMDx.configure do |config|
|
9
|
+
# Redirect your logs through the app defined logger:
|
10
|
+
config.logger = Rails.logger
|
11
|
+
|
12
|
+
# Adjust the log level to write depending on the environment:
|
13
|
+
config.logger.level = Rails.env.development? ? Logger::DEBUG : Logger::INFO
|
14
|
+
|
15
|
+
# Structure log lines using a pre-built or custom formatter:
|
16
|
+
config.logger.formatter = CMDx::LogFormatters::Logstash.new
|
17
|
+
end
|
18
|
+
```
|
19
|
+
|
20
|
+
## Setup
|
21
|
+
|
22
|
+
While not required, a common setup involves creating an `app/cmds` directory
|
23
|
+
to place all of your tasks and batches under, eg:
|
24
|
+
|
25
|
+
```txt
|
26
|
+
/app
|
27
|
+
/cmds
|
28
|
+
/notifications
|
29
|
+
- deliver_email_task.rb
|
30
|
+
- post_slack_message_task.rb
|
31
|
+
- send_carrier_pigeon_task.rb
|
32
|
+
- batch_deliver_all.rb
|
33
|
+
- process_order_task.rb
|
34
|
+
- application_batch.rb
|
35
|
+
- application_task.rb
|
36
|
+
```
|
37
|
+
|
38
|
+
> [!TIP]
|
39
|
+
> Prefix batches with `batch_` and suffix tasks with `_task` to they convey their function.
|
40
|
+
> Use a verb+noun naming structure to convey the work that will be performed, eg:
|
41
|
+
> `BatchDeliverNotifications` or `DeliverEmailTask`
|
42
|
+
|
43
|
+
## Parameters
|
44
|
+
|
45
|
+
Use the Rails `with_options` as an elegant way to factor duplication
|
46
|
+
out of options passed to a series of parameter definitions. The following
|
47
|
+
are a few common example:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
class UpdateUserDetailsTask < CMDx::Task
|
51
|
+
|
52
|
+
# Apply `type: :string, presence: true` to this set of parameters:
|
53
|
+
with_options(type: :string, presence: true) do
|
54
|
+
required :email, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i }
|
55
|
+
optional :first_name, :last_name
|
56
|
+
end
|
57
|
+
|
58
|
+
required :address do
|
59
|
+
# Apply the `address_*` prefix to this set of nested parameters:
|
60
|
+
with_options(prefix: :address_) do
|
61
|
+
required :city, :country
|
62
|
+
optional :state
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def call
|
67
|
+
# Do work
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
[Learn More](https://api.rubyonrails.org/classes/Object.html#method-i-with_options)
|
74
|
+
about its usages on the official Rails docs.
|
75
|
+
|
76
|
+
---
|
77
|
+
|
78
|
+
- **Prev:** [Logging](https://github.com/drexed/cmdx/blob/main/docs/logging.md)
|
79
|
+
- **Next:** [Example](https://github.com/drexed/cmdx/blob/main/docs/example.md)
|
data/lib/cmdx/.DS_Store
ADDED
Binary file
|
data/lib/cmdx/batch.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CMDx
|
4
|
+
class Batch < Task
|
5
|
+
|
6
|
+
Group = Struct.new(:tasks, :options)
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
def batch_groups
|
11
|
+
@batch_groups ||= []
|
12
|
+
end
|
13
|
+
|
14
|
+
def process(*tasks, **options)
|
15
|
+
batch_groups << Group.new(
|
16
|
+
tasks.flatten.map do |task|
|
17
|
+
next task if task <= Task
|
18
|
+
|
19
|
+
raise ArgumentError, "must be a Batch or Task"
|
20
|
+
end,
|
21
|
+
options
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
def call
|
28
|
+
self.class.batch_groups.each do |group|
|
29
|
+
next unless __cmdx_eval(group.options)
|
30
|
+
|
31
|
+
batch_halt = group.options[:batch_halt] || task_setting(:batch_halt)
|
32
|
+
|
33
|
+
group.tasks.each do |task|
|
34
|
+
task_result = task.call(context)
|
35
|
+
next unless Array(batch_halt).include?(task_result.status)
|
36
|
+
|
37
|
+
throw!(task_result)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CMDx
|
4
|
+
module Coercions
|
5
|
+
module BigDecimal
|
6
|
+
|
7
|
+
DEFAULT_PRECISION = 14
|
8
|
+
|
9
|
+
module_function
|
10
|
+
|
11
|
+
def call(value, options = {})
|
12
|
+
BigDecimal(value, options[:precision] || DEFAULT_PRECISION)
|
13
|
+
rescue ArgumentError, TypeError
|
14
|
+
raise CoercionError, I18n.t(
|
15
|
+
"cmdx.coercions.into_a",
|
16
|
+
type: "big decimal",
|
17
|
+
default: "could not coerce into a big decimal"
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CMDx
|
4
|
+
module Coercions
|
5
|
+
module Boolean
|
6
|
+
|
7
|
+
FALSEY = /^(false|f|no|n|0)$/i
|
8
|
+
TRUTHY = /^(true|t|yes|y|1)$/i
|
9
|
+
|
10
|
+
module_function
|
11
|
+
|
12
|
+
def call(value, _options = {})
|
13
|
+
case value.to_s
|
14
|
+
when FALSEY then false
|
15
|
+
when TRUTHY then true
|
16
|
+
else
|
17
|
+
raise CoercionError, I18n.t(
|
18
|
+
"cmdx.coercions.into_a",
|
19
|
+
type: "boolean",
|
20
|
+
default: "could not coerce into a boolean"
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CMDx
|
4
|
+
module Coercions
|
5
|
+
module Complex
|
6
|
+
|
7
|
+
module_function
|
8
|
+
|
9
|
+
def call(value, _options = {})
|
10
|
+
Complex(value)
|
11
|
+
rescue ArgumentError, TypeError
|
12
|
+
raise CoercionError, I18n.t(
|
13
|
+
"cmdx.coercions.into_a",
|
14
|
+
type: "complex",
|
15
|
+
default: "could not coerce into a complex"
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|