interactify 0.2.0.pre.alpha.1 → 0.3.0.pre.RC1

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.
@@ -0,0 +1,157 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ interactify (0.3.0.pre.alpha.2)
5
+ interactor
6
+ interactor-contracts
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ actionpack (7.0.0)
12
+ actionview (= 7.0.0)
13
+ activesupport (= 7.0.0)
14
+ rack (~> 2.0, >= 2.2.0)
15
+ rack-test (>= 0.6.3)
16
+ rails-dom-testing (~> 2.0)
17
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
18
+ actionview (7.0.0)
19
+ activesupport (= 7.0.0)
20
+ builder (~> 3.1)
21
+ erubi (~> 1.4)
22
+ rails-dom-testing (~> 2.0)
23
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
24
+ activesupport (7.0.0)
25
+ concurrent-ruby (~> 1.0, >= 1.0.2)
26
+ i18n (>= 1.6, < 2)
27
+ minitest (>= 5.1)
28
+ tzinfo (~> 2.0)
29
+ appraisal (2.5.0)
30
+ bundler
31
+ rake
32
+ thor (>= 0.14.0)
33
+ builder (3.2.4)
34
+ concurrent-ruby (1.2.2)
35
+ crass (1.0.6)
36
+ debug (1.9.1)
37
+ irb (~> 1.10)
38
+ reline (>= 0.3.8)
39
+ diff-lcs (1.5.0)
40
+ docile (1.4.0)
41
+ dry-configurable (1.0.1)
42
+ dry-core (~> 1.0, < 2)
43
+ zeitwerk (~> 2.6)
44
+ dry-core (1.0.0)
45
+ concurrent-ruby (~> 1.0)
46
+ zeitwerk (~> 2.6)
47
+ dry-inflector (1.0.0)
48
+ dry-initializer (3.1.1)
49
+ dry-logic (1.5.0)
50
+ concurrent-ruby (~> 1.0)
51
+ dry-core (~> 1.0, < 2)
52
+ zeitwerk (~> 2.6)
53
+ dry-schema (1.13.3)
54
+ concurrent-ruby (~> 1.0)
55
+ dry-configurable (~> 1.0, >= 1.0.1)
56
+ dry-core (~> 1.0, < 2)
57
+ dry-initializer (~> 3.0)
58
+ dry-logic (>= 1.4, < 2)
59
+ dry-types (>= 1.7, < 2)
60
+ zeitwerk (~> 2.6)
61
+ dry-types (1.7.1)
62
+ concurrent-ruby (~> 1.0)
63
+ dry-core (~> 1.0)
64
+ dry-inflector (~> 1.0)
65
+ dry-logic (~> 1.4)
66
+ zeitwerk (~> 2.6)
67
+ dry-validation (1.10.0)
68
+ concurrent-ruby (~> 1.0)
69
+ dry-core (~> 1.0, < 2)
70
+ dry-initializer (~> 3.0)
71
+ dry-schema (>= 1.12, < 2)
72
+ zeitwerk (~> 2.6)
73
+ erubi (1.12.0)
74
+ i18n (1.14.1)
75
+ concurrent-ruby (~> 1.0)
76
+ interactor (3.1.2)
77
+ interactor-contracts (0.3.0)
78
+ dry-validation (~> 1.0)
79
+ interactor (~> 3)
80
+ io-console (0.7.1)
81
+ irb (1.11.0)
82
+ rdoc
83
+ reline (>= 0.3.8)
84
+ loofah (2.22.0)
85
+ crass (~> 1.0.2)
86
+ nokogiri (>= 1.12.0)
87
+ method_source (1.0.0)
88
+ mini_portile2 (2.8.5)
89
+ minitest (5.20.0)
90
+ nokogiri (1.15.5)
91
+ mini_portile2 (~> 2.8.2)
92
+ racc (~> 1.4)
93
+ psych (5.1.2)
94
+ stringio
95
+ racc (1.7.3)
96
+ rack (2.2.8)
97
+ rack-test (2.1.0)
98
+ rack (>= 1.3)
99
+ rails-dom-testing (2.2.0)
100
+ activesupport (>= 5.0.0)
101
+ minitest
102
+ nokogiri (>= 1.6)
103
+ rails-html-sanitizer (1.6.0)
104
+ loofah (~> 2.21)
105
+ nokogiri (~> 1.14)
106
+ railties (7.0.0)
107
+ actionpack (= 7.0.0)
108
+ activesupport (= 7.0.0)
109
+ method_source
110
+ rake (>= 12.2)
111
+ thor (~> 1.0)
112
+ zeitwerk (~> 2.5)
113
+ rake (13.1.0)
114
+ rdoc (6.6.2)
115
+ psych (>= 4.0.0)
116
+ reline (0.4.1)
117
+ io-console (~> 0.5)
118
+ rspec (3.12.0)
119
+ rspec-core (~> 3.12.0)
120
+ rspec-expectations (~> 3.12.0)
121
+ rspec-mocks (~> 3.12.0)
122
+ rspec-core (3.12.2)
123
+ rspec-support (~> 3.12.0)
124
+ rspec-expectations (3.12.3)
125
+ diff-lcs (>= 1.2.0, < 2.0)
126
+ rspec-support (~> 3.12.0)
127
+ rspec-mocks (3.12.6)
128
+ diff-lcs (>= 1.2.0, < 2.0)
129
+ rspec-support (~> 3.12.0)
130
+ rspec-support (3.12.1)
131
+ simplecov (0.22.0)
132
+ docile (~> 1.1)
133
+ simplecov-html (~> 0.11)
134
+ simplecov_json_formatter (~> 0.1)
135
+ simplecov-html (0.12.3)
136
+ simplecov_json_formatter (0.1.4)
137
+ stringio (3.1.0)
138
+ thor (1.3.0)
139
+ tzinfo (2.0.6)
140
+ concurrent-ruby (~> 1.0)
141
+ zeitwerk (2.6.12)
142
+
143
+ PLATFORMS
144
+ ruby
145
+
146
+ DEPENDENCIES
147
+ appraisal
148
+ bundler (~> 2.0)
149
+ debug
150
+ interactify!
151
+ railties (= 7)
152
+ rake (~> 13.0)
153
+ rspec (~> 3.0)
154
+ simplecov
155
+
156
+ BUNDLED WITH
157
+ 2.4.22
@@ -0,0 +1,18 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake", "~> 13.0"
6
+ gem "railties", "7"
7
+ gem "sidekiq", "7"
8
+
9
+ group :development do
10
+ gem "bundler", "~> 2.0"
11
+ end
12
+
13
+ group :test do
14
+ gem "simplecov", require: false
15
+ gem "rspec", "~> 3.0"
16
+ end
17
+
18
+ gemspec path: "../"
@@ -0,0 +1,166 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ interactify (0.3.0.pre.alpha.2)
5
+ interactor
6
+ interactor-contracts
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ actionpack (7.0.0)
12
+ actionview (= 7.0.0)
13
+ activesupport (= 7.0.0)
14
+ rack (~> 2.0, >= 2.2.0)
15
+ rack-test (>= 0.6.3)
16
+ rails-dom-testing (~> 2.0)
17
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
18
+ actionview (7.0.0)
19
+ activesupport (= 7.0.0)
20
+ builder (~> 3.1)
21
+ erubi (~> 1.4)
22
+ rails-dom-testing (~> 2.0)
23
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
24
+ activesupport (7.0.0)
25
+ concurrent-ruby (~> 1.0, >= 1.0.2)
26
+ i18n (>= 1.6, < 2)
27
+ minitest (>= 5.1)
28
+ tzinfo (~> 2.0)
29
+ appraisal (2.5.0)
30
+ bundler
31
+ rake
32
+ thor (>= 0.14.0)
33
+ builder (3.2.4)
34
+ concurrent-ruby (1.2.2)
35
+ connection_pool (2.4.1)
36
+ crass (1.0.6)
37
+ debug (1.9.1)
38
+ irb (~> 1.10)
39
+ reline (>= 0.3.8)
40
+ diff-lcs (1.5.0)
41
+ docile (1.4.0)
42
+ dry-configurable (1.0.1)
43
+ dry-core (~> 1.0, < 2)
44
+ zeitwerk (~> 2.6)
45
+ dry-core (1.0.0)
46
+ concurrent-ruby (~> 1.0)
47
+ zeitwerk (~> 2.6)
48
+ dry-inflector (1.0.0)
49
+ dry-initializer (3.1.1)
50
+ dry-logic (1.5.0)
51
+ concurrent-ruby (~> 1.0)
52
+ dry-core (~> 1.0, < 2)
53
+ zeitwerk (~> 2.6)
54
+ dry-schema (1.13.3)
55
+ concurrent-ruby (~> 1.0)
56
+ dry-configurable (~> 1.0, >= 1.0.1)
57
+ dry-core (~> 1.0, < 2)
58
+ dry-initializer (~> 3.0)
59
+ dry-logic (>= 1.4, < 2)
60
+ dry-types (>= 1.7, < 2)
61
+ zeitwerk (~> 2.6)
62
+ dry-types (1.7.1)
63
+ concurrent-ruby (~> 1.0)
64
+ dry-core (~> 1.0)
65
+ dry-inflector (~> 1.0)
66
+ dry-logic (~> 1.4)
67
+ zeitwerk (~> 2.6)
68
+ dry-validation (1.10.0)
69
+ concurrent-ruby (~> 1.0)
70
+ dry-core (~> 1.0, < 2)
71
+ dry-initializer (~> 3.0)
72
+ dry-schema (>= 1.12, < 2)
73
+ zeitwerk (~> 2.6)
74
+ erubi (1.12.0)
75
+ i18n (1.14.1)
76
+ concurrent-ruby (~> 1.0)
77
+ interactor (3.1.2)
78
+ interactor-contracts (0.3.0)
79
+ dry-validation (~> 1.0)
80
+ interactor (~> 3)
81
+ io-console (0.7.1)
82
+ irb (1.11.0)
83
+ rdoc
84
+ reline (>= 0.3.8)
85
+ loofah (2.22.0)
86
+ crass (~> 1.0.2)
87
+ nokogiri (>= 1.12.0)
88
+ method_source (1.0.0)
89
+ mini_portile2 (2.8.5)
90
+ minitest (5.20.0)
91
+ nokogiri (1.15.5)
92
+ mini_portile2 (~> 2.8.2)
93
+ racc (~> 1.4)
94
+ psych (5.1.2)
95
+ stringio
96
+ racc (1.7.3)
97
+ rack (2.2.8)
98
+ rack-test (2.1.0)
99
+ rack (>= 1.3)
100
+ rails-dom-testing (2.2.0)
101
+ activesupport (>= 5.0.0)
102
+ minitest
103
+ nokogiri (>= 1.6)
104
+ rails-html-sanitizer (1.6.0)
105
+ loofah (~> 2.21)
106
+ nokogiri (~> 1.14)
107
+ railties (7.0.0)
108
+ actionpack (= 7.0.0)
109
+ activesupport (= 7.0.0)
110
+ method_source
111
+ rake (>= 12.2)
112
+ thor (~> 1.0)
113
+ zeitwerk (~> 2.5)
114
+ rake (13.1.0)
115
+ rdoc (6.6.2)
116
+ psych (>= 4.0.0)
117
+ redis-client (0.19.1)
118
+ connection_pool
119
+ reline (0.4.1)
120
+ io-console (~> 0.5)
121
+ rspec (3.12.0)
122
+ rspec-core (~> 3.12.0)
123
+ rspec-expectations (~> 3.12.0)
124
+ rspec-mocks (~> 3.12.0)
125
+ rspec-core (3.12.2)
126
+ rspec-support (~> 3.12.0)
127
+ rspec-expectations (3.12.3)
128
+ diff-lcs (>= 1.2.0, < 2.0)
129
+ rspec-support (~> 3.12.0)
130
+ rspec-mocks (3.12.6)
131
+ diff-lcs (>= 1.2.0, < 2.0)
132
+ rspec-support (~> 3.12.0)
133
+ rspec-support (3.12.1)
134
+ sidekiq (7.0.0)
135
+ concurrent-ruby (< 2)
136
+ connection_pool (>= 2.3.0)
137
+ rack (>= 2.2.4)
138
+ redis-client (>= 0.9.0)
139
+ simplecov (0.22.0)
140
+ docile (~> 1.1)
141
+ simplecov-html (~> 0.11)
142
+ simplecov_json_formatter (~> 0.1)
143
+ simplecov-html (0.12.3)
144
+ simplecov_json_formatter (0.1.4)
145
+ stringio (3.1.0)
146
+ thor (1.3.0)
147
+ tzinfo (2.0.6)
148
+ concurrent-ruby (~> 1.0)
149
+ zeitwerk (2.6.12)
150
+
151
+ PLATFORMS
152
+ ruby
153
+
154
+ DEPENDENCIES
155
+ appraisal
156
+ bundler (~> 2.0)
157
+ debug
158
+ interactify!
159
+ railties (= 7)
160
+ rake (~> 13.0)
161
+ rspec (~> 3.0)
162
+ sidekiq (= 7)
163
+ simplecov
164
+
165
+ BUNDLED WITH
166
+ 2.4.22
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "interactify/jobable"
4
4
  require "interactify/call_wrapper"
5
- require "interactify/organizer_call_monkey_patch"
5
+ require "interactify/organizer"
6
6
  require "interactify/contract_failure"
7
7
 
8
8
  module Interactify
@@ -48,7 +48,7 @@ module Interactify
48
48
  # failure class: Whatever::SomeInteractor::InteractorContractFailure
49
49
  const_set "InteractorContractFailure", c
50
50
  prepend CallWrapper
51
- include OrganizerCallMonkeyPatch if ancestors.include? Interactor::Organizer
51
+ include Organizer
52
52
 
53
53
  on_breach do |breaches|
54
54
  breaches = breaches.map { |b| { b.property => b.messages } }.inject(&:merge)
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "interactify/each_chain"
4
4
  require "interactify/if_interactor"
5
+ require "interactify/unique_klass_name"
5
6
 
6
7
  module Interactify
7
8
  module Dsl
@@ -63,6 +64,7 @@ module Interactify
63
64
 
64
65
  # attach the class to the calling namespace
65
66
  where_to_attach = self.binding.receiver
67
+ klass_name = UniqueKlassName.for(where_to_attach, klass_name)
66
68
  where_to_attach.const_set(klass_name, klass)
67
69
  end
68
70
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "interactify/unique_klass_name"
4
+
3
5
  module Interactify
4
6
  class EachChain
5
7
  attr_reader :each_loop_klasses, :plural_resource_name, :evaluating_receiver
@@ -38,7 +40,7 @@ module Interactify
38
40
  context[this.singular_resource_name] = resource # context.package = package
39
41
  context[this.singular_resource_index_name] = index # context.package_index = index
40
42
 
41
- klasses = self.class.wrap_lambdas_in_interactors(this.each_loop_klasses)
43
+ klasses = InteractorWrapper.wrap_many(self, this.each_loop_klasses)
42
44
 
43
45
  klasses.each do |interactor| # [A, B, C].each do |interactor|
44
46
  interactor.call!(context) # interactor.call!(context)
@@ -59,8 +61,10 @@ module Interactify
59
61
  # rubocop:enable all
60
62
 
61
63
  def attach_klass
62
- namespace.const_set(iterator_klass_name, klass)
63
- namespace.const_get(iterator_klass_name)
64
+ name = iterator_klass_name
65
+
66
+ namespace.const_set(name, klass)
67
+ namespace.const_get(name)
64
68
  end
65
69
 
66
70
  def namespace
@@ -68,7 +72,9 @@ module Interactify
68
72
  end
69
73
 
70
74
  def iterator_klass_name
71
- :"Each#{singular_resource_name.to_s.camelize}".to_sym
75
+ prefix = "Each#{singular_resource_name.to_s.camelize}"
76
+
77
+ UniqueKlassName.for(namespace, prefix)
72
78
  end
73
79
 
74
80
  def singular_resource_name
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "interactify/unique_klass_name"
4
+
3
5
  module Interactify
4
6
  class IfInteractor
5
7
  attr_reader :condition, :success_interactor, :failure_interactor, :evaluating_receiver
@@ -49,8 +51,9 @@ module Interactify
49
51
  # rubocop:enable all
50
52
 
51
53
  def attach_klass
52
- namespace.const_set(if_klass_name, klass)
53
- namespace.const_get(if_klass_name)
54
+ name = if_klass_name
55
+ namespace.const_set(name, klass)
56
+ namespace.const_get(name)
54
57
  end
55
58
 
56
59
  def namespace
@@ -58,9 +61,10 @@ module Interactify
58
61
  end
59
62
 
60
63
  def if_klass_name
61
- name = condition.is_a?(Proc) ? "Proc" : condition
64
+ prefix = condition.is_a?(Proc) ? "Proc" : condition
65
+ prefix = "If#{prefix.to_s.camelize}"
62
66
 
63
- "If#{name.to_s.camelize}".to_sym
67
+ UniqueKlassName.for(namespace, prefix)
64
68
  end
65
69
  end
66
70
  end
@@ -67,7 +67,7 @@ module Interactify
67
67
 
68
68
  def interactor_klass?(object)
69
69
  return unless object.is_a?(Class) && object.ancestors.include?(Interactor)
70
- return if object.is_a?(Sidekiq::Job)
70
+ return if Interactify.sidekiq? && object.is_a?(Sidekiq::Job)
71
71
 
72
72
  true
73
73
  end
@@ -116,9 +116,9 @@ module Interactify
116
116
  .gsub(root.to_s, "") # "/namespace/sub_namespace/class_name.rb"
117
117
  .gsub("/concerns", "") # concerns directory is ignored by Zeitwerk
118
118
  .split("/") # "['', 'namespace', 'sub_namespace', 'class_name.rb']
119
- .compact_blank # "['namespace', 'sub_namespace', 'class_name.rb']
119
+ .reject(&:blank?) # "['namespace', 'sub_namespace', 'class_name.rb']
120
120
  .join("/") # 'namespace/sub_namespace/class_name.rb'
121
- .gsub(/\.rb\z/, "") # 'namespace/sub_namespace/class_name'
121
+ .gsub(/\.rb\z/, "") # 'namespace/sub_namespace/class_name'
122
122
  end
123
123
  end
124
124
  end
@@ -10,7 +10,7 @@ module Interactify
10
10
  class InteractorWiring
11
11
  attr_reader :root, :ignore
12
12
 
13
- def initialize(root: Rails.root, ignore: [])
13
+ def initialize(root:, ignore: [])
14
14
  @root = root.to_s.gsub(%r{/$}, "")
15
15
  @ignore = ignore
16
16
  end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "interactify/unique_klass_name"
