kangaru 0.1.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.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +2 -0
  3. data/.rubocop.yml +75 -0
  4. data/.ruby-version +1 -0
  5. data/CHANGELOG.md +5 -0
  6. data/Gemfile +14 -0
  7. data/Gemfile.lock +139 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +49 -0
  10. data/Rakefile +10 -0
  11. data/Steepfile +12 -0
  12. data/kangaru.gemspec +27 -0
  13. data/lib/kangaru/application.rb +73 -0
  14. data/lib/kangaru/argument_parser.rb +34 -0
  15. data/lib/kangaru/components/component.rb +19 -0
  16. data/lib/kangaru/concerns/attributes_concern.rb +31 -0
  17. data/lib/kangaru/concerns/concern.rb +33 -0
  18. data/lib/kangaru/concerns/configurable.rb +22 -0
  19. data/lib/kangaru/concerns/validatable.rb +46 -0
  20. data/lib/kangaru/config.rb +45 -0
  21. data/lib/kangaru/configurators/application_configurator.rb +7 -0
  22. data/lib/kangaru/configurators/configurator.rb +22 -0
  23. data/lib/kangaru/configurators/database_configurator.rb +7 -0
  24. data/lib/kangaru/configurators/external_configurator.rb +6 -0
  25. data/lib/kangaru/configurators/open_configurator.rb +33 -0
  26. data/lib/kangaru/configurators/request_configurator.rb +7 -0
  27. data/lib/kangaru/configurators.rb +12 -0
  28. data/lib/kangaru/controller.rb +53 -0
  29. data/lib/kangaru/database.rb +54 -0
  30. data/lib/kangaru/inflectors/class_inflector.rb +13 -0
  31. data/lib/kangaru/inflectors/constant_inflector.rb +13 -0
  32. data/lib/kangaru/inflectors/constantiser.rb +18 -0
  33. data/lib/kangaru/inflectors/human_inflector.rb +15 -0
  34. data/lib/kangaru/inflectors/inflector.rb +98 -0
  35. data/lib/kangaru/inflectors/inflector_macros.rb +33 -0
  36. data/lib/kangaru/inflectors/path_inflector.rb +21 -0
  37. data/lib/kangaru/inflectors/screaming_snakecase_inflector.rb +11 -0
  38. data/lib/kangaru/inflectors/snakecase_inflector.rb +11 -0
  39. data/lib/kangaru/inflectors/tokeniser.rb +20 -0
  40. data/lib/kangaru/initialiser.rb +12 -0
  41. data/lib/kangaru/initialisers/rspec.rb +11 -0
  42. data/lib/kangaru/injected_methods.rb +23 -0
  43. data/lib/kangaru/model.rb +7 -0
  44. data/lib/kangaru/patches/constantise.rb +11 -0
  45. data/lib/kangaru/patches/inflections.rb +23 -0
  46. data/lib/kangaru/patches/source.rb +11 -0
  47. data/lib/kangaru/patches/symboliser.rb +29 -0
  48. data/lib/kangaru/path_parser.rb +35 -0
  49. data/lib/kangaru/paths.rb +93 -0
  50. data/lib/kangaru/renderer.rb +15 -0
  51. data/lib/kangaru/request.rb +40 -0
  52. data/lib/kangaru/request_builder.rb +33 -0
  53. data/lib/kangaru/router.rb +38 -0
  54. data/lib/kangaru/validation/attribute_validator.rb +32 -0
  55. data/lib/kangaru/validation/error.rb +32 -0
  56. data/lib/kangaru/validators/required_validator.rb +17 -0
  57. data/lib/kangaru/validators/validator.rb +22 -0
  58. data/lib/kangaru/version.rb +3 -0
  59. data/lib/kangaru.rb +58 -0
  60. data/rbs_collection.lock.yaml +83 -0
  61. data/rbs_collection.yaml +24 -0
  62. metadata +173 -0
