gl_command 1.0.1 → 1.1.1

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: 11a5d5ab9dd4a28700cb671153c1a757612f5b735194a28ad22f8be660a09250
4
- data.tar.gz: a3a351134af335563ee6e970d5920d08e52f2b4375cef1c8adc87e555bd1e64f
3
+ metadata.gz: 2c64386ffafec990d01604c12482b0cf2deb8b4caff95c638d9d4e8835c4cb92
4
+ data.tar.gz: 9a58f0b30039b493382f01d22fb8cc2c8d6950af71f16ec3b4e708a776e78af9
5
5
  SHA512:
6
- metadata.gz: 4344a62dca3bccad299425f9d22db05ca7e3062518c0f4a0c3b1be2d3708de8ccb3d310380c5a4eb73b515a21c4866c8b3385f9ee18c23e9389fd49e9afb8803
7
- data.tar.gz: '08c842fe87ac92bc793fad76498e0b12ecc772a8d5385fea2d5c34670115ba40a17545227e572a5ce1bcee9b3bc6c52f2e70f59e45a8755a871b93ebe3d344a2'
6
+ metadata.gz: 126e275c0bd65be3fb7fe1ead77e283231a158359b1c95ab00d691e010287f41b397a97a9202de316ba921313b1df66c59e76b4f5e13d62d4130d762aba6f803
7
+ data.tar.gz: 728454632aa41f5b740b4f3eaf97c85ee8022857ac349ca86bfa0d655687fc6b402b7501629e580c3ed3c2b87b621a56a11333a95f3239d8ec0095901bb0e311
@@ -6,7 +6,7 @@ require 'gl_command/validatable'
6
6
 
7
7
  module GLCommand
8
8
  class Callable
9
- DEFAULT_OPTS = { raise_errors: false, skip_unknown_parameters: true }.freeze
9
+ DEFAULT_OPTS = { raise_errors: false, skip_unknown_parameters: true, in_chain: false }.freeze
10
10
  RESERVED_WORDS = (DEFAULT_OPTS.keys + GLCommand::ChainableContext.reserved_words).sort.freeze
11
11
 
12
12
  class << self
@@ -25,7 +25,8 @@ module GLCommand
25
25
 
26
26
  # DEFAULT_OPTS contains skip_unknown_parameters: true - so it raises on call
27
27
  # (rather than in context initialize) to make errors more legible
28
- opts = DEFAULT_OPTS.merge(raise_errors: args.delete(:raise_errors)).compact
28
+ opts = DEFAULT_OPTS.merge(raise_errors: args.delete(:raise_errors),
29
+ in_chain: args.delete(:in_chain)).compact
29
30
  # args are passed in in perform_call(args) so that invalid args raise in a legible place
30
31
  new(build_context(**args.merge(opts))).perform_call(args)
31
32
  end
@@ -34,9 +35,13 @@ module GLCommand
34
35
  call(*posargs, **args.merge(raise_errors: true))
35
36
  end
36
37
 
