axn 0.1.0.pre.alpha.2.4.1 → 0.1.0.pre.alpha.2.5.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 +4 -4
- data/.rubocop.yml +6 -2
- data/CHANGELOG.md +18 -2
- data/docs/recipes/testing.md +50 -0
- data/docs/reference/class.md +42 -6
- data/docs/reference/configuration.md +9 -12
- data/docs/reference/instance.md +6 -0
- data/docs/usage/using.md +30 -0
- data/lib/action/core/configuration.rb +5 -4
- data/lib/action/core/context_facade.rb +14 -5
- data/lib/action/core/contract.rb +78 -20
- data/lib/action/core/contract_for_subfields.rb +118 -0
- data/lib/action/core/event_handlers.rb +2 -2
- data/lib/action/core/exceptions.rb +7 -6
- data/lib/action/core/hoist_errors.rb +4 -2
- data/lib/action/core/logging.rb +3 -9
- data/lib/action/core/swallow_exceptions.rb +12 -30
- data/lib/action/core/top_level_around_hook.rb +43 -27
- data/lib/action/core/validation/fields.rb +38 -0
- data/lib/action/core/validation/subfields.rb +44 -0
- data/lib/action/core/validation/validators/model_validator.rb +35 -0
- data/lib/action/core/validation/validators/type_validator.rb +30 -0
- data/lib/action/core/validation/validators/validate_validator.rb +21 -0
- data/lib/action/enqueueable/enqueue_all_in_background.rb +17 -0
- data/lib/action/enqueueable/enqueue_all_worker.rb +21 -0
- data/lib/action/enqueueable/via_sidekiq.rb +76 -0
- data/lib/action/enqueueable.rb +15 -0
- data/lib/axn/factory.rb +1 -2
- data/lib/axn/version.rb +1 -1
- data/lib/axn.rb +11 -4
- metadata +12 -4
- data/lib/action/core/contract_validator.rb +0 -66
- data/lib/action/core/enqueueable.rb +0 -74
@@ -1,66 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Action
|
4
|
-
class ContractValidator
|
5
|
-
include ActiveModel::Validations
|
6
|
-
|
7
|
-
def initialize(context)
|
8
|
-
@context = context
|
9
|
-
end
|
10
|
-
|
11
|
-
def read_attribute_for_validation(attr)
|
12
|
-
@context.public_send(attr)
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.validate!(validations:, context:, exception_klass:)
|
16
|
-
validator = Class.new(self) do
|
17
|
-
def self.name = "Action::ContractValidator::OneOff"
|
18
|
-
|
19
|
-
validations.each do |field, field_validations|
|
20
|
-
field_validations.each do |key, value|
|
21
|
-
validates field, key => value
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end.new(context)
|
25
|
-
|
26
|
-
return if validator.valid?
|
27
|
-
|
28
|
-
raise exception_klass, validator.errors
|
29
|
-
end
|
30
|
-
|
31
|
-
# Allow for custom validators to be defined in the context of the action
|
32
|
-
class ValidateValidator < ActiveModel::EachValidator
|
33
|
-
def validate_each(record, attribute, value)
|
34
|
-
msg = begin
|
35
|
-
options[:with].call(value)
|
36
|
-
rescue StandardError => e
|
37
|
-
warn("Custom validation on field '#{attribute}' raised #{e.class.name}: #{e.message}")
|
38
|
-
|
39
|
-
"failed validation: #{e.message}"
|
40
|
-
end
|
41
|
-
|
42
|
-
record.errors.add(attribute, msg) if msg.present?
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
class TypeValidator < ActiveModel::EachValidator
|
47
|
-
def validate_each(record, attribute, value)
|
48
|
-
# TODO: the last one (:value) might be my fault from the make-it-a-hash fallback in #parse_field_configs
|
49
|
-
types = options[:in].presence || Array(options[:with]).presence || Array(options[:value]).presence
|
50
|
-
|
51
|
-
return if value.blank? && !types.include?(:boolean) # Handled with a separate default presence validator
|
52
|
-
|
53
|
-
msg = types.size == 1 ? "is not a #{types.first}" : "is not one of #{types.join(", ")}"
|
54
|
-
record.errors.add attribute, (options[:message] || msg) unless types.any? do |type|
|
55
|
-
if type == :boolean
|
56
|
-
[true, false].include?(value)
|
57
|
-
elsif type == :uuid
|
58
|
-
value.is_a?(String) && value.match?(/\A[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}\z/i)
|
59
|
-
else
|
60
|
-
value.is_a?(type)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
@@ -1,74 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Action
|
4
|
-
module Enqueueable
|
5
|
-
def self.included(base)
|
6
|
-
base.class_eval do
|
7
|
-
begin
|
8
|
-
require "sidekiq"
|
9
|
-
include Sidekiq::Job
|
10
|
-
rescue LoadError
|
11
|
-
puts "Sidekiq not available -- skipping Enqueueable"
|
12
|
-
return
|
13
|
-
end
|
14
|
-
|
15
|
-
define_method(:perform) do |*args|
|
16
|
-
context = self.class._params_from_global_id(args.first)
|
17
|
-
bang = args.size > 1 ? args.last : false
|
18
|
-
|
19
|
-
if bang
|
20
|
-
self.class.call!(context)
|
21
|
-
else
|
22
|
-
self.class.call(context)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.enqueue(context = {})
|
27
|
-
perform_async(_process_context_to_sidekiq_args(context))
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.enqueue!(context = {})
|
31
|
-
perform_async(_process_context_to_sidekiq_args(context), true)
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.queue_options(opts)
|
35
|
-
opts = opts.transform_keys(&:to_s)
|
36
|
-
self.sidekiq_options_hash = get_sidekiq_options.merge(opts)
|
37
|
-
end
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
def self._process_context_to_sidekiq_args(context)
|
42
|
-
client = Sidekiq::Client.new
|
43
|
-
|
44
|
-
_params_to_global_id(context).tap do |args|
|
45
|
-
if client.send(:json_unsafe?, args).present?
|
46
|
-
raise ArgumentError,
|
47
|
-
"Cannot pass non-JSON-serializable objects to Sidekiq. Make sure all objects in the context are serializable (or respond to to_global_id)."
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def self._params_to_global_id(context)
|
53
|
-
context.stringify_keys.each_with_object({}) do |(key, value), hash|
|
54
|
-
if value.respond_to?(:to_global_id)
|
55
|
-
hash["#{key}_as_global_id"] = value.to_global_id.to_s
|
56
|
-
else
|
57
|
-
hash[key] = value
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def self._params_from_global_id(params)
|
63
|
-
params.each_with_object({}) do |(key, value), hash|
|
64
|
-
if key.end_with?("_as_global_id")
|
65
|
-
hash[key.delete_suffix("_as_global_id")] = GlobalID::Locator.locate(value)
|
66
|
-
else
|
67
|
-
hash[key] = value
|
68
|
-
end
|
69
|
-
end.symbolize_keys
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|