mona 0.2.3 → 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.

Potentially problematic release.


This version of mona might be problematic. Click here for more details.

Files changed (98) hide show
  1. data/.document +5 -0
  2. data/.rspec +0 -2
  3. data/.travis.yml +3 -0
  4. data/Gemfile +9 -0
  5. data/LICENSE.txt +17 -18
  6. data/README.md +2 -192
  7. data/Rakefile +46 -5
  8. data/VERSION +1 -0
  9. data/lib/mona/board.rb +50 -0
  10. data/lib/mona/client.rb +10 -0
  11. data/lib/mona/response.rb +27 -0
  12. data/lib/mona/thread.rb +64 -0
  13. data/lib/mona.rb +22 -105
  14. data/mona.gemspec +66 -47
  15. data/spec/board_spec.rb +27 -0
  16. data/spec/client_spec.rb +15 -0
  17. data/spec/mona_spec.rb +5 -0
  18. data/spec/response_spec.rb +39 -0
  19. data/spec/spec_helper.rb +12 -0
  20. data/spec/test_data/dat.txt +119 -0
  21. data/spec/test_data/subject.txt +2 -0
  22. data/spec/thread_spec.rb +68 -0
  23. metadata +75 -219
  24. checksums.yaml +0 -7
  25. data/.ruby-version +0 -1
  26. data/.standard.yml +0 -3
  27. data/CHANGELOG.md +0 -5
  28. data/CODE_OF_CONDUCT.md +0 -84
  29. data/example/Gemfile +0 -1
  30. data/example/Gemfile.lock +0 -51
  31. data/example/Monafile.rb +0 -16
  32. data/example/bounded_contexts/cart/cart_app/Package.rb +0 -11
  33. data/example/bounded_contexts/cart/cart_app/bin/console +0 -11
  34. data/example/bounded_contexts/cart/cart_app/package/cart_app.rb +0 -2
  35. data/example/bounded_contexts/cart/cart_core/Package.rb +0 -12
  36. data/example/bounded_contexts/cart/cart_core/bin/console +0 -9
  37. data/example/bounded_contexts/cart/cart_core/package/cart_core.rb +0 -2
  38. data/example/bounded_contexts/cart/cart_datasets/Package.rb +0 -10
  39. data/example/bounded_contexts/cart/cart_datasets/bin/console +0 -9
  40. data/example/bounded_contexts/cart/cart_datasets/package/cart_datasets.rb +0 -2
  41. data/example/bounded_contexts/orders/orders_app/Package.rb +0 -12
  42. data/example/bounded_contexts/orders/orders_app/bin/console +0 -9
  43. data/example/bounded_contexts/orders/orders_app/package/orders_app.rb +0 -2
  44. data/example/bounded_contexts/orders/orders_core/Package.rb +0 -13
  45. data/example/bounded_contexts/orders/orders_core/bin/console +0 -9
  46. data/example/bounded_contexts/orders/orders_core/package/orders_core.rb +0 -2
  47. data/example/bounded_contexts/orders/orders_datasets/Package.rb +0 -11
  48. data/example/bounded_contexts/orders/orders_datasets/bin/console +0 -9
  49. data/example/bounded_contexts/orders/orders_datasets/package/orders_datasets.rb +0 -2
  50. data/example/bounded_contexts/orders/orders_query_api/Package.rb +0 -13
  51. data/example/bounded_contexts/orders/orders_query_api/bin/console +0 -9
  52. data/example/bounded_contexts/orders/orders_query_api/package/cart_query_api.rb +0 -2
  53. data/example/bounded_contexts/orders/paypal_client/Package.rb +0 -9
  54. data/example/bounded_contexts/orders/paypal_client/bin/console +0 -9
  55. data/example/bounded_contexts/orders/paypal_client/package/paypal_client.rb +0 -2
  56. data/example/bounded_contexts/shared/db_connection/Package.rb +0 -10
  57. data/example/bounded_contexts/shared/db_connection/bin/console +0 -9
  58. data/example/bounded_contexts/shared/db_connection/package/db_connection.rb +0 -2
  59. data/example/bounded_contexts/shared/test_utils/Package.rb +0 -9
  60. data/example/bounded_contexts/shared/test_utils/bin/console +0 -9
  61. data/example/bounded_contexts/shared/test_utils/package/test_utils.rb +0 -2
  62. data/example/bounded_contexts/storefront/storefront_app/Package.rb +0 -11
  63. data/example/bounded_contexts/storefront/storefront_app/bin/console +0 -9
  64. data/example/bounded_contexts/storefront/storefront_app/package/storefront_app.rb +0 -2
  65. data/example/bounded_contexts/storefront/storefront_core/Package.rb +0 -11
  66. data/example/bounded_contexts/storefront/storefront_core/bin/console +0 -9
  67. data/example/bounded_contexts/storefront/storefront_core/package/storefront_core.rb +0 -2
  68. data/example/bounded_contexts/storefront/storefront_datasets/Package.rb +0 -11
  69. data/example/bounded_contexts/storefront/storefront_datasets/bin/console +0 -9
  70. data/example/bounded_contexts/storefront/storefront_datasets/package/storefront_datasets.rb +0 -2
  71. data/example/configs/cart_app/default.yml +0 -4
  72. data/example/configs/cart_app/test.yml +0 -3
  73. data/example/configs/orders_app/default.yml +0 -4
  74. data/example/configs/orders_app/test.yml +0 -2
  75. data/exe/mona +0 -15
  76. data/lib/mona/config/loader.rb +0 -74
  77. data/lib/mona/config/manager.rb +0 -20
  78. data/lib/mona/config.rb +0 -23
  79. data/lib/mona/loaders/base.rb +0 -63
  80. data/lib/mona/loaders/zeitwerk.rb +0 -77
  81. data/lib/mona/package/builder.rb +0 -27
  82. data/lib/mona/package/dependency_bypasser.rb +0 -43
  83. data/lib/mona/package/factory.rb +0 -95
  84. data/lib/mona/package/find_root.rb +0 -33
  85. data/lib/mona/package/indexer.rb +0 -30
  86. data/lib/mona/package/initializer.rb +0 -46
  87. data/lib/mona/package/list.rb +0 -10
  88. data/lib/mona/package/repo.rb +0 -63
  89. data/lib/mona/package.rb +0 -51
  90. data/lib/mona/project/builder.rb +0 -20
  91. data/lib/mona/project/factory.rb +0 -115
  92. data/lib/mona/project/find_root.rb +0 -31
  93. data/lib/mona/project/initializer.rb +0 -26
  94. data/lib/mona/project.rb +0 -43
  95. data/lib/mona/task/manager.rb +0 -36
  96. data/lib/mona/task.rb +0 -21
  97. data/lib/mona/version.rb +0 -5
  98. data/sig/mona.rbs +0 -4