37
- def build_context(raise_errors: false, skip_unknown_parameters: false,
38
+ # error can be passed to build context, useful for stubbing in tests
39
+ def build_context(raise_errors: false, skip_unknown_parameters: false, error: nil,
38
40
  **arguments_and_returns)
39
- context_class.new(self, raise_errors:, skip_unknown_parameters:, **arguments_and_returns)
41
+ new_context = context_class.new(self, raise_errors:, skip_unknown_parameters:,
42
+ **arguments_and_returns)
43
+ new_context.error = error if error.present?
44
+ new_context
40
45
  end
41
46
 
42
47
  def requires(*attributes, **strong_attributes)
@@ -70,7 +75,7 @@ module GLCommand
70
75
  (arguments + returns).uniq
71
76
  end
72
77
 
73
- # Used internally by GLCommand (probably don't reference them in your own GLCommands)
78
+ # Used internally by GLCommand (probably don't reference in your own GLCommands)
74
79
  # is true in GLCommand::Chainable
75
80
  def chain?
76
81
  false
@@ -106,7 +111,9 @@ module GLCommand
106
111
 
107
112
  def perform_call(args)
108
113
  raise_for_invalid_args!(**args)
114
+ instrument_command(:before_call)
109
115
  call_with_callbacks
116
+ instrument_command(:after_call)
110
117
  raise_unless_chained_or_skipped if self.class.chain? # defined in GLCommand::Chainable
111
118
  context.failure? ? handle_failure : context
112
119
  rescue StandardError => e
@@ -133,6 +140,11 @@ module GLCommand
133
140
 
134
141
  private
135
142
 
143
+ # trigger: [:before_call, :after_call, :before_rollback]
144
+ def instrument_command(trigger)
145
+ # Override where gem is used if you want to instrument commands
146
+ end
147
+
136
148
  # rubocop:disable Metrics/AbcSize
137
149
  def handle_failure(e = nil)
138
150
  context.error ||= e
@@ -159,9 +171,7 @@ module GLCommand
159
171
  rescue GLCommand::CommandNoNotifyError
160
172
  raise context.error # makes CommandNoNotifyError the cause
161
173
  end
162
- # rubocop:enable Metrics/AbcSize
163
174
 
164
- # rubocop:disable Metrics/AbcSize
165
175
  def call_with_callbacks
166
176
  GLExceptionNotifier.breadcrumbs(data: { context: context.inspect }, message: self.class.to_s)
167
177
  validate_validatable! # defined in GLCommand::Validatable
@@ -186,6 +196,8 @@ module GLCommand
186
196
  def call_rollbacks
187
197
  return if defined?(@rolled_back) # Not sure this is required
188
198
 
199
+ instrument_command(:before_rollback)
200
+
189
201
  @rolled_back = true
190
202
 
191
203
  chain_rollback if self.class.chain? # defined in GLCommand::Chainable
@@ -41,7 +41,7 @@ module GLCommand
41
41
 
42
42
  commands.map do |command|
43
43
  cargs = context.chain_arguments_and_returns.slice(*command.arguments)
44
- .merge(context.opts_hash)
44
+ .merge(context.opts_hash).merge(in_chain: true)
45
45
 
46
46
  result = command.call(**cargs)
47
47
  context.assign_parameters(skip_unknown_parameters: true, **result.returns)
@@ -7,9 +7,10 @@ require 'active_support/core_ext/module'
7
7
  module GLCommand
8
8
  class Context
9
9
  def initialize(klass, raise_errors: false, skip_unknown_parameters: false,
10
- **arguments_and_returns)
10
+ in_chain: false, **arguments_and_returns)
11
11
  @klass = klass
12
12
  @raise_errors = raise_errors.nil? ? false : raise_errors
13
+ @in_chain = in_chain
13
14
  @klass.arguments_and_returns.each { |key| singleton_class.class_eval { attr_accessor key } }
14
15
  initialize_chain_context(**arguments_and_returns) if chain?
15
16
  assign_parameters(skip_unknown_parameters:, **arguments_and_returns)
@@ -24,12 +25,21 @@ module GLCommand
24
25
  attr_reader :klass, :error
25
26
  attr_writer :full_error_message
26
27
 
27
- delegate :errors, to: :@callable, allow_nil: true
28
+ # If someone calls errors on a context, they expect to get the errors!
29
+ # Make that work, but also try to make it clear that they probably shouldn't be using that for presentation
30
+ def errors
31
+ current_errors&.add(:base, "full_error_message: #{full_error_message}") if @failure && current_errors.blank?
32
+ current_errors
33
+ end
28
34
 
29
35
  def chain?
30
36
  false
31
37
  end
32
38
 
39
+ def in_chain?
40
+ @in_chain
41
+ end
42
+
33
43
  def returns
34
44
  @klass.returns.index_with { |rattr| send(rattr) }
35
45
  end
@@ -43,7 +53,7 @@ module GLCommand
43
53
  end
44
54
 
45
55
  def failure?
46
- @failure || errors.present? || @full_error_message.present? || false
56
+ @failure || current_errors.present? || @full_error_message.present? || false
47
57
  end
48
58
 
49
59
  def success?
@@ -98,7 +108,7 @@ module GLCommand
98
108
  passed_error.is_a?(ActiveRecord::RecordInvalid) && defined?(passed_error.record.errors)
99
109
  # Return a new error if it's an error (rather than the class)
100
110
  passed_error.is_a?(Class) ? passed_error.new(@full_error_message) : passed_error
101
- elsif errors.present? # check for validation errors
111
+ elsif current_errors.present? # check for validation errors
102
112
  # Assign ActiveRecord::RecordInvalid if validatable error
103
113
  ActiveRecord::RecordInvalid.new(@callable)
104
114
  else
@@ -118,6 +128,10 @@ module GLCommand
118
128
 
119
129
  private
120
130
 
131
+ def current_errors
132
+ @callable&.errors
133
+ end
134
+
121
135
  def exception?(passed_error)
122
136
  passed_error.is_a?(Exception) ||
123
137
  (passed_error.respond_to?(:ancestors) && passed_error.ancestors.include?(Exception))
@@ -1,3 +1,3 @@
1
1
  module GLCommand
2
- VERSION = '1.0.1'.freeze
2
+ VERSION = '1.1.1'.freeze
3
3
  end
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: 1.0.1
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Give Lively
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-19 00:00:00.000000000 Z
11
+ date: 2024-09-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord