gl_command 1.4.0 → 2.0.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 +4 -4
- data/README.md +33 -3
- data/gl_command.gemspec +0 -2
- data/lib/gl_command/callable.rb +21 -2
- data/lib/gl_command/context.rb +6 -3
- data/lib/gl_command/rspec/matchers.rb +6 -4
- data/lib/gl_command/validatable.rb +1 -1
- data/lib/gl_command/version.rb +1 -1
- metadata +3 -17
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5c1d353b0428b6aa32adf96b5962256b415dfacc45116d3e4a21ea8ca345de8e
|
|
4
|
+
data.tar.gz: f8056f5711ef8ffdfafd3678fd25c05fa1879f2893f1d9acecaa9a3880565d7a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8229ae95a48c6b5fc83fbeb647d604666be82cc2e35b08d294c6c8c4c7e44ec4c52d40f520ead9005d38edeee0aab9fc03f0db9e637271b2fd1dbd98619efd21
|
|
7
|
+
data.tar.gz: 678f19f03349988f5a033eeb1f5cff6acb846e0c2b5eea744b7493e0419232d7e0e56f2b4684c9857f0d5e78d7119b9147bef86cf0b5ac642056f9b67365cd8d
|
data/README.md
CHANGED
|
@@ -18,6 +18,7 @@ Calling a command returns a `GLCommand::Context` which has these properties:
|
|
|
18
18
|
- [Displaying errors (use `full_error_message`)](#displaying-errors-use-full_error_message)
|
|
19
19
|
- [stop_and_fail!](#stop_and_fail)
|
|
20
20
|
- [Validations](#validations)
|
|
21
|
+
- [Best practices for error handling](#best-practices-for-error-handling)
|
|
21
22
|
- [GLExceptionNotifier](#glexceptionnotifier)
|
|
22
23
|
- [Chainable](#chainable)
|
|
23
24
|
- [Testing `GLCommand`s](#testing-glcommands)
|
|
@@ -146,12 +147,41 @@ stop_and_fail!('An error message', no_notify: true) # GLExceptionNotifier is *no
|
|
|
146
147
|
|
|
147
148
|
### Validations
|
|
148
149
|
|
|
149
|
-
You can add validations to `GLCommand::Callable` and `GLCommand::Chainable`.
|
|
150
|
+
You can add validations to `GLCommand::Callable` and `GLCommand::Chainable`. They include `ActiveModel::Validations`, so you can use [Rails active record validations](https://guides.rubyonrails.org/active_record_validations.html).
|
|
150
151
|
|
|
151
|
-
If the validations fail, the command returns `success: false` without executing.
|
|
152
|
+
If the validations fail, the command returns `success: false` without executing and if validations fail, `GLExceptionNotifier` is **not** called.
|
|
152
153
|
|
|
153
|
-
|
|
154
|
+
```ruby
|
|
155
|
+
class ExampleCommand < GLCommand::Callable
|
|
156
|
+
validates :name, presence: true
|
|
157
|
+
validate :name_must_start_with_cool
|
|
158
|
+
|
|
159
|
+
def name_must_start_with_cool
|
|
160
|
+
return true unless name.start_with?('cool')
|
|
161
|
+
|
|
162
|
+
errors.add(:name, "Doesn't start with 'cool'")
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Best practices for error handling
|
|
168
|
+
|
|
169
|
+
#### Only add validation errors in validations
|
|
170
|
+
|
|
171
|
+
i.e. don't use `errors.add` in the `call` method. Use `stop_and_fail!` instead.
|
|
172
|
+
|
|
173
|
+
#### Prefer raising the original error
|
|
174
|
+
|
|
175
|
+
For example, if you want to raise a custom error message, don't rescue and then `stop_and_fail!('Some special error message')`. Do this instead:
|
|
176
|
+
|
|
177
|
+
```ruby
|
|
178
|
+
rescue StandardError => e
|
|
179
|
+
context.full_error_message = "Some special error message"
|
|
180
|
+
raise e
|
|
181
|
+
end
|
|
182
|
+
```
|
|
154
183
|
|
|
184
|
+
This will preserve the original error and stack trace, which makes it easier to debug and track down issues.
|
|
155
185
|
|
|
156
186
|
## GLExceptionNotifier
|
|
157
187
|
|
data/gl_command.gemspec
CHANGED
data/lib/gl_command/callable.rb
CHANGED
|
@@ -214,12 +214,15 @@ module GLCommand
|
|
|
214
214
|
raise ArgumentError, "unknown #{error_keys_str(unknown)}" if unknown.any?
|
|
215
215
|
|
|
216
216
|
# strong_attributes type checking
|
|
217
|
+
# type can be a class (e.g. String), a symbol naming a predicate method
|
|
218
|
+
# the value must answer truthily (e.g. :acts_as_syncable?), or an array of
|
|
219
|
+
# either (e.g. [User, AdminUser]), in which case the value may match any
|
|
217
220
|
self.class.requires.merge(self.class.allows).each do |arg, type|
|
|
218
|
-
next if type.nil? || args[arg]
|
|
221
|
+
next if type.nil? || Array(type).any? { |t| value_matches_type?(args[arg], t) }
|
|
219
222
|
# Validation skipped if allows and nil (but not if blank)
|
|
220
223
|
next if args[arg].nil? && self.class.allows.include?(arg)
|
|
221
224
|
|
|
222
|
-
raise GLCommand::ArgumentTypeError, ":#{arg} is not
|
|
225
|
+
raise GLCommand::ArgumentTypeError, ":#{arg} is not #{type_error_str(type)}"
|
|
223
226
|
end
|
|
224
227
|
end
|
|
225
228
|
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
@@ -227,5 +230,21 @@ module GLCommand
|
|
|
227
230
|
def error_keys_str(keys)
|
|
228
231
|
"keyword#{keys.count > 1 ? 's' : ''}: #{keys.map { |k| ":#{k}" }.join(', ')}"
|
|
229
232
|
end
|
|
233
|
+
|
|
234
|
+
# A type can be a class (checked with is_a?) or a symbol naming a predicate
|
|
235
|
+
# method the value must respond to and return truthy from
|
|
236
|
+
def value_matches_type?(value, type)
|
|
237
|
+
if type.is_a?(Symbol)
|
|
238
|
+
value.respond_to?(type) && value.public_send(type)
|
|
239
|
+
else
|
|
240
|
+
value.is_a?(type)
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def type_error_str(type)
|
|
245
|
+
return "one of #{type.join(', ')}" if type.is_a?(Array)
|
|
246
|
+
|
|
247
|
+
type.is_a?(Symbol) ? type.to_s : "a #{type}"
|
|
248
|
+
end
|
|
230
249
|
end
|
|
231
250
|
end
|
data/lib/gl_command/context.rb
CHANGED
|
@@ -27,7 +27,7 @@ module GLCommand
|
|
|
27
27
|
|
|
28
28
|
# If someone calls #errors, they expect to get the errors! Include the non-validation error, if it exists
|
|
29
29
|
def errors
|
|
30
|
-
current_errors&.add(:base,
|
|
30
|
+
current_errors&.add(:base, full_error_message) if add_command_error?
|
|
31
31
|
current_errors
|
|
32
32
|
end
|
|
33
33
|
|
|
@@ -126,7 +126,10 @@ module GLCommand
|
|
|
126
126
|
private
|
|
127
127
|
|
|
128
128
|
def current_errors
|
|
129
|
-
@callable
|
|
129
|
+
return @callable.errors if defined?(@callable)
|
|
130
|
+
|
|
131
|
+
# @standalone_errors only is instantiated when you use build_context
|
|
132
|
+
(@standalone_errors ||= ActiveModel::Errors.new(self))
|
|
130
133
|
end
|
|
131
134
|
|
|
132
135
|
def add_command_error?
|
|
@@ -136,7 +139,7 @@ module GLCommand
|
|
|
136
139
|
|
|
137
140
|
# Add command error unless the existing error is a validation error or there's already a command error
|
|
138
141
|
@error&.class != ActiveRecord::RecordInvalid &&
|
|
139
|
-
current_errors.full_messages.none? { |err| err
|
|
142
|
+
current_errors.full_messages.none? { |err| err == @error.message }
|
|
140
143
|
end
|
|
141
144
|
|
|
142
145
|
def exception?(passed_error)
|
|
@@ -46,15 +46,15 @@ module GLCommand
|
|
|
46
46
|
class RequireArgumentMatcher < CommandArgumentMatcher
|
|
47
47
|
private
|
|
48
48
|
|
|
49
|
-
def scope
|
|
50
|
-
def action
|
|
49
|
+
def scope = :requires
|
|
50
|
+
def action = 'require'
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
class AllowArgumentMatcher < CommandArgumentMatcher
|
|
54
54
|
private
|
|
55
55
|
|
|
56
|
-
def scope
|
|
57
|
-
def action
|
|
56
|
+
def scope = :allows
|
|
57
|
+
def action = 'allow'
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
# Specific matcher for `returns`, which only stores an Array of keys.
|
|
@@ -69,6 +69,7 @@ module GLCommand
|
|
|
69
69
|
self
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
+
# rubocop:disable Layout/LineLength
|
|
72
73
|
def matches?(command_class)
|
|
73
74
|
@command_class = command_class.is_a?(Class) ? command_class : command_class.class
|
|
74
75
|
|
|
@@ -79,6 +80,7 @@ module GLCommand
|
|
|
79
80
|
|
|
80
81
|
@command_class.returns.include?(@attribute)
|
|
81
82
|
end
|
|
83
|
+
# rubocop:enable Layout/LineLength
|
|
82
84
|
|
|
83
85
|
def description
|
|
84
86
|
"return attribute `#{@attribute}`"
|
data/lib/gl_command/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gl_command
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Give Lively
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-07-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|
|
@@ -38,20 +38,6 @@ dependencies:
|
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: 1.0.2
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: rspec
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - "~>"
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: '3.0'
|
|
48
|
-
type: :development
|
|
49
|
-
prerelease: false
|
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
-
requirements:
|
|
52
|
-
- - "~>"
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
54
|
-
version: '3.0'
|
|
55
41
|
description:
|
|
56
42
|
email:
|
|
57
43
|
executables: []
|
|
@@ -92,7 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
92
78
|
- !ruby/object:Gem::Version
|
|
93
79
|
version: '0'
|
|
94
80
|
requirements: []
|
|
95
|
-
rubygems_version: 3.
|
|
81
|
+
rubygems_version: 3.5.22
|
|
96
82
|
signing_key:
|
|
97
83
|
specification_version: 4
|
|
98
84
|
summary: Give Lively Commands
|