kangaru 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []