belt 0.1.12 → 0.1.13

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 892f04b0175a10793ba63f3cf65182b3d3e380ab75446984004283b8b8cf7377
4
- data.tar.gz: 701534fd954f5d444783d3adb085d0582c4f9d1b92bf4518249e677add64c1b7
3
+ metadata.gz: b3834960aad8c883c22d15acec6a320d860bd8b90acb3c7c74751c59ce49b959
4
+ data.tar.gz: 29f96098a7ac79c0f3eb62edd9950a9d1e95e2f56b219b3fcd024b19f3b4daac
5
5
  SHA512:
6
- metadata.gz: '0682c1d964357b0109baca143e7c43fb8688a375aa84af0ba3ba2c46fbf0d56932a011b8eef6a0027dcf6b289f3eb6bc4617bcecd8717812246133b107563233'
7
- data.tar.gz: 68d1e8b3a4311ca6bd3be6dbfeb48594efeb4afaf42500d27b7076e0068a8b21c8cd73a1e41a70c4ee6338e910dab96aaa8fdba09683c922027574141049a899
6
+ metadata.gz: 597c85007b7e8838f77605ec31de2b20bac85bf29ec20de371b85f03dd4a9eb20abba6be12f5b8f701e2c36ee02377b3488efa00292060f36b8bc956ef04d8ed
7
+ data.tar.gz: 4c24a2f2c554244010ec246bc337194f6616ed04394ff8aeea4d5aabe91c97adeac7aaad76fbfa9a812fa35397fc46770c347ee590a5d307e2073ee2e811ff13
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.13
4
+
5
+ ### Generator Extension API
6
+
7
+ - Gems can now register generators by placing a file at `lib/belt/generators/<name>_generator.rb`.
8
+ - Discovered automatically via `Gem.loaded_specs` — no manual registration needed.
9
+ - Available as `belt generate <name>` / `belt destroy <name>`.
10
+ - Generator contract: `.run(args)` (required), `.destroy(args)` (optional), `.description` (optional).
11
+ - Help output includes gem-provided generators under "Gem Generators" section.
12
+
3
13
  ## Unreleased
4
14
 
5
15
  ## 0.1.11
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'fileutils'
4
4
  require_relative 'app_detection'
5
+ require_relative 'generator_registry'
5
6
  require_relative 'tables_command'
6
7
  require_relative '../inflector'
7
8
 
@@ -20,13 +21,22 @@ module Belt
20
21
  exit 0
21
22
  end
22
23
 
23
- unless GENERATORS.include?(generator)
24
+ unless GENERATORS.include?(generator) || GeneratorRegistry.generator_names.include?(generator)
24
25
  puts "Unknown generator: '#{generator}'"
25
- puts "Available: #{GENERATORS.uniq.join(', ')}"
26
+ puts "Available: #{(GENERATORS + GeneratorRegistry.generator_names).uniq.join(', ')}"
26
27
  puts "\nRun 'belt destroy --help' for usage information."
27
28
  exit 1
28
29
  end
29
30
 
31
+ # Delegate to gem-provided generator if not a built-in
32
+ unless GENERATORS.include?(generator)
33
+ klass = GeneratorRegistry.find(generator)
34
+ return klass.destroy(args) if klass.respond_to?(:destroy)
35
+
36
+ puts "Generator '#{generator}' does not support destroy."
37
+ exit 1
38
+ end
39
+
30
40
  # Normalize: resource is an alias for scaffold
31
41
  generator = 'scaffold' if generator == 'resource'
32
42
 
@@ -181,7 +181,7 @@ module Belt
181
181
  return str if str.empty?
182
182
  return str unless str.match?(/[\s\#"'\\$`]/)
183
183
 
184
- %("#{str.gsub('\\', '\\\\').gsub('"', '\\"')}")
184
+ %("#{str.gsub('\\') { '\\\\' }.gsub('"') { '\\"' }}")
185
185
  end
186
186
  end
187
187
  end
@@ -7,6 +7,7 @@ require_relative 'environment_command'
7
7
  require_relative 'frontend_command'
8
8
  require_relative 'tables_command'
9
9
  require_relative 'views_command'
10
+ require_relative 'generator_registry'
10
11
  require_relative '../inflector'
11
12
 
12
13
  module Belt
@@ -84,13 +85,19 @@ module Belt
84
85
  exit 0
85
86
  end
86
87
 
87
- unless GENERATORS.include?(generator)
88
+ unless GENERATORS.include?(generator) || GeneratorRegistry.generator_names.include?(generator)
88
89
  puts "Unknown generator: '#{generator}'"
89
- puts "Available generators: #{(GENERATORS - ['resource']).join(', ')}"
90
+ puts "Available generators: #{all_generator_names.join(', ')}"
90
91
  puts "\nRun 'belt generate --help' for usage information."
91
92
  exit 1
92
93
  end
93
94
 
95
+ # Delegate to gem-provided generator if not a built-in
96
+ unless GENERATORS.include?(generator)
97
+ klass = GeneratorRegistry.find(generator)
98
+ return klass.run(args)
99
+ end
100
+
94
101
  # Normalize: resource is an alias for scaffold
95
102
  generator = 'scaffold' if generator == 'resource'
96
103
 
@@ -131,6 +138,10 @@ module Belt
131
138
  belt setup environment frontend views routes
132
139
  ].freeze
133
140
 
141
+ def self.all_generator_names
142
+ ((GENERATORS - ['resource']) + GeneratorRegistry.generator_names).uniq
143
+ end
144
+
134
145
  def self.validate_resource_name!(name, generator)
135
146
  errors = []
136
147
 
@@ -152,6 +163,8 @@ module Belt
152
163
  end
153
164
 
154
165
  def self.print_generate_help
166
+ gem_generators = GeneratorRegistry.discovered_generators
167
+
155
168
  puts <<~HELP
156
169
  Usage: belt generate <generator> <name> [field:type ...] [options]
157
170
  belt g <generator> <name> [field:type ...] [options]
@@ -167,6 +180,18 @@ module Belt
167
180
  Aliases:
168
181
  resource Same as scaffold
169
182
 
183
+ HELP
184
+
185
+ if gem_generators.any?
186
+ puts ' Gem Generators:'
187
+ gem_generators.each do |name, klass|
188
+ desc = klass.respond_to?(:description) ? klass.description : "Generate #{name} (from gem)"
189
+ puts format(' %<name>-14s %<desc>s', name: name, desc: desc)
190
+ end
191
+ puts
192
+ end
193
+
194
+ puts <<~HELP
170
195
  Field Types:
171
196
  #{FIELD_TYPES.join(', ')}
172
197
  (defaults to string if omitted)
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Belt
4
+ module CLI
5
+ # Discovers and manages generators provided by external gems.
6
+ #
7
+ # Gems register generators by placing a file at:
8
+ # lib/belt/generators/<name>_generator.rb
9
+ #
10
+ # Each generator class must:
11
+ # - Be named <Name>Generator (e.g., MessagingGenerator)
12
+ # - Live in the Belt::Generators module
13
+ # - Implement .run(args) for generation
14
+ # - Implement .destroy(args) for teardown (optional)
15
+ # - Implement .description for help text (optional)
16
+ #
17
+ # Example gem structure:
18
+ # belt-messaging/
19
+ # lib/
20
+ # belt/
21
+ # generators/
22
+ # messaging_generator.rb
23
+ #
24
+ # The generator is then available as:
25
+ # belt generate messaging
26
+ # belt destroy messaging
27
+ #
28
+ module GeneratorRegistry
29
+ GENERATOR_PATH = 'lib/belt/generators'
30
+
31
+ class << self
32
+ # Returns a hash of { "name" => generator_class } for all discovered generators.
33
+ def discovered_generators
34
+ @discovered_generators ||= discover_all
35
+ end
36
+
37
+ # Returns just the names of discovered generators.
38
+ def generator_names
39
+ discovered_generators.keys
40
+ end
41
+
42
+ # Loads and returns the generator class for a given name.
43
+ # Returns nil if not found.
44
+ def find(name)
45
+ discovered_generators[name]
46
+ end
47
+
48
+ # Reset cache (useful in tests)
49
+ def reset!
50
+ @discovered_generators = nil
51
+ end
52
+
53
+ private
54
+
55
+ def discover_all
56
+ generators = {}
57
+
58
+ Gem.loaded_specs.each_value do |spec|
59
+ generators_dir = File.join(spec.gem_dir, GENERATOR_PATH)
60
+ next unless File.directory?(generators_dir)
61
+
62
+ Dir.glob(File.join(generators_dir, '*_generator.rb')).each do |file|
63
+ name = File.basename(file, '.rb').sub(/_generator\z/, '')
64
+ klass = load_generator_class(file, name)
65
+ generators[name] = klass if klass
66
+ end
67
+ end
68
+
69
+ generators
70
+ end
71
+
72
+ def load_generator_class(file, name)
73
+ require file
74
+
75
+ class_name = "#{classify(name)}Generator"
76
+ Belt::Generators.const_get(class_name)
77
+ rescue LoadError, NameError => e
78
+ warn "[Belt] Warning: Failed to load generator '#{name}' from #{file}: #{e.message}"
79
+ nil
80
+ end
81
+
82
+ def classify(name)
83
+ name.split('_').map(&:capitalize).join
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ # Namespace for generator classes provided by external gems
90
+ module Generators
91
+ end
92
+ end
data/lib/belt/cli.rb CHANGED
@@ -56,7 +56,7 @@ module Belt
56
56
  # belt destroy --help → DestroyCommand help
57
57
  # belt destroy (no args) → terraform destroy (needs env)
58
58
  return DestroyCommand.run(args) if args.include?('--help') || args.include?('-h')
59
- elsif DestroyCommand::GENERATORS.include?(args.first)
59
+ elsif DestroyCommand::GENERATORS.include?(args.first) || GeneratorRegistry.generator_names.include?(args.first)
60
60
  return DestroyCommand.run(args)
61
61
  end
62
62
  end
data/lib/belt/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Belt
4
- VERSION = '0.1.12'
4
+ VERSION = '0.1.13'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: belt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.12
4
+ version: 0.1.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stowzilla
@@ -82,6 +82,7 @@ files:
82
82
  - lib/belt/cli/frontend_env_map.rb
83
83
  - lib/belt/cli/frontend_setup_command.rb
84
84
  - lib/belt/cli/generate_command.rb
85
+ - lib/belt/cli/generator_registry.rb
85
86
  - lib/belt/cli/new_command.rb
86
87
  - lib/belt/cli/routes_command.rb
87
88
  - lib/belt/cli/routes_command/route_inference.rb