@@ -1,95 +0,0 @@
1
- # frozen_string_literal: true
2
- class Mona::Package::Factory
3
- include Mona::Mixins
4
-
5
- sig do
6
- params(
7
- package_root: String,
8
- package_dirname: String,
9
- init_proc: T.proc.params(args: T.anything).returns(T.anything)
10
- )
11
- .returns(Mona::Package)
12
- end
13
- def call(package_root, package_dirname, init_proc:)
14
- @dependencies = Hash.new { |h, k| h[k] = [] }
15
- @current_env = Mona::Package::DEFAULT_ENV
16
-
17
- instance_exec(&init_proc)
18
-
19
- package = Mona::Package.new(
20
- name: @name,
21
- root_path: package_root,
22
- package_dirname: package_dirname,
23
- dependencies: @dependencies
24
- ).tap { check_duplicate_dependencies(_1) }
25
- end
26
-
27
- private
28
-
29
- def check_duplicate_dependencies(package)
30
- package.get_dependency_envs.each do |env|
31
- already_imported = package
32
- .get_dependencies(env)
33
- .group_by{ |e| e }
34
- .select { |k, v| v.size > 1 }
35
- .keys
36
-
37
- if already_imported.any?
38
- puts "WARNING: duplicated import for following packages #{already_imported.inspect} was already added for package '#{package.name}' for env '#{env}'"
39
- end
40
- end
41
- end
42
-
43
- sig do
44
- params(
45
- env: Symbol,
46
- block: T.proc.params(args: T.anything).returns(T.anything)
47
- )
48
- .void
49
- end
50
- def dependency(env = Mona::Package::DEFAULT_ENV, &block)
51
- @current_env = env
52
- instance_eval(&block)
53
- @current_env = Mona::Package::DEFAULT_ENV
54
-
55
- nil
56
- end
57
-
58
- sig do
59
- params(
60
- package_name: T.any(Symbol, String),
61
- )
62
- .void
63
- end
64
- def import(package_name)
65
- package_name = package_name.to_sym
66
-
67
- @dependencies[@current_env] << package_name
68
-
69
- nil
70
- end
71
-
72
- sig do
73
- params(
74
- block: T.proc.params(args: T.anything).returns(T.anything)
75
- )
76
- .void
77
- end
78
- def package(&block)
79
- instance_exec(&block)
80
-
81
- nil
82
- end
83
-
84
- sig do
85
- params(
86
- value: T.any(String, Symbol)
87
- )
88
- .void
89
- end
90
- def name(value)
91
- @name = value.to_sym
92
-
93
- nil
94
- end
95
- end
@@ -1,33 +0,0 @@
1
- class Mona::Package::FindRoot
2
- include Mona::Mixins
3
-
4
- SYSTEM_ROOT = '/'
5
-
6
- sig do
7
- params(
8
- dir: String,
9
- project_root: T.nilable(String),
10
- )
11
- .returns(String)
12
- end
13
- def call(dir, project_root)
14
- init_dir = dir = File.expand_path(dir)
15
- project_root = File.expand_path(project_root || SYSTEM_ROOT)
16
-
17
- unless File.exist?(init_dir)
18
- raise StandardError.new("path '#{init_dir}' does not exist")
19
- end
20
-
21
- loop do
22
- project_file_path = File.expand_path(File.join(dir, Mona::PACKAGE_FILENAME))
23
-
24
- if File.exist?(project_file_path)
25
- return dir
26
- elsif dir == project_root
27
- raise StandardError.new("Package.rb for path '#{init_dir}' not found")
28
- else
29
- dir = File.expand_path("../", dir)
30
- end
31
- end
32
- end
33
- end
@@ -1,30 +0,0 @@
1
- class Mona::Package::Indexer
2
- include Mona::Mixins
3
-
4
- Inject = Mona::Deps[
5
- "package.builder",
6
- ]
7
-
8
- include Inject
9
-
10
- sig do
11
- params(
12
- packages_lookup_subdir: String,
13
- project_root: String,
14
- package_dirname: String
15
- )
16
- .returns(T::Array[Mona::Package])
17
- end
18
- def call(packages_lookup_subdir, project_root, package_dirname)
19
- packages_path = File.join(
20
- project_root,
21
- packages_lookup_subdir,
22
- "**",
23
- Mona::PACKAGE_FILENAME
24
- )
25
-
26
- Dir[packages_path].map do |package_file_path|
27
- builder.call(package_file_path, project_root, package_dirname)
28
- end
29
- end
30
- end
@@ -1,46 +0,0 @@
1
- require 'benchmark'
2
-
3
- class Mona::Package::Initializer
4
- include Mona::Mixins
5
-
6
- Inject = Mona::Deps[
7
- "package.dependency_bypasser",
8
- "package.repo"
9
- ]
10
-
11
- include Inject
12
-
13
- sig do
14
- params(
15
- package_name: T.any(Symbol, String),
16
- env: Symbol,
17
- prevent_eager_load: T::Boolean
18
- )
19
- .void
20
- end
21
- def call(package_name, env:, prevent_eager_load: false)
22
- package = repo.find!(package_name.to_sym)
23
- sorted_packages = nil
24
-
25
- time = Benchmark.realtime do
26
- sorted_packages = dependency_bypasser
27
- .call(package_name: package.name, env: env)
28
- .map { |name| repo.find(name) }
29
-
30
- sorted_packages.each do |sorted_package|
31
- Mona.current_project.loader.push_dir(sorted_package.workdir_path)
32
- Mona.current_project.loader.loader.do_not_eager_load(sorted_package.workdir_path) if prevent_eager_load
33
- end
34
-
35
- Mona.current_project.loader.setup
36
-
37
- sorted_packages.each do |package|
38
- require package.entrypoint_file if File.exist?(package.entrypoint_file)
39
- end
40
- end
41
-
42
- puts "imported package '#{package_name}' with #{sorted_packages.size} dependencies in #{'%.2f' % time} seconds for env: #{env}"
43
-
44
- nil
45
- end
46
- end
@@ -1,10 +0,0 @@
1
- class Mona::Package::List
2
- include Mona::Mixins
3
-
4
- sig do
5
- returns(T::Array[Mona::Package])
6
- end
7
- def call
8
- Mona.current_project.packages
9
- end
10
- end
@@ -1,63 +0,0 @@
1
- class Mona::Package::Repo
2
- include Mona::Mixins
3
-
4
- Inject = Mona::Deps[
5
- "package.list",
6
- ]
7
-
8
- include Inject
9
-
10
- sig do
11
- returns(T::Array[Mona::Package])
12
- end
13
- def all
14
- list.call
15
- end
16
-
17
- sig do
18
- params(
19
- name: T.nilable(
20
- T.any(
21
- String,
22
- Symbol,
23
- T::Array[
24
- T.any(String, Symbol)
25
- ]
26
- )
27
- ),
28
- )
29
- .returns(T::Array[Mona::Package])
30
- end
31
- def filter(name: nil)
32
- packages = all
33
-
34
- unless name.nil?
35
- names = name.is_a?(Array) ? name : [name]
36
- names = names.map(&:to_sym)
37
-
38
- packages = packages.select { names.include?(_1.name) }
39
- end
40
-
41
- packages
42
- end
43
-
44
- sig do
45
- params(
46
- package_name: T.nilable(T.any(String, Symbol)),
47
- )
48
- .returns(T.nilable(Mona::Package))
49
- end
50
- def find(package_name)
51
- all.detect { _1.name == package_name.to_sym }
52
- end
53
-
54
- sig do
55
- params(
56
- package_name: T.nilable(T.any(String, Symbol)),
57
- )
58
- .returns(Mona::Package)
59
- end
60
- def find!(package_name)
61
- find(package_name) || (raise StandardError.new("package '#{package_name}' was not found for Mona project '#{Mona.current_project.root_path}'"))
62
- end
63
- end
data/lib/mona/package.rb DELETED
@@ -1,51 +0,0 @@
1
- class Mona::Package
2
- include Mona::Mixins
3
-
4
- attr_reader :name, :root_path, :dependencies, :configs
5
-
6
- DEFAULT_ENV = :_default_
7
-
8
- sig do
9
- params(
10
- name: Symbol,
11
- root_path: String,
12
- package_dirname: String,
13
- dependencies: T::Hash[Symbol, Symbol]
14
- )
15
- .void
16
- end
17
- def initialize(name:, root_path:, package_dirname:, dependencies:)
18
- @name = name
19
- @root_path = root_path
20
- @package_dirname = package_dirname
21
- @dependencies = dependencies
22
-
23
- nil
24
- end
25
-
26
- sig do
27
- params(env: T.nilable(Symbol)).returns(T::Array[Symbol])
28
- end
29
- def get_dependencies(env = nil)
30
- default_deps = @dependencies.fetch(DEFAULT_ENV, [])
31
- env_deps = @dependencies.fetch(env, [])
32
-
33
- default_deps | env_deps
34
- end
35
-
36
- def get_dependency_envs
37
- @dependencies.keys
38
- end
39
-
40
- def workdir_path
41
- File.join( self.root_path, @package_dirname )
42
- end
43
-
44
- def entrypoint_file
45
- File.join( self.workdir_path, "#{name}.rb" )
46
- end
47
-
48
- def specs_path
49
- File.join( self.root_path, '/spec/' )
50
- end
51
- end
@@ -1,20 +0,0 @@
1
- class Mona::Project::Builder
2
- include Mona::Mixins
3
-
4
- Inject = Mona::Deps[
5
- "project.factory",
6
- ]
7
-
8
- include Inject
9
-
10
- sig do
11
- params(project_root: String).returns(Mona::Project)
12
- end
13
- def call(project_root)
14
- project_file_path = "#{project_root}/#{Mona::PROJECT_FILENAME}"
15
-
16
- project_init_proc = proc { instance_eval(File.read(project_file_path)) }
17
-
18
- project = factory.call(project_root, init_proc: project_init_proc)
19
- end
20
- end
@@ -1,115 +0,0 @@
1
- class Mona::Project::Factory
2
- include Mona::Mixins
3
-
4
- sig do
5
- params(
6
- root_path: String,
7
- init_proc: T.proc.params(args: T.anything).returns(T.anything)
8
- )
9
- .returns(Mona::Project)
10
- end
11
- def call(root_path, init_proc:)
12
- @tasks = []
13
- @root_path = root_path
14
-
15
- instance_exec(&init_proc)
16
-
17
- project = Mona::Project.new(
18
- root_path: root_path,
19
- env: @env,
20
- configs_dir: @configs_dir,
21
- package_dirname: @package_dirname,
22
- packages_lookup_subdir: @packages_lookup_subdir,
23
- loader: @loader,
24
- tasks: @tasks
25
- )
26
- end
27
-
28
- private
29
-
30
- sig do
31
- params(
32
- block: T.proc.params(args: T.anything).returns(T.anything)
33
- )
34
- .void
35
- end
36
- def setup(&block)
37
- instance_exec(@project, &block)
38
-
39
- nil
40
- end
41
-
42
- sig do
43
- params(
44
- value: T.any(Symbol, String, T.proc.returns(T.any(String, Symbol)))
45
- )
46
- .void
47
- end
48
- def set_env(value)
49
- @env = (value.is_a?(Proc) ? value.call : value).to_sym
50
-
51
- raise Mona::Error.new("block or value should be provided") if @env.nil?
52
-
53
- nil
54
- end
55
-
56
- sig do
57
- params(value: String).void
58
- end
59
- def set_configs_dir(value)
60
- @configs_dir = value
61
-
62
- nil
63
- end
64
-
65
- sig do
66
- params(value: String).void
67
- end
68
- def set_package_dirname(value)
69
- @package_dirname = value
70
-
71
- nil
72
- end
73
-
74
- sig do
75
- params(
76
- name: Symbol,
77
- block: T.proc.params(args: T.anything).returns(T.anything)
78
- )
79
- .void
80
- end
81
- def set_loader(name, &block)
82
- @loader = Mona::Loaders::Base.registry.fetch(name).new(@root_path, &block)
83
-
84
- nil
85
- end
86
-
87
- sig do
88
- params(value: String).void
89
- end
90
- def set_packages_lookup_subdir(value)
91
- @packages_lookup_subdir = value
92
-
93
- nil
94
- end
95
-
96
- sig do
97
- params(
98
- name: Symbol,
99
- on: Symbol,
100
- block: T.proc.params(args: T.anything).returns(T.anything),
101
- )
102
- .void
103
- end
104
- def register_task(name, on:, &block)
105
- @tasks.push(
106
- Mona::Task.new(
107
- name: name,
108
- on: on,
109
- block: block
110
- )
111
- )
112
-
113
- nil
114
- end
115
- end
@@ -1,31 +0,0 @@
1
- require 'zeitwerk'
2
- require 'singleton'
3
-
4
- class Mona::Project::FindRoot
5
- include Mona::Mixins
6
-
7
- SYSTEM_ROOT = '/'
8
-
9
- sig do
10
- params(dir: String).returns(String)
11
- end
12
- def call(dir)
13
- init_dir = dir = File.expand_path(dir)
14
-
15
- unless File.exist?(init_dir)
16
- raise StandardError.new("path '#{init_dir}' does not exist")
17
- end
18
-
19
- loop do
20
- project_file_path = File.expand_path(File.join(dir, Mona::PROJECT_FILENAME))
21
-
22
- if File.exist?(project_file_path)
23
- return dir
24
- elsif dir == SYSTEM_ROOT
25
- raise StandardError.new("#{Mona::PROJECT_FILENAME} for path '#{init_dir}' not found")
26
- else
27
- dir = File.expand_path("../", dir)
28
- end
29
- end
30
- end
31
- end
@@ -1,26 +0,0 @@
1
- class Mona::Project::Initializer
2
- include Mona::Mixins
3
-
4
- Inject = Mona::Deps[
5
- "package.indexer",
6
- "project.builder",
7
- "project.find_root"
8
- ]
9
-
10
- include Inject
11
-
12
- sig do
13
- params(dir: String).returns(Mona::Project)
14
- end
15
- def call(dir)
16
- project_root = find_root.call(dir)
17
-
18
- project = builder.call(project_root)
19
-
20
- project_packages = indexer.call(project.packages_lookup_subdir, project.root_path, project.package_dirname)
21
-
22
- project.set_packages(project_packages)
23
-
24
- project
25
- end
26
- end
data/lib/mona/project.rb DELETED
@@ -1,43 +0,0 @@
1
- class Mona::Project
2
- include Mona::Mixins
3
-
4
- attr_reader :env, :configs_dir, :package_dirname, :root_path, :packages, :loader, :tasks, :packages_lookup_subdir
5
-
6
- DEFAULT_OPTIONS = {
7
- configs_dir: 'configs',
8
- package_dirname: 'package',
9
- packages_lookup_subdir: ".",
10
- loader: Mona::Loaders::Base,
11
- tasks: []
12
- }
13
-
14
- sig do
15
- params(
16
- root_path: String,
17
- env: Symbol,
18
- loader: T.nilable(Mona::Loaders::Base),
19
- configs_dir: T.nilable(String),
20
- package_dirname: T.nilable(String),
21
- packages_lookup_subdir: T.nilable(String),
22
- tasks: T.nilable(T::Array[Mona::Task])
23
- )
24
- .void
25
- end
26
- def initialize(root_path:, env:, loader: nil, configs_dir: nil, package_dirname: nil, packages_lookup_subdir: nil, tasks: nil)
27
- @root_path = root_path
28
- @env = env
29
- @packages = []
30
-
31
- @loader = loader || DEFAULT_OPTIONS.fetch(:loader).new(@root_path)
32
- @tasks = tasks || DEFAULT_OPTIONS.fetch(:tasks)
33
- @configs_dir = configs_dir || DEFAULT_OPTIONS.fetch(:configs_dir)
34
- @package_dirname = package_dirname || DEFAULT_OPTIONS.fetch(:package_dirname)
35
- @packages_lookup_subdir = packages_lookup_subdir || DEFAULT_OPTIONS.fetch(:packages_lookup_subdir)
36
-
37
- nil
38
- end
39
-
40
- def set_packages(packages)
41
- @packages = packages
42
- end
43
- end
@@ -1,36 +0,0 @@
1
- class Mona::Task::Manager
2
- include Mona::Mixins
3
-
4
- Inject = Mona::Deps[
5
- packages_repo: "package.repo"
6
- ]
7
-
8
- include Inject
9
-
10
- sig do
11
- params(
12
- method_name: Symbol,
13
- args: T.anything,
14
- kwargs: T.anything
15
- )
16
- .returns(T::Boolean)
17
- end
18
- def method_missing(method_name, *args, **kwargs)
19
- task = Mona.current_project.tasks.detect { _1.name == method_name } || (raise StandardError.new("task '#{method_name}' not found for project"))
20
-
21
- case task.on
22
- when :package
23
- statuses = kwargs[:packages].inject({}) do |result, package_name|
24
- package = packages_repo.find!(package_name.to_sym)
25
- result[package.name] = task.block.call(package, kwargs[:args])
26
- result
27
- end
28
-
29
- failed_packages = statuses.reject {|package_name, exitstatus| exitstatus == 0}
30
-
31
- failed_packages.any? ? false : true
32
- else
33
- raise StandardError.new("unsupported task subject: #{task.on}")
34
- end
35
- end
36
- end
data/lib/mona/task.rb DELETED
@@ -1,21 +0,0 @@
1
- class Mona::Task
2
- include Mona::Mixins
3
-
4
- attr_reader :name, :on, :block
5
-
6
- sig do
7
- params(
8
- name: Symbol,
9
- on: Symbol,
10
- block: T.proc.params(args: T.anything).returns(T.anything),
11
- )
12
- .void
13
- end
14
- def initialize(name:, on:, block:)
15
- @name = name.to_sym
16
- @on = on
17
- @block = block
18
-
19
- nil
20
- end
21
- end
data/lib/mona/version.rb DELETED
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Mona
4
- VERSION = "0.2.3"
5
- end
data/sig/mona.rbs DELETED
@@ -1,4 +0,0 @@
1
- module Mona
2
- VERSION: String
3
- # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
- end