interactify 0.3.0.pre.alpha.1 → 0.4.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/.rubocop.yml +1 -0
- data/.ruby-version +1 -0
- data/Appraisals +23 -0
- data/CHANGELOG.md +14 -0
- data/README.md +33 -44
- data/gemfiles/.bundle/config +2 -0
- data/gemfiles/no_railties_no_sidekiq.gemfile +18 -0
- data/gemfiles/no_railties_no_sidekiq.gemfile.lock +127 -0
- data/gemfiles/railties_6.gemfile +14 -0
- data/gemfiles/railties_6.gemfile.lock +253 -0
- data/gemfiles/railties_6_no_sidekiq.gemfile +19 -0
- data/gemfiles/railties_6_no_sidekiq.gemfile.lock +159 -0
- data/gemfiles/railties_6_sidekiq.gemfile +20 -0
- data/gemfiles/railties_6_sidekiq.gemfile.lock +168 -0
- data/gemfiles/railties_7_no_sidekiq.gemfile +19 -0
- data/gemfiles/railties_7_no_sidekiq.gemfile.lock +158 -0
- data/gemfiles/railties_7_sidekiq.gemfile +20 -0
- data/gemfiles/railties_7_sidekiq.gemfile.lock +167 -0
- data/lib/interactify/async/job_klass.rb +63 -0
- data/lib/interactify/async/job_maker.rb +58 -0
- data/lib/interactify/async/jobable.rb +96 -0
- data/lib/interactify/async/null_job.rb +23 -0
- data/lib/interactify/configuration.rb +15 -0
- data/lib/interactify/contracts/call_wrapper.rb +19 -0
- data/lib/interactify/contracts/failure.rb +8 -0
- data/lib/interactify/contracts/helpers.rb +81 -0
- data/lib/interactify/contracts/mismatching_promise_error.rb +19 -0
- data/lib/interactify/contracts/promising.rb +36 -0
- data/lib/interactify/contracts/setup.rb +39 -0
- data/lib/interactify/dsl/each_chain.rb +90 -0
- data/lib/interactify/dsl/if_interactor.rb +81 -0
- data/lib/interactify/dsl/if_klass.rb +82 -0
- data/lib/interactify/dsl/organizer.rb +32 -0
- data/lib/interactify/dsl/unique_klass_name.rb +23 -0
- data/lib/interactify/dsl/wrapper.rb +74 -0
- data/lib/interactify/dsl.rb +12 -6
- data/lib/interactify/rspec_matchers/matchers.rb +68 -0
- data/lib/interactify/version.rb +1 -1
- data/lib/interactify/{interactor_wiring → wiring}/callable_representation.rb +2 -2
- data/lib/interactify/{interactor_wiring → wiring}/constants.rb +4 -4
- data/lib/interactify/{interactor_wiring → wiring}/error_context.rb +1 -1
- data/lib/interactify/{interactor_wiring → wiring}/files.rb +1 -1
- data/lib/interactify/{interactor_wiring.rb → wiring.rb} +5 -5
- data/lib/interactify.rb +58 -38
- metadata +48 -71
- data/lib/interactify/async_job_klass.rb +0 -61
- data/lib/interactify/call_wrapper.rb +0 -17
- data/lib/interactify/contract_failure.rb +0 -6
- data/lib/interactify/contract_helpers.rb +0 -71
- data/lib/interactify/each_chain.rb +0 -88
- data/lib/interactify/if_interactor.rb +0 -70
- data/lib/interactify/interactor_wrapper.rb +0 -72
- data/lib/interactify/job_maker.rb +0 -56
- data/lib/interactify/jobable.rb +0 -92
- data/lib/interactify/mismatching_promise_error.rb +0 -17
- data/lib/interactify/organizer.rb +0 -30
- data/lib/interactify/promising.rb +0 -34
- data/lib/interactify/rspec/matchers.rb +0 -67
- data/lib/interactify/unique_klass_name.rb +0 -21
data/lib/interactify/jobable.rb
DELETED
@@ -1,92 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "interactify/job_maker"
|
4
|
-
|
5
|
-
module Interactify
|
6
|
-
module Jobable
|
7
|
-
extend ActiveSupport::Concern
|
8
|
-
|
9
|
-
# e.g. if Klass < Base
|
10
|
-
# and Base has a Base::Job class
|
11
|
-
#
|
12
|
-
# then let's make sure to define Klass::Job separately
|
13
|
-
included do |base|
|
14
|
-
def base.inherited(klass)
|
15
|
-
super_klass = klass.superclass
|
16
|
-
super_job = super_klass::Job # really spiffing
|
17
|
-
|
18
|
-
opts = super_job::JOBABLE_OPTS
|
19
|
-
jobable_method_name = super_job::JOBABLE_METHOD_NAME
|
20
|
-
|
21
|
-
to_call = defined?(super_klass::Async) ? :interactor_job : :job_calling
|
22
|
-
|
23
|
-
klass.send(to_call, opts:, method_name: jobable_method_name)
|
24
|
-
super(klass)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
class_methods do
|
29
|
-
# create a Job class and an Async class
|
30
|
-
# see job_calling for details on the Job class
|
31
|
-
#
|
32
|
-
# the Async class is a wrapper around the Job class
|
33
|
-
# that allows it to be used in an interactor chain
|
34
|
-
#
|
35
|
-
# E.g.
|
36
|
-
#
|
37
|
-
# class ExampleInteractor
|
38
|
-
# include Interactify
|
39
|
-
# expect :foo
|
40
|
-
#
|
41
|
-
# include Jobable
|
42
|
-
# interactor_job
|
43
|
-
# end
|
44
|
-
#
|
45
|
-
# doing the following will immediately enqueue a job
|
46
|
-
# that calls the interactor ExampleInteractor with (foo: 'bar')
|
47
|
-
# ExampleInteractor::Async.call(foo: 'bar')
|
48
|
-
#
|
49
|
-
# it will also ensure to pluck only the expects from the context
|
50
|
-
# so that you can have other non primitive values in the context
|
51
|
-
# but the job will only have the expects passed to it
|
52
|
-
#
|
53
|
-
# obviously you will need to be aware that later interactors
|
54
|
-
# in an interactor chain cannot depend on the result of the async
|
55
|
-
# interactor
|
56
|
-
def interactor_job(method_name: :call!, opts: {}, klass_suffix: "")
|
57
|
-
job_maker = JobMaker.new(container_klass: self, opts:, method_name:, klass_suffix:)
|
58
|
-
# with WhateverInteractor::Job you can perform the interactor as a job
|
59
|
-
# from sidekiq
|
60
|
-
# e.g. WhateverInteractor::Job.perform_async(...)
|
61
|
-
const_set("Job#{klass_suffix}", job_maker.job_klass)
|
62
|
-
|
63
|
-
# with WhateverInteractor::Async you can call WhateverInteractor::Job
|
64
|
-
# in an organizer oro on its oen using normal interactor call call! semantics
|
65
|
-
# e.g. WhateverInteractor::Async.call(...)
|
66
|
-
# WhateverInteractor::Async.call!(...)
|
67
|
-
const_set("Async#{klass_suffix}", job_maker.async_job_klass)
|
68
|
-
end
|
69
|
-
|
70
|
-
# if this was defined in ExampleClass this creates the following class
|
71
|
-
# ExampleClass::Job
|
72
|
-
# this class ia added as a convenience so you can easily turn a
|
73
|
-
# class method into a job
|
74
|
-
#
|
75
|
-
# Example:
|
76
|
-
#
|
77
|
-
# class ExampleClass
|
78
|
-
# include Jobable
|
79
|
-
# job_calling method_name: :some_method
|
80
|
-
# end
|
81
|
-
#
|
82
|
-
# # the following class is created that you can use to enqueue a job
|
83
|
-
# in the sidekiq yaml file
|
84
|
-
# ExampleClass::Job.some_method
|
85
|
-
def job_calling(method_name:, opts: {}, klass_suffix: "")
|
86
|
-
job_maker = JobMaker.new(container_klass: self, opts:, method_name:, klass_suffix:)
|
87
|
-
|
88
|
-
const_set("Job#{klass_suffix}", job_maker.job_klass)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "interactify/contract_failure"
|
4
|
-
|
5
|
-
module Interactify
|
6
|
-
class MismatchingPromiseError < ContractFailure
|
7
|
-
def initialize(interactor, promising, promised_keys)
|
8
|
-
super <<~MESSAGE.chomp
|
9
|
-
#{interactor} does not promise:
|
10
|
-
#{promising.inspect}
|
11
|
-
|
12
|
-
Actual promises are:
|
13
|
-
#{promised_keys.inspect}
|
14
|
-
MESSAGE
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "interactify/interactor_wrapper"
|
4
|
-
|
5
|
-
module Interactify
|
6
|
-
module Organizer
|
7
|
-
extend ActiveSupport::Concern
|
8
|
-
|
9
|
-
class_methods do
|
10
|
-
def organize(*interactors)
|
11
|
-
wrapped = InteractorWrapper.wrap_many(self, interactors)
|
12
|
-
|
13
|
-
super(*wrapped)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def call
|
18
|
-
self.class.organized.each do |interactor|
|
19
|
-
instance = interactor.new(context)
|
20
|
-
|
21
|
-
instance.instance_variable_set(
|
22
|
-
:@_interactor_called_by_non_bang_method,
|
23
|
-
@_interactor_called_by_non_bang_method
|
24
|
-
)
|
25
|
-
|
26
|
-
instance.tap(&:run!)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "interactify/mismatching_promise_error"
|
4
|
-
|
5
|
-
module Interactify
|
6
|
-
class Promising
|
7
|
-
attr_reader :interactor, :promising
|
8
|
-
|
9
|
-
def self.validate(interactor, *promising)
|
10
|
-
new(interactor, *promising).validate
|
11
|
-
|
12
|
-
interactor
|
13
|
-
end
|
14
|
-
|
15
|
-
def initialize(interactor, *promising)
|
16
|
-
@interactor = interactor
|
17
|
-
@promising = format_keys promising
|
18
|
-
end
|
19
|
-
|
20
|
-
def validate
|
21
|
-
return if promising == promised_keys
|
22
|
-
|
23
|
-
raise MismatchingPromiseError.new(interactor, promising, promised_keys)
|
24
|
-
end
|
25
|
-
|
26
|
-
def promised_keys
|
27
|
-
format_keys interactor.promised_keys
|
28
|
-
end
|
29
|
-
|
30
|
-
def format_keys(keys)
|
31
|
-
Array(keys).compact.map(&:to_sym).sort
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
@@ -1,67 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "interactify/interactor_wiring"
|
4
|
-
|
5
|
-
# Custom matcher that implements expect_inputs
|
6
|
-
# e.g.
|
7
|
-
# expect(described_class).to expect_inputs(:connection, :order)
|
8
|
-
|
9
|
-
RSpec::Matchers.define :expect_inputs do |*expected_inputs|
|
10
|
-
match do |actual|
|
11
|
-
next false unless actual.respond_to?(:expected_keys)
|
12
|
-
|
13
|
-
actual_inputs = Array(actual.expected_keys)
|
14
|
-
@missing_inputs = expected_inputs - actual_inputs
|
15
|
-
@extra_inputs = actual_inputs - expected_inputs
|
16
|
-
|
17
|
-
@missing_inputs.empty? && @extra_inputs.empty?
|
18
|
-
end
|
19
|
-
|
20
|
-
failure_message do |actual|
|
21
|
-
message = "expected #{actual} to expect inputs #{expected_inputs.inspect}"
|
22
|
-
message += "\n\tmissing inputs: #{@missing_inputs}" if @missing_inputs
|
23
|
-
message += "\n\textra inputs: #{@extra_inputs}" if @extra_inputs
|
24
|
-
message
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
# Custom matcher that implements promise_outputs
|
29
|
-
# e.g. expect(described_class).to promise_outputs(:request_logger)
|
30
|
-
RSpec::Matchers.define :promise_outputs do |*expected_outputs|
|
31
|
-
match do |actual|
|
32
|
-
next false unless actual.respond_to?(:promised_keys)
|
33
|
-
|
34
|
-
actual_outputs = Array(actual.promised_keys)
|
35
|
-
@missing_outputs = expected_outputs - actual_outputs
|
36
|
-
@extra_outputs = actual_outputs - expected_outputs
|
37
|
-
|
38
|
-
@missing_outputs.empty? && @extra_outputs.empty?
|
39
|
-
end
|
40
|
-
|
41
|
-
failure_message do |actual|
|
42
|
-
message = "expected #{actual} to promise outputs #{expected_outputs.inspect}"
|
43
|
-
message += "\n\tmissing outputs: #{@missing_outputs}" if @missing_outputs
|
44
|
-
message += "\n\textra outputs: #{@extra_outputs}" if @extra_outputs
|
45
|
-
message
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
# Custom matcher that implements organize_interactors
|
50
|
-
#
|
51
|
-
# e.g. expect(described_class).to organize_interactors(SeparateIntoPackages, SendPackagesToSeko)
|
52
|
-
RSpec::Matchers.define :organize_interactors do |*expected_interactors|
|
53
|
-
match do |actual|
|
54
|
-
actual_interactors = actual.organized
|
55
|
-
@missing_interactors = expected_interactors - actual_interactors
|
56
|
-
@extra_interactors = actual_interactors - expected_interactors
|
57
|
-
|
58
|
-
@missing_interactors.empty? && @extra_interactors.empty?
|
59
|
-
end
|
60
|
-
|
61
|
-
failure_message do |actual|
|
62
|
-
message = "expected #{actual} to organize interactors #{expected_interactors.inspect}"
|
63
|
-
message += "\n\tmissing interactors: #{@missing_interactors}" if @missing_interactors
|
64
|
-
message += "\n\textra interactors: #{@extra_interactors}" if @extra_interactors
|
65
|
-
message
|
66
|
-
end
|
67
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Interactify
|
4
|
-
module UniqueKlassName
|
5
|
-
def self.for(namespace, prefix)
|
6
|
-
id = generate_unique_id
|
7
|
-
klass_name = :"#{prefix}#{id}"
|
8
|
-
|
9
|
-
while namespace.const_defined?(klass_name)
|
10
|
-
id = generate_unique_id
|
11
|
-
klass_name = :"#{prefix}#{id}"
|
12
|
-
end
|
13
|
-
|
14
|
-
klass_name.to_sym
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.generate_unique_id
|
18
|
-
rand(10_000)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|