@@ -0,0 +1,93 @@
1
+ # Builds Pathname objects for files inside a Ruby gem application. Note that
2
+ # this assumes a conventional Ruby gem file structure.
3
+ #
4
+ # For example:
5
+ # + some_gem/ (gem_dir)
6
+ # | + lib/ (lib_dir)
7
+ # | |-- some_gem.rb (source)
8
+ # | | + some_gem/ (app_dir)
9
+ # | | |-- some_file.rb (path)
10
+ #
11
+ module Kangaru
12
+ class Paths
13
+ attr_reader :source
14
+
15
+ def initialize(source:)
16
+ @source = Pathname.new(source)
17
+ end
18
+
19
+ # The directory that contains the gem.
20
+ def dir
21
+ @dir ||= gem_path.dirname
22
+ end
23
+
24
+ # Infers the gem name from the source filename.
25
+ def name
26
+ @name ||= source.basename(".rb").to_s
27
+ end
28
+
29
+ # Paths inside the root directory of the gem.
30
+ def gem_path(*fragments, ext: nil)
31
+ build_path(*fragments, dir: gem_dir, ext:)
32
+ end
33
+
34
+ # Paths inside the lib path of the gem. Conventionally contains two files:
35
+ # 1. gem_name.rb (source_file)
36
+ # 2. gem_name/*.rb (app_path)
37
+ def lib_path(*fragments, ext: :rb)
38
+ build_path(*fragments, dir: lib_dir, ext:)
39
+ end
40
+
41
+ # The gem application directory (gem/lib/gem_name).
42
+ def path(*fragments, ext: :rb)
43
+ build_path(*fragments, dir: app_dir, ext:)
44
+ end
45
+
46
+ def view_path(*fragments, ext: :erb)
47
+ build_path(*fragments, dir: views_dir, ext:)
48
+ end
49
+
50
+ def collapsed_dirs
51
+ [models_dir, controllers_dir].map(&:to_s)
52
+ end
53
+
54
+ private
55
+
56
+ MODELS_DIRNAME = "models".freeze
57
+ VIEWS_DIRNAME = "views".freeze
58
+ CONTROLLERS_DIRNAME = "controllers".freeze
59
+
60
+ def gem_dir
61
+ @gem_dir ||= lib_dir.dirname
62
+ end
63
+
64
+ def lib_dir
65
+ @lib_dir ||= source.dirname
66
+ end
67
+
68
+ def app_dir
69
+ @app_dir ||= lib_dir.join(name)
70
+ end
71
+
72
+ def models_dir
73
+ @models_dir ||= app_dir.join(MODELS_DIRNAME)
74
+ end
75
+
76
+ def views_dir
77
+ @views_dir ||= app_dir.join(VIEWS_DIRNAME)
78
+ end
79
+
80
+ def controllers_dir
81
+ @controllers_dir ||= app_dir.join(CONTROLLERS_DIRNAME)
82
+ end
83
+
84
+ def build_path(*fragments, dir:, ext:)
85
+ return dir if fragments.empty?
86
+
87
+ ext = ".#{ext}" if ext
88
+ file = "#{fragments.pop}#{ext}"
89
+
90
+ dir.join(*fragments, file)
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,15 @@
1
+ module Kangaru
2
+ class Renderer
3
+ attr_reader :path
4
+
5
+ def initialize(path)
6
+ @path = path
7
+ end
8
+
9
+ def render(binding)
10
+ return unless path.exist?
11
+
12
+ ERB.new(path.read, trim_mode: "-").run(binding)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,40 @@
1
+ module Kangaru
2
+ class Request
3
+ using Patches::Inflections
4
+
5
+ include Concerns::Configurable
6
+
7
+ DEFAULT_CONTROLLER = "DefaultController".freeze
8
+
9
+ DEFAULT_ACTION = :default
10
+
11
+ attr_reader :path, :params
12
+
13
+ def initialize(path:, params:)
14
+ @path = path
15
+ @params = params
16
+ end
17
+
18
+ def controller
19
+ return default_controller if path_parser.controller.nil?
20
+
21
+ path_parser.controller&.to_class_name&.concat(Controller::SUFFIX) || raise
22
+ end
23
+
24
+ def action
25
+ path_parser.action || DEFAULT_ACTION
26
+ end
27
+
28
+ private
29
+
30
+ def path_parser
31
+ @path_parser ||= PathParser.new(path)
32
+ end
33
+
34
+ def default_controller
35
+ return DEFAULT_CONTROLLER if config.default_controller.nil?
36
+
37
+ config.default_controller.to_s
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,33 @@
1
+ module Kangaru
2
+ class RequestBuilder
3
+ ARGUMENT_TOKEN = /^--?/
4
+
5
+ attr_reader :tokens
6
+
7
+ def initialize(tokens)
8
+ @tokens = tokens
9
+ end
10
+
11
+ def build
12
+ Request.new(path:, params:)
13
+ end
14
+
15
+ private
16
+
17
+ def path
18
+ File.join(*path_tokens).prepend("/")
19
+ end
20
+
21
+ def params
22
+ ArgumentParser.new(*param_tokens).parse
23
+ end
24
+
25
+ def path_tokens
26
+ tokens.take_while { |token| !token.match?(ARGUMENT_TOKEN) }
27
+ end
28
+
29
+ def param_tokens
30
+ tokens.drop_while { |token| !token.match?(ARGUMENT_TOKEN) }
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,38 @@
1
+ module Kangaru
2
+ class Router
3
+ using Patches::Inflections
4
+ using Patches::Constantise
5
+
6
+ attr_reader :request, :namespace
7
+
8
+ def initialize(request, namespace: Object)
9
+ @request = request
10
+ @namespace = namespace
11
+
12
+ validate_controller_defined!
13
+ validate_action_defined!
14
+ end
15
+
16
+ def resolve
17
+ controller_class.new(request).execute
18
+ end
19
+
20
+ private
21
+
22
+ def controller_class
23
+ @controller_class ||= request.controller.constantise(root: namespace)
24
+ end
25
+
26
+ def validate_controller_defined!
27
+ return if namespace.const_defined?(request.controller)
28
+
29
+ raise "#{request.controller} is not defined in #{namespace}"
30
+ end
31
+
32
+ def validate_action_defined!
33
+ return if controller_class.instance_methods.include?(request.action)
34
+
35
+ raise "#{request.action} is not defined by #{request.controller}"
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,32 @@
1
+ module Kangaru
2
+ module Validation
3
+ class AttributeValidator
4
+ using Patches::Inflections
5
+
6
+ attr_reader :model, :attribute
7
+
8
+ def initialize(model:, attribute:)
9
+ @model = model
10
+ @attribute = attribute
11
+ end
12
+
13
+ def validate(validator, **params)
14
+ load_validator(validator:, params:).validate
15
+ end
16
+
17
+ private
18
+
19
+ def validator_name(validator)
20
+ "#{validator.to_s.to_class_name}Validator"
21
+ end
22
+
23
+ def load_validator(validator:, params:)
24
+ name = validator_name(validator)
25
+
26
+ raise "#{name} is not defined" unless Validators.const_defined?(name)
27
+
28
+ Validators.const_get(name).new(model:, attribute:, params:)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,32 @@
1
+ module Kangaru
2
+ module Validation
3
+ class Error
4
+ using Patches::Inflections
5
+
6
+ MESSAGES = {
7
+ blank: "can't be blank"
8
+ }.freeze
9
+
10
+ attr_reader :attribute, :type
11
+
12
+ def initialize(attribute:, type:)
13
+ @attribute = attribute
14
+ @type = type
15
+ end
16
+
17
+ def full_message
18
+ [human_attribute, message].join(" ")
19
+ end
20
+
21
+ private
22
+
23
+ def human_attribute
24
+ attribute.to_s.to_humanised
25
+ end
26
+
27
+ def message
28
+ MESSAGES[type] || raise("invalid message type")
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,17 @@
1
+ module Kangaru
2
+ module Validators
3
+ class RequiredValidator < Validator
4
+ def validate
5
+ return unless value_missing?
6
+
7
+ add_error!(:blank)
8
+ end
9
+
10
+ private
11
+
12
+ def value_missing?
13
+ value.nil? || value == "" || value == false
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ module Kangaru
2
+ module Validators
3
+ class Validator
4
+ attr_reader :model, :attribute, :params, :value
5
+
6
+ def initialize(model:, attribute:, params:)
7
+ @model = model
8
+ @attribute = attribute
9
+ @params = params
10
+ @value = model.public_send(attribute)
11
+ end
12
+
13
+ def validate
14
+ raise NotImplementedError
15
+ end
16
+
17
+ def add_error!(type)
18
+ model.errors << Validation::Error.new(attribute:, type:)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,3 @@
1
+ module Kangaru
2
+ VERSION = "0.1.0".freeze
3
+ end
data/lib/kangaru.rb ADDED
@@ -0,0 +1,58 @@
1
+ require "zeitwerk"
2
+
3
+ require "colorize"
4
+ require "erb"
5
+ require "fileutils"
6
+ require "forwardable"
7
+ require "pathname"
8
+ require "sequel"
9
+ require "sqlite3"
10
+ require "yaml"
11
+
12
+ module Kangaru
13
+ COLLAPSED_DIRS = [
14
+ "#{__dir__}/kangaru/components"
15
+ ].freeze
16
+
17
+ DEFAULT_ENV = :runtime
18
+
19
+ INFLECTIONS = {
20
+ "rspec" => "RSpec"
21
+ }.freeze
22
+
23
+ @loader = Zeitwerk::Loader.for_gem(
24
+ warn_on_extra_files: false
25
+ ).tap do |loader|
26
+ loader.inflector.inflect(INFLECTIONS)
27
+ loader.collapse(COLLAPSED_DIRS)
28
+ loader.setup
29
+ end
30
+
31
+ class << self
32
+ attr_writer :application
33
+
34
+ def application
35
+ @application || raise("application not set")
36
+ end
37
+
38
+ def env=(value)
39
+ @env = value.to_sym
40
+ end
41
+
42
+ def env
43
+ @env ||= DEFAULT_ENV
44
+ end
45
+
46
+ def env?(value)
47
+ env == value
48
+ end
49
+
50
+ def eager_load(namespace)
51
+ @loader.eager_load_namespace(namespace)
52
+ end
53
+
54
+ def test?
55
+ env == :test
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,83 @@
1
+ ---
2
+ sources:
3
+ - type: git
4
+ name: apexatoll/gem_signatures
5
+ revision: '086b4786ad2ada720a3ed3498ab99218e5bfc085'
6
+ remote: https://github.com/apexatoll/gem_signatures
7
+ repo_dir: sig
8
+ - type: git
9
+ name: ruby/gem_rbs_collection
10
+ revision: 2de2d4535caba275f3b8533684aab110d921f553
11
+ remote: https://github.com/ruby/gem_rbs_collection.git
12
+ repo_dir: gems
13
+ path: ".gems"
14
+ gems:
15
+ - name: bigdecimal
16
+ version: '0'
17
+ source:
18
+ type: stdlib
19
+ - name: cgi
20
+ version: '0'
21
+ source:
22
+ type: stdlib
23
+ - name: colorize
24
+ version: '1.1'
25
+ source:
26
+ type: git
27
+ name: apexatoll/gem_signatures
28
+ revision: '086b4786ad2ada720a3ed3498ab99218e5bfc085'
29
+ remote: https://github.com/apexatoll/gem_signatures
30
+ repo_dir: sig
31
+ - name: dbm
32
+ version: '0'
33
+ source:
34
+ type: stdlib
35
+ - name: erb
36
+ version: '0'
37
+ source:
38
+ type: stdlib
39
+ - name: fileutils
40
+ version: '0'
41
+ source:
42
+ type: stdlib
43
+ - name: forwardable
44
+ version: '0'
45
+ source:
46
+ type: stdlib
47
+ - name: pathname
48
+ version: '0'
49
+ source:
50
+ type: stdlib
51
+ - name: pstore
52
+ version: '0'
53
+ source:
54
+ type: stdlib
55
+ - name: sequel
56
+ version: '5.72'
57
+ source:
58
+ type: git
59
+ name: apexatoll/gem_signatures
60
+ revision: '086b4786ad2ada720a3ed3498ab99218e5bfc085'
61
+ remote: https://github.com/apexatoll/gem_signatures
62
+ repo_dir: sig
63
+ - name: tempfile
64
+ version: '0'
65
+ source:
66
+ type: stdlib
67
+ - name: yaml
68
+ version: '0'
69
+ source:
70
+ type: stdlib
71
+ - name: zeitwerk
72
+ version: '2.6'
73
+ source:
74
+ type: git
75
+ name: apexatoll/gem_signatures
76
+ revision: '086b4786ad2ada720a3ed3498ab99218e5bfc085'
77
+ remote: https://github.com/apexatoll/gem_signatures
78
+ repo_dir: sig
79
+ - name: zirconia
80
+ version: 0.1.2
81
+ source:
82
+ type: rubygems
83
+ gemfile_lock_path: Gemfile.lock
@@ -0,0 +1,24 @@
1
+ sources:
2
+ - type: git
3
+ name: ruby/gem_rbs_collection
4
+ remote: https://github.com/ruby/gem_rbs_collection.git
5
+ revision: main
6
+ repo_dir: gems
7
+
8
+ - type: git
9
+ name: apexatoll/gem_signatures
10
+ remote: https://github.com/apexatoll/gem_signatures
11
+ revision: main
12
+ repo_dir: sig
13
+
14
+ path: .gems
15
+
16
+ gems:
17
+ - name: rake
18
+ ignore: true
19
+ - name: rspec
20
+ ignore: true
21
+ - name: rubocop
22
+ ignore: true
23
+ - name: steep
24
+ ignore: true
metadata ADDED
@@ -0,0 +1,173 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kangaru
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Chris Welham
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-11-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: colorize
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: erb
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sequel
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.72'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.72'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sqlite3
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.6'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.6'
69
+ - !ruby/object:Gem::Dependency
70
+ name: zeitwerk
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.6'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.6'
83
+ description:
84
+ email:
85
+ - 71787007+apexatoll@users.noreply.github.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".rspec"
91
+ - ".rubocop.yml"
92
+ - ".ruby-version"
93
+ - CHANGELOG.md
94
+ - Gemfile
95
+ - Gemfile.lock
96
+ - LICENSE.txt
97
+ - README.md
98
+ - Rakefile
99
+ - Steepfile
100
+ - kangaru.gemspec
101
+ - lib/kangaru.rb
102
+ - lib/kangaru/application.rb
103
+ - lib/kangaru/argument_parser.rb
104
+ - lib/kangaru/components/component.rb
105
+ - lib/kangaru/concerns/attributes_concern.rb
106
+ - lib/kangaru/concerns/concern.rb
107
+ - lib/kangaru/concerns/configurable.rb
108
+ - lib/kangaru/concerns/validatable.rb
109
+ - lib/kangaru/config.rb
110
+ - lib/kangaru/configurators.rb
111
+ - lib/kangaru/configurators/application_configurator.rb
112
+ - lib/kangaru/configurators/configurator.rb
113
+ - lib/kangaru/configurators/database_configurator.rb
114
+ - lib/kangaru/configurators/external_configurator.rb
115
+ - lib/kangaru/configurators/open_configurator.rb
116
+ - lib/kangaru/configurators/request_configurator.rb
117
+ - lib/kangaru/controller.rb
118
+ - lib/kangaru/database.rb
119
+ - lib/kangaru/inflectors/class_inflector.rb
120
+ - lib/kangaru/inflectors/constant_inflector.rb
121
+ - lib/kangaru/inflectors/constantiser.rb
122
+ - lib/kangaru/inflectors/human_inflector.rb
123
+ - lib/kangaru/inflectors/inflector.rb
124
+ - lib/kangaru/inflectors/inflector_macros.rb
125
+ - lib/kangaru/inflectors/path_inflector.rb
126
+ - lib/kangaru/inflectors/screaming_snakecase_inflector.rb
127
+ - lib/kangaru/inflectors/snakecase_inflector.rb
128
+ - lib/kangaru/inflectors/tokeniser.rb
129
+ - lib/kangaru/initialiser.rb
130
+ - lib/kangaru/initialisers/rspec.rb
131
+ - lib/kangaru/injected_methods.rb
132
+ - lib/kangaru/model.rb
133
+ - lib/kangaru/patches/constantise.rb
134
+ - lib/kangaru/patches/inflections.rb
135
+ - lib/kangaru/patches/source.rb
136
+ - lib/kangaru/patches/symboliser.rb
137
+ - lib/kangaru/path_parser.rb
138
+ - lib/kangaru/paths.rb
139
+ - lib/kangaru/renderer.rb
140
+ - lib/kangaru/request.rb
141
+ - lib/kangaru/request_builder.rb
142
+ - lib/kangaru/router.rb
143
+ - lib/kangaru/validation/attribute_validator.rb
144
+ - lib/kangaru/validation/error.rb
145
+ - lib/kangaru/validators/required_validator.rb
146
+ - lib/kangaru/validators/validator.rb
147
+ - lib/kangaru/version.rb
148
+ - rbs_collection.lock.yaml
149
+ - rbs_collection.yaml
150
+ homepage:
151
+ licenses:
152
+ - MIT
153
+ metadata: {}
154
+ post_install_message:
155
+ rdoc_options: []
156
+ require_paths:
157
+ - lib
158
+ required_ruby_version: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ version: 3.2.0
163
+ required_rubygems_version: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - ">="
166
+ - !ruby/object:Gem::Version
167
+ version: '0'
168
+ requirements: []
169
+ rubygems_version: 3.4.1
170
+ signing_key:
171
+ specification_version: 4
172
+ summary: A lightweight framework for building command line interfaces
173
+ test_files: []