coactive 0.1.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: caf295bc360ec4f4551be981b1233660a33406714b2627d1d7c3118cfde42ba1
4
- data.tar.gz: 55a5254e390a82bf22188fb8d84253d2382e52360abc2a36a007ef129b74572f
3
+ metadata.gz: 0e5b954d90120e156df60ee60c5c271cd7e8666bc507018c6814a5594f62e21b
4
+ data.tar.gz: 1923a89bba5c5b58c8be13f052ef902e2fb8744bccc6fe513bc3f0d84d70a02c
5
5
  SHA512:
6
- metadata.gz: 477b7ff382d1697943a0898ac2d8703aaa98fde5088e1f88c6e6a05f7cdee08d4071914d5a8308103ec0dd7a553df777a5d0382bf088db44bdf0d2e5f80c9b7c
7
- data.tar.gz: 6c038fdee9aed452ac716abd2d6f29149164c57e0db32777fde17bad46652f25ba1ad34c86cad1139e90c8e41911cdae46428df9572e9b80efb783afafe815a1
6
+ metadata.gz: e6b0e3585803ebfe054851c4f55db0276e4544f0eeb169375d20e9901126377a804e3e56e10b836092b7fcab41b1dd025ecc5cfc0f9fac2ec7743c757bdd4efc
7
+ data.tar.gz: 4dc336fd5a76d3be0c303febe8d8a05d3b0dcfc4dc6ad2d6dbbcb977286c73e1e24a8a203212cb60fa051ceb8d1a8445ad055f83c01efbfe34407a681683bc8f
@@ -4,21 +4,29 @@ on: [push, pull_request]
4
4
 
5
5
  jobs:
6
6
  test:
7
- runs-on: ubuntu-18.04
7
+ runs-on: ubuntu-20.04
8
8
  strategy:
9
9
  fail-fast: false
10
10
  matrix:
11
- ruby: [2.3, 2.4, 2.5, 2.6, 2.7, 3.0]
12
- gemfile: ['rails50', 'rails51', 'rails52', 'rails60', 'rails61']
11
+ ruby: [2.3, 2.4, 2.5, 2.6, 2.7, '3.0']
12
+ gemfile: ['rails50', 'rails51', 'rails52', 'rails60', 'rails61', 'rails70']
13
13
  exclude:
14
14
  - ruby: 2.3
15
15
  gemfile: rails60
16
16
  - ruby: 2.3
17
17
  gemfile: rails61
18
+ - ruby: 2.3
19
+ gemfile: rails70
18
20
  - ruby: 2.4
19
21
  gemfile: rails60
20
22
  - ruby: 2.4
21
23
  gemfile: rails61
24
+ - ruby: 2.4
25
+ gemfile: rails70
26
+ - ruby: 2.5
27
+ gemfile: rails70
28
+ - ruby: 2.6
29
+ gemfile: rails70
22
30
  - ruby: 3.0
23
31
  gemfile: rails50
24
32
  - ruby: 3.0
data/.gitignore CHANGED
@@ -1,6 +1,7 @@
1
1
  .bundle/
2
2
  Gemfile.lock
3
3
  coverage/