4
+
5
+ module Interactify
6
+ class InteractorWrapper
7
+ attr_reader :organizer, :interactor
8
+
9
+ def self.wrap_many(organizer, interactors)
10
+ Array(interactors).map do |interactor|
11
+ wrap(organizer, interactor)
12
+ end
13
+ end
14
+
15
+ def self.wrap(organizer, interactor)
16
+ new(organizer, interactor).wrap
17
+ end
18
+
19
+ def initialize(organizer, interactor)
20
+ @organizer = organizer
21
+ @interactor = interactor
22
+ end
23
+
24
+ def wrap
25
+ case interactor
26
+ when Hash
27
+ wrap_conditional
28
+ when Array
29
+ wrap_chain
30
+ when Proc
31
+ wrap_proc
32
+ else
33
+ interactor
34
+ end
35
+ end
36
+
37
+ def wrap_chain
38
+ return self.class.wrap(organizer, interactor.first) if interactor.length == 1
39
+
40
+ klass_name = UniqueKlassName.for(organizer, "Chained")
41
+ organizer.chain(klass_name, *interactor.map { self.class.wrap(organizer, _1) })
42
+ end
43
+
44
+ def wrap_conditional
45
+ raise ArgumentError, "Hash must have at least :if, and :then key" unless condition && then_do
46
+
47
+ return organizer.if(condition, then_do, else_do) if else_do
48
+
49
+ organizer.if(condition, then_do)
50
+ end
51
+
52
+ def condition = interactor[:if]
53
+ def then_do = interactor[:then]
54
+ def else_do = interactor[:else]
55
+
56
+ def wrap_proc
57
+ this = self
58
+
59
+ Class.new do
60
+ include Interactify
61
+
62
+ define_singleton_method :wrapped do
63
+ this.interactor
64
+ end
65
+
66
+ define_method(:call) do
67
+ this.interactor.call(context)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -1,9 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "sidekiq"
4
- require "sidekiq/job"
5
-
6
3
  require "interactify/async_job_klass"
4
+ require "interactify/null_job"
7
5
 
8
6
  module Interactify
9
7
  class JobMaker
@@ -24,6 +22,8 @@ module Interactify
24
22
  private
25
23
 
26
24
  def define_job_klass
25
+ return NullJob if Interactify.sidekiq_missing?
26
+
27
27
  this = self
28
28
 
29
29
  invalid_keys = this.opts.symbolize_keys.keys - %i[queue retry dead backtrace pool tags]
@@ -11,6 +11,8 @@ module Interactify
11
11
  #
12
12
  # then let's make sure to define Klass::Job separately
13
13
  included do |base|
14
+ next if Interactify.sidekiq_missing?
15
+
14
16
  def base.inherited(klass)
15
17
  super_klass = klass.superclass
16
18
  super_job = super_klass::Job # really spiffing
@@ -0,0 +1,11 @@
1
+ module Interactify
2
+ class NullJob
3
+ def method_missing(...)
4
+ self
5
+ end
6
+
7
+ def self.method_missing(...)
8
+ self
9
+ end
10
+ end
11
+ end
@@ -1,32 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "interactify/interactor_wrapper"
4
+
3
5
  module Interactify
4
- module OrganizerCallMonkeyPatch
6
+ module Organizer
5
7
  extend ActiveSupport::Concern
6
8
 
7
9
  class_methods do
8
10
  def organize(*interactors)
9
- wrapped = wrap_lambdas_in_interactors(interactors)
11
+ wrapped = InteractorWrapper.wrap_many(self, interactors)
10
12
 
11
13
  super(*wrapped)
12
14
  end
13
-
14
- def wrap_lambdas_in_interactors(interactors)
15
- Array(interactors).map do |interactor|
16
- case interactor
17
- when Proc
18
- Class.new do
19
- include Interactify
20
-
21
- define_method(:call) do
22
- interactor.call(context)
23
- end
24
- end
25
- else
26
- interactor
27
- end
28
- end
29
- end
30
15
  end
31
16
 
32
17
  def call
@@ -0,0 +1,21 @@
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Interactify
4
- VERSION = "0.2.0-alpha.1"
4
+ VERSION = "0.3.0-RC1"
5
5
  end