4
+ gemfiles/*.lock
4
5
  pkg/
5
6
  tmp/
6
7
  spec/dummy/db/*.sqlite3
data/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # CHANGELOG
2
2
 
3
- ## 1.0.0
3
+ ## 0.2.1
4
+
5
+ * Truncate string for context.
6
+
7
+ ## 0.2.0
8
+
9
+ * Add context feature.
10
+ * Add priority to coaction.
11
+
12
+ ## 0.1.2
13
+
14
+ * Clear lookup cache when reloaded.
15
+ * Use require_dependency instead of require.
16
+
17
+ ## 0.1.1
18
+
19
+ * Reassign config data when configured.
20
+
21
+ ## 0.1.0
4
22
 
5
23
  * First release.
data/README.md CHANGED
@@ -21,6 +21,8 @@ Then execute:
21
21
 
22
22
  ## Usage
23
23
 
24
+ ### Coactors
25
+
24
26
  Include `Coactive::Base` to your base class:
25
27
 
26
28
  ```ruby
@@ -51,7 +53,7 @@ C.new.coactors
51
53
  # => [A, B]
52
54
  ```
53
55
 
54
- ### Named coactors
56
+ #### Named coactors
55
57
 
56
58
  You can also define coactive classes by using specific name:
57
59
 
@@ -88,30 +90,30 @@ class Base
88
90
  end
89
91
  ```
90
92
 
91
- ### Object-based coactors
93
+ #### Object-based coactors
92
94
 
93
95
  You can also define coactive classes by using object:
94
96
 
95
97
  ```ruby
96
- class ModelA
98
+ class ItemA
97
99
  end
98
100
 
99
- class ModelB
101
+ class ItemB
100
102
  end
101
103
 
102
- class Base::ModelA < Base
104
+ class Base::ItemA < Base
103
105
  end
104
106
 
105
- class Base::ModelB < Base
107
+ class Base::ItemB < Base
106
108
  end
107
109
 
108
110
  class Base::C < Base
109
- coact ModelA
110
- coact ModelB
111
+ coact ItemA
112
+ coact ItemB
111
113
  end
112
114
 
113
115
  Base::C.new.coactors
114
- #=> [Base::ModelA, Base::ModelB]
116
+ #=> [Base::ItemA, Base::ItemB]
115
117
  ```
116
118
 
117
119
  Coactors are looked up from the namespace corresponding with caller classes.
@@ -130,7 +132,7 @@ class Base
130
132
  end
131
133
  ```
132
134
 
133
- ### Dynamic coactors
135
+ #### Dynamic coactors
134
136
 
135
137
  You can also dynamically lookup coactors by using block or instance method:
136
138
 
@@ -184,7 +186,7 @@ D.new('B').coactors
184
186
  #=> [B]
185
187
  ```
186
188
 
187
- ### Nested coactors
189
+ #### Nested coactors
188
190
 
189
191
  You can define nested coactors. For example:
190
192
 
@@ -212,6 +214,110 @@ C.new.coactors.map { |klass| [klass] + klass.new.coactors }.flatten
212
214
  #=> [A, NestedA, B, NestedB]
213
215
  ```
214
216
 
217
+ ### Context
218
+
219
+ You can define variables used in a coactor as a context by including `Coactive::Initializer`.
220
+ The variables are stored in `context` as follows:
221
+
222
+ ```ruby
223
+ class Base
224
+ include Coactive::Base
225
+ include Coactive::Initializer
226
+ end
227
+
228
+ class A < Base
229
+ context :input
230
+ end
231
+
232
+ coactor = A.new(input: 'something')
233
+ coactor.context.input
234
+ #=> something
235
+ ```
236
+
237
+ #### Required context
238
+
239
+ You can also define required context as follows:
240
+
241
+ ```ruby
242
+ class A < Base
243
+ context :input, required: true
244
+ end
245
+
246
+ A.new
247
+ #=> Coactive::MissingContextError (missing required context: input)
248
+ ```
249
+
250
+ #### Default value
251
+
252
+ You can also define default value as follows:
253
+
254
+ ```ruby
255
+ class A < Base
256
+ context :input, default: 'something'
257
+ end
258
+
259
+ coactor = A.new
260
+ coactor.context.input
261
+ #=> something
262
+ ```
263
+
264
+ ### Contextualizer
265
+
266
+ You can copy context variables to a coactor by calling `contextualize`:
267
+
268
+ ```ruby
269
+ class Base
270
+ include Coactive::Base
271
+ include Coactive::Initializer
272
+ include Coactive::Contextualizer
273
+ end
274
+
275
+ class A < Base
276
+ context :input
277
+ end
278
+
279
+ coactor = A.new(input: 'something')
280
+ coactor.contextualize
281
+ coactor.input
282
+ #=> something
283
+ coactor.instance_variable_get(:@input)
284
+ #=> something
285
+ ```
286
+
287
+ #### Output
288
+
289
+ You can also set context when finished `contextualize` block:
290
+
291
+ ```ruby
292
+ class A < Base
293
+ context :result, output: true
294
+
295
+ def call
296
+ @result = 'something'
297
+ end
298
+ end
299
+
300
+ coactor = A.new
301
+ coactor.contextualize { coactor.call }
302
+ coactor.context.result
303
+ #=> something
304
+ ```
305
+
306
+ #### Output return value
307
+
308
+ You can also set context from return value of `contextualize` block:
309
+
310
+ ```ruby
311
+ class A < Base
312
+ context :result, output: :return
313
+ end
314
+
315
+ coactor = A.new
316
+ coactor.contextualize { 'return value' }
317
+ coactor.context.result
318
+ #=> return value
319
+ ```
320
+
215
321
  ### Configuration
216
322
 
217
323
  You can set configurations in your base class as follows:
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem "rails", "~> 7.0.0"
4
+
5
+ gemspec path: "../"
data/lib/coactive/base.rb CHANGED
@@ -1,10 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'configure'
4
- require_relative 'coact'
5
- require_relative 'coaction'
6
- require_relative 'coactors'
7
- require_relative 'loader'
4
+ require_relative 'coactor'
5
+ require_relative 'interface'
6
+ require_relative 'errors'
8
7
 
9
8
  module Coactive
10
9
  module Base
@@ -12,9 +11,7 @@ module Coactive
12
11
 
13
12
  included do
14
13
  include Configure
15
- include Coact
16
- include Coaction
17
- include Coactors
14
+ include Coactor
18
15
  end
19
16
  end
20
17
  end
@@ -1,10 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'loader'
3
4
  require_relative 'lookup'
5
+ require_relative 'coactors/coactants'
6
+ require_relative 'coactors/coactions'
4
7
 
5
8
  module Coactive
6
- module Coactors
9
+ module Coactor
7
10
  extend ActiveSupport::Concern
11
+ include Coactors::Coactants
12
+ include Coactors::Coactions
8
13
 
9
14
  def coactors
10
15
  self.class._coactants.map do |coactant|
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coactive
4
+ module Coactors
5
+ module Coactants
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute :_coactants
10
+ self._coactants = []
11
+ end
12
+
13
+ class_methods do
14
+ def coact(*coactants, **options, &block)
15
+ if block
16
+ self._coactants = _coactants + [block]
17
+ elsif options[:before]
18
+ index = self._coactants.index { |coactant| coactant == options[:before] }
19
+ self._coactants = self._coactants.insert(index, *coactants)
20
+ else
21
+ self._coactants = _coactants + coactants
22
+ end
23
+ end
24
+
25
+ def coactants
26
+ self._coactants
27
+ end
28
+
29
+ def clear_coactants
30
+ self._coactants = []
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coactive
4
+ module Coactors
5
+ class Coaction < Struct.new(:name, :options)
6
+ def priority
7
+ options[:priority]
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'coaction'
4
+
5
+ module Coactive
6
+ module Coactors
7
+ module Coactions
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ class_attribute :_coactions
12
+ self._coactions = []
13
+ end
14
+
15
+ class_methods do
16
+ def coaction(*names, **options)
17
+ self._coactions = _coactions + names.map { |name| Coaction.new(name, options) }
18
+ end
19
+
20
+ def coactions
21
+ self._coactions
22
+ end
23
+
24
+ def clear_coactions
25
+ self._coactions = []
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -11,8 +11,13 @@ module Coactive
11
11
  lookup_superclass_until: ['ActiveRecord::Base', 'ActiveModel::Base'],
12
12
  }
13
13
 
14
- def initialize
14
+ attr_accessor :data
15
+
16
+ def initialize(attrs = {})
15
17
  @data = DEFAULTS.deep_dup
18
+ attrs.each do |key, value|
19
+ send("#{key}=", value)
20
+ end
16
21
  end
17
22
 
18
23
  DEFAULTS.keys.each do |key|
@@ -14,6 +14,7 @@ module Coactive
14
14
 
15
15
  class_methods do
16
16
  def configure_coactive
17
+ self.coactive_config = Config.new(coactive_config.data)
17
18
  yield coactive_config
18
19
  end
19
20
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coactive
4
+ class Context
5
+ attr_reader :_data
6
+
7
+ def initialize(data = {}, &block)
8
+ data = data.to_h if data.respond_to?(:to_h)
9
+ @_data = data
10
+ end
11
+
12
+ def [](key)
13
+ @_data[key]
14
+ end
15
+
16
+ def []=(key, value)
17
+ @_data[key] = value
18
+ end
19
+
20
+ def key?(key)
21
+ @_data.key?(key)
22
+ end
23
+
24
+ def to_h
25
+ @_data
26
+ end
27
+
28
+ def to_s
29
+ attrs = @_data.map { |k, v| "#{k}=#{v.to_s.truncate(300)}" }.join(', ')
30
+ "#<#{self.class} #{attrs}>"
31
+ end
32
+
33
+ def define_accessors!(keys)
34
+ Array(keys).each do |key|
35
+ define_singleton_method key do
36
+ @_data[key]
37
+ end
38
+ define_singleton_method "#{key}=" do |value|
39
+ @_data[key] = value
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coactive
4
+ module Contextualizer
5
+ extend ActiveSupport::Concern
6
+
7
+ def contextualize
8
+ self.class._contexts.each do |var|
9
+ instance_variable_set("@#{var.name}", @context[var.name])
10
+ define_singleton_method var.name do
11
+ instance_variable_get("@#{var.name}")
12
+ end
13
+ end
14
+
15
+ return_value = yield if block_given?
16
+
17
+ self.class._contexts.each do |var|
18
+ if var.output_return?
19
+ @context[var.name] = return_value
20
+ elsif var.output? && instance_variable_defined?("@#{var.name}")
21
+ @context[var.name] = instance_variable_get("@#{var.name}")
22
+ end
23
+ end
24
+
25
+ return_value
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coactive
4
+ class MissingContextError < StandardError
5
+ end
6
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'context'
4
+ require_relative 'initializers/contexts'
5
+
6
+ module Coactive
7
+ module Initializer
8
+ extend ActiveSupport::Concern
9
+ include Initializers::Contexts
10
+
11
+ included do
12
+ class_attribute :context_class
13
+ self.context_class = Context
14
+ attr_reader :context
15
+ end
16
+
17
+ def initialize(args = {}, &block)
18
+ @context = self.class.context_class.new(args, &block)
19
+ @context.define_accessors!(self.class._contexts.map(&:name))
20
+
21
+ self.class._contexts.each do |var|
22
+ if var.required? && !@context.key?(var.name)
23
+ raise MissingContextError.new("missing required context: #{var.name}")
24
+ end
25
+ if var.default && !@context.key?(var.name)
26
+ @context[var.name] = Initializer.resolve(self, var.default)
27
+ end
28
+ end
29
+ end
30
+
31
+ class << self
32
+ def resolve(instance, value)
33
+ if value.respond_to?(:call)
34
+ instance.instance_exec(&value)
35
+ elsif value.is_a?(Symbol) && instance.respond_to?(value, true)
36
+ instance.send(value)
37
+ else
38
+ value.deep_dup
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'variable'
4
+
5
+ module Coactive
6
+ module Initializers
7
+ module Contexts
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ class_attribute :_contexts
12
+ self._contexts = []
13
+ end
14
+
15
+ class_methods do
16
+ def context(*names, **options)
17
+ self._contexts = self._contexts + names.map { |name| Variable.new(name, options) }
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coactive
4
+ module Initializers
5
+ class Variable < Struct.new(:name, :options)
6
+ def default
7
+ options[:default] if options
8
+ end
9
+
10
+ def required?
11
+ options[:required] if options
12
+ end
13
+
14
+ def output?
15
+ options[:output] if options
16
+ end
17
+
18
+ def output_return?
19
+ options[:output] == :return if options
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'initializer'
4
+ require_relative 'contextualizer'
5
+
6
+ module Coactive
7
+ module Interface
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ include Initializer
12
+ include Contextualizer
13
+ end
14
+ end
15
+ end
@@ -11,7 +11,7 @@ module Coactive
11
11
  engines = [Rails] + Rails::Engine.subclasses.map(&:instance)
12
12
  engines.each do |engine|
13
13
  Dir["#{engine.root}/{#{Array(paths).join(',')}}/**/*.rb"].each do |file|
14
- require file
14
+ require_dependency file
15
15
  end
16
16
  end
17
17
  end
@@ -13,19 +13,19 @@ module Coactive
13
13
  class_attribute :cache
14
14
  self.cache = {}
15
15
 
16
- def call(klass, coactivation)
17
- with_cache(klass, coactivation) do
18
- lookup = lookups.detect { |lookup| lookup.callable?(coactivation) }
19
- lookup.new(klass, coactivation).call if lookup
16
+ def call(klass, coactant)
17
+ with_cache(klass, coactant) do
18
+ lookup = lookups.detect { |lookup| lookup.callable?(coactant) }
19
+ lookup.new(klass, coactant).call if lookup
20
20
  end
21
21
  end
22
22
 
23
23
  private
24
24
 
25
- def with_cache(klass, coactivation)
25
+ def with_cache(klass, coactant)
26
26
  if klass.coactive_config.use_cache
27
27
  self.cache[klass] ||= {}
28
- self.cache[klass][coactivation] ||= yield
28
+ self.cache[klass][coactant] ||= yield
29
29
  else
30
30
  yield
31
31
  end
@@ -15,9 +15,10 @@ module Coactive
15
15
  end
16
16
 
17
17
  def lookup
18
- @klass.coactive_config.base_class.descendants.select do |coactor|
19
- coactor._coactions.any? { |coaction| coaction == @coactant }
20
- end
18
+ @klass.coactive_config.base_class.descendants.each_with_object({}).with_index do |(coactor, hash), i|
19
+ coaction = coactor.coactions.detect { |coaction| coaction.name == @coactant }
20
+ hash[[coaction.priority || 1 << 63, i]] = coactor if coaction
21
+ end.sort.map { |x| x[1] }
21
22
  end
22
23
 
23
24
  class << self
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coactive
4
+ module Railtie
5
+ ActiveSupport::Reloader.to_prepare do
6
+ Coactive::Lookup.cache.clear
7
+ end
8
+ end
9
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Coactive
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.1'
5
5
  end
data/lib/coactive.rb CHANGED
@@ -2,3 +2,4 @@ require 'active_support'
2
2
 
3
3
  require 'coactive/version'
4
4
  require 'coactive/base'
5
+ require 'coactive/railtie' if defined?(Rails)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: coactive
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yoshikazu Kaneta
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-11-28 00:00:00.000000000 Z
11
+ date: 2022-01-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -117,18 +117,28 @@ files:
117
117
  - gemfiles/rails52.gemfile
118
118
  - gemfiles/rails60.gemfile
119
119
  - gemfiles/rails61.gemfile
120
+ - gemfiles/rails70.gemfile
120
121
  - lib/coactive.rb
121
122
  - lib/coactive/base.rb
122
- - lib/coactive/coact.rb
123
- - lib/coactive/coaction.rb
124
- - lib/coactive/coactors.rb
123
+ - lib/coactive/coactor.rb
124
+ - lib/coactive/coactors/coactants.rb
125
+ - lib/coactive/coactors/coaction.rb
126
+ - lib/coactive/coactors/coactions.rb
125
127
  - lib/coactive/config.rb
126
128
  - lib/coactive/configure.rb
129
+ - lib/coactive/context.rb
130
+ - lib/coactive/contextualizer.rb
131
+ - lib/coactive/errors.rb
132
+ - lib/coactive/initializer.rb
133
+ - lib/coactive/initializers/contexts.rb
134
+ - lib/coactive/initializers/variable.rb
135
+ - lib/coactive/interface.rb
127
136
  - lib/coactive/loader.rb
128
137
  - lib/coactive/lookup.rb
129
138
  - lib/coactive/lookups/base.rb
130
139
  - lib/coactive/lookups/name.rb
131
140
  - lib/coactive/lookups/object.rb
141
+ - lib/coactive/railtie.rb
132
142
  - lib/coactive/version.rb
133
143
  homepage: https://github.com/kanety/coactive
134
144
  licenses: []
@@ -148,7 +158,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
148
158
  - !ruby/object:Gem::Version
149
159
  version: '0'
150
160
  requirements: []
151
- rubygems_version: 3.1.2
161
+ rubygems_version: 3.1.6
152
162
  signing_key:
153
163
  specification_version: 4
154
164
  summary: Make classes coactive
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coactive
4
- module Coact
5
- extend ActiveSupport::Concern
6
-
7
- included do
8
- class_attribute :_coactants
9
- self._coactants = []
10
- end
11
-
12
- class_methods do
13
- def coact(*coactants, **options, &block)
14
- if block
15
- self._coactants = _coactants + [block]
16
- elsif options[:before]
17
- index = self._coactants.index { |coactant| coactant == options[:before] }
18
- self._coactants = self._coactants.insert(index, *coactants)
19
- else
20
- self._coactants = _coactants + coactants
21
- end
22
- end
23
-
24
- def coactants
25
- self._coactants
26
- end
27
-
28
- def clear_coactants
29
- self._coactants = []
30
- end
31
- end
32
- end
33
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coactive
4
- module Coaction
5
- extend ActiveSupport::Concern
6
-
7
- included do
8
- class_attribute :_coactions
9
- self._coactions = []
10
- end
11
-
12
- class_methods do
13
- def coaction(*coactions)
14
- self._coactions = _coactions + coactions
15
- end
16
-
17
- def coactions
18
- self._coactions
19
- end
20
-
21
- def clear_coactions
22
- self._coactions = []
23
- end
24
- end
25
- end
26
- end