thundercli 0.2.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 (69) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +5 -0
  3. data/exe/thunder +11 -0
  4. data/lib/thunder/application.rb +35 -0
  5. data/lib/thunder/cli/command.rb +19 -0
  6. data/lib/thunder/cli/commands/console.rb +24 -0
  7. data/lib/thunder/cli/commands/db/create.rb +20 -0
  8. data/lib/thunder/cli/commands/db/drop.rb +20 -0
  9. data/lib/thunder/cli/commands/db/migrate.rb +20 -0
  10. data/lib/thunder/cli/commands/db/reset.rb +22 -0
  11. data/lib/thunder/cli/commands/db/setup.rb +21 -0
  12. data/lib/thunder/cli/commands/generate/chunk.rb +23 -0
  13. data/lib/thunder/cli/commands/generate/command.rb +64 -0
  14. data/lib/thunder/cli/commands/generate/event.rb +28 -0
  15. data/lib/thunder/cli/commands/generate/model.rb +39 -0
  16. data/lib/thunder/cli/commands/generate/operation.rb +39 -0
  17. data/lib/thunder/cli/commands/generate/outbox.rb +33 -0
  18. data/lib/thunder/cli/commands/generate/subscriber.rb +33 -0
  19. data/lib/thunder/cli/commands/messaging/consumer.rb +52 -0
  20. data/lib/thunder/cli/commands/messaging/outbox.rb +36 -0
  21. data/lib/thunder/cli/commands/new.rb +44 -0
  22. data/lib/thunder/cli/commands/serve.rb +38 -0
  23. data/lib/thunder/cli/commands/spec.rb +77 -0
  24. data/lib/thunder/cli/commands/version.rb +16 -0
  25. data/lib/thunder/cli.rb +78 -0
  26. data/lib/thunder/console/backtrace.rb +36 -0
  27. data/lib/thunder/console/banner.rb +93 -0
  28. data/lib/thunder/console/completion.rb +68 -0
  29. data/lib/thunder/console/constants.rb +57 -0
  30. data/lib/thunder/console/evaluation.rb +72 -0
  31. data/lib/thunder/console/help.rb +30 -0
  32. data/lib/thunder/console/helpers.rb +16 -0
  33. data/lib/thunder/console/inspector.rb +114 -0
  34. data/lib/thunder/console/pretty.rb +70 -0
  35. data/lib/thunder/console/prompt.rb +52 -0
  36. data/lib/thunder/console/theme.rb +85 -0
  37. data/lib/thunder/console/views/collection.rb +172 -0
  38. data/lib/thunder/console/views/format.rb +114 -0
  39. data/lib/thunder/console/views/layout.rb +72 -0
  40. data/lib/thunder/console/views/model.rb +92 -0
  41. data/lib/thunder/console/views/operation.rb +112 -0
  42. data/lib/thunder/console/views/record.rb +95 -0
  43. data/lib/thunder/console/views.rb +45 -0
  44. data/lib/thunder/console.rb +167 -0
  45. data/lib/thunder/generator.rb +73 -0
  46. data/lib/thunder/templates/chunk/app/common/models/application_model.rb.erb +4 -0
  47. data/lib/thunder/templates/chunk/app/common/operations/external_operation.rb.erb +3 -0
  48. data/lib/thunder/templates/chunk/app/common/operations/internal_operation.rb.erb +3 -0
  49. data/lib/thunder/templates/chunk/app/external/operations/_keep.erb +0 -0
  50. data/lib/thunder/templates/chunk/app/internal/models/_keep.erb +0 -0
  51. data/lib/thunder/templates/chunk/app/internal/operations/_keep.erb +0 -0
  52. data/lib/thunder/templates/chunk/config/chunk.rb.erb +9 -0
  53. data/lib/thunder/templates/chunk/database/migrate/_keep.erb +0 -0
  54. data/lib/thunder/templates/event/event.rb.erb +6 -0
  55. data/lib/thunder/templates/model/migration.rb.erb +17 -0
  56. data/lib/thunder/templates/model/model.rb.erb +6 -0
  57. data/lib/thunder/templates/new/Gemfile.erb +21 -0
  58. data/lib/thunder/templates/new/README.md.erb +8 -0
  59. data/lib/thunder/templates/new/_gitignore.erb +4 -0
  60. data/lib/thunder/templates/new/_ruby-version.erb +2 -0
  61. data/lib/thunder/templates/new/bin/thunder.erb +6 -0
  62. data/lib/thunder/templates/new/chunks/_keep.erb +0 -0
  63. data/lib/thunder/templates/operation/external.rb.erb +24 -0
  64. data/lib/thunder/templates/operation/internal.rb.erb +26 -0
  65. data/lib/thunder/templates/outbox/migration.rb.erb +21 -0
  66. data/lib/thunder/templates/subscriber/subscriber.rb.erb +18 -0
  67. data/lib/thunder/version.rb +5 -0
  68. data/lib/thunder.rb +32 -0
  69. metadata +147 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ee0b6c7018e2162267ecfad4743b3974594c4ebd38d489f4f659b354258eb9e6
4
+ data.tar.gz: fbb9fc8aba354637e6a138fc3e23211491cfecce6bd2e69891702ecbd09a6349
5
+ SHA512:
6
+ metadata.gz: 287d0ffb7f45c51f4490cb5a1cc0bc4394317f547b97e933fd630b6bc10d0825c58fc8e8fbb0fe3eb40ddc18b395c91794f45b40b65d090f653156c027a03b91
7
+ data.tar.gz: 9d37e7f243dd8417bcd0c63f1a5ca87262e5c2907ce83a0bf398a567a8c0693fbcbc5d368fdf246466da32ac637b6a89af5e6ba69f67dd63d3bb1641ca52986e
data/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # thunder
2
+
3
+ The application and entry point of every repo. Finds the chunks (`chunks/<name>/`), reads the aggregate config
4
+ (`infra`, `connections`), boots and supervises the chunk processes and provides the CLI. Depends on `thunderchunk`
5
+ and the gems below it.
data/exe/thunder ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'thunder'
5
+
6
+ begin
7
+ Dry::CLI.new(Thunder::CLI).call(arguments: Thunder::CLI.expand(ARGV))
8
+ rescue Thunder::Error, ThunderChunk::Error => e
9
+ warn e.message
10
+ exit 1
11
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thunder
4
+ class Application
5
+ attr_reader :root
6
+
7
+ def initialize(root:)
8
+ @root = Pathname(root)
9
+
10
+ load
11
+ end
12
+
13
+ def load
14
+ @chunks = root.join('chunks').children
15
+ .select { |dir| dir.join('config/chunk.rb').file? }
16
+ .to_h do |dir|
17
+ chunk = ThunderChunk::Chunk.new(root_path: dir)
18
+
19
+ [chunk.identifier.to_sym, chunk]
20
+ end
21
+ end
22
+
23
+ def chunks
24
+ @chunks&.values || []
25
+ end
26
+
27
+ def chunk(identifier)
28
+ @chunks[identifier.to_sym] || raise(Error, "unknown chunk '#{identifier}'; chunks: #{@chunks.keys.join(', ')}")
29
+ end
30
+
31
+ def inspect
32
+ "#<#{self.class.name} root=#{root} chunks=#{@chunks&.keys.inspect}>"
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thunder
4
+ module CLI
5
+ class Command < Dry::CLI::Command
6
+ option :env, default: 'development', desc: 'THUNDER_ENV'
7
+
8
+ private
9
+
10
+ def default_chunk
11
+ cwd = Pathname(Dir.pwd).expand_path
12
+ inside = Thunder.application.chunks.find { |chunk| cwd.ascend.include?(chunk.root_path.expand_path) }
13
+ return inside.identifier if inside
14
+
15
+ Thunder.application.chunks.first.identifier if Thunder.application.chunks.one?
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thunder
4
+ module CLI
5
+ module Commands
6
+ class Console < Command
7
+ desc 'Boot a chunk and open IRB with it; chunk :name switches to another chunk'
8
+ option :chunk, aliases: ['-c'], desc: 'Chunk name'
9
+
10
+ def call(env:, chunk: nil, **)
11
+ require 'thunder/console'
12
+ # WARN: AI MADE
13
+ require 'thunderwire'
14
+
15
+ ThunderChunk::SenderDock.resolver = ThunderWire.method(:sender)
16
+ ThunderChunk::ExternalImporters::Events.resolver = ThunderWire.method(:publisher)
17
+ # WARN: ---
18
+
19
+ Thunder::Console.new(Thunder.application, chunk || default_chunk, env:).start
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thunder
4
+ module CLI
5
+ module Commands
6
+ module Db
7
+ class Create < Command
8
+ desc 'Create the database'
9
+
10
+ def call(**)
11
+ Thunder.application.chunks.each do |chunk|
12
+ chunk.boot
13
+ chunk.database.create!
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thunder
4
+ module CLI
5
+ module Commands
6
+ module Db
7
+ class Drop < Command
8
+ desc 'Drop the databases'
9
+
10
+ def call(**)
11
+ Thunder.application.chunks.each do |chunk|
12
+ chunk.boot
13
+ chunk.database.drop!
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thunder
4
+ module CLI
5
+ module Commands
6
+ module Db
7
+ class Migrate < Command
8
+ desc 'Migrate the databases'
9
+
10
+ def call(**)
11
+ Thunder.application.chunks.each do |chunk|
12
+ chunk.boot
13
+ chunk.database.migrate!
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thunder
4
+ module CLI
5
+ module Commands
6
+ module Db
7
+ class Reset < Command
8
+ desc 'Reset the databases'
9
+
10
+ def call(**)
11
+ Thunder.application.chunks.each do |chunk|
12
+ chunk.boot
13
+ chunk.database.drop!
14
+ chunk.database.create!
15
+ chunk.database.migrate!
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thunder
4
+ module CLI
5
+ module Commands
6
+ module Db
7
+ class Setup < Command
8
+ desc 'Setups the databases'
9
+
10
+ def call(**)
11
+ Thunder.application.chunks.each do |chunk|
12
+ chunk.boot
13
+ chunk.database.create!
14
+ chunk.database.migrate!
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ # WARN: AI MADE
4
+ module Thunder
5
+ module CLI
6
+ module Commands
7
+ module Generate
8
+ class Chunk < CLI::Command
9
+ desc 'Create the chunk NAME in chunks/'
10
+
11
+ argument :name, required: true, desc: 'Chunk name'
12
+
13
+ option :force, type: :boolean, default: false, aliases: ['-f'], desc: 'Overwrites existing files'
14
+
15
+ def call(name:, force:, **)
16
+ Thunder::Generator.new(Thunder.application.root, force:, out:)
17
+ .directory('chunk', "chunks/#{name}", chunk_name: name)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ # WARN: AI MADE
4
+ module Thunder
5
+ module CLI
6
+ module Commands
7
+ module Generate
8
+ # Base of the generators that write into a chunk: the chunk comes from -c, or from the current directory.
9
+ # The key of the file (books.add, library/books.added) gives its path and its modules.
10
+ class Command < CLI::Command
11
+ option :chunk, aliases: ['-c'], desc: 'Chunk name'
12
+ option :force, type: :boolean, default: false, aliases: ['-f'], desc: 'Overwrites existing files'
13
+
14
+ USE_KINDS = { 'model' => :model, 'operation' => :operation, 'event' => :event,
15
+ 'external' => :external_operation }.freeze
16
+
17
+ private
18
+
19
+ # title:String author_id:Integer:optional -> [['title', 'String', false], ['author_id', 'Integer', true]].
20
+ # The type defaults to String. `optional` instead of a `?`, which the shell would take as a glob.
21
+ def fields(list)
22
+ list.map do |field|
23
+ # The type may hold :: (ActiveRecord::Relation), so only the name and an :optional suffix are split off.
24
+ name, rest = field.split(':', 2)
25
+ type = (rest&.delete_suffix(':optional') || 'String').camelize
26
+ # A model is named in quotes ('Models::Book'): the chunk resolves it on boot (ThunderChunk::TypeNames).
27
+ type = "'#{type}'" if type.start_with?('Models::')
28
+ [name, type, rest.to_s.end_with?(':optional')]
29
+ end
30
+ end
31
+
32
+ # model:book,external:library/books.show -> the use lines. A model is named by its constant (Book); the
33
+ # others get an accessor from the key (books_add, library_books_show).
34
+ def uses(list, allowed)
35
+ list.map do |use|
36
+ kind, key = use.split(':', 2)
37
+ unless key && allowed.include?(kind)
38
+ raise Error, "--use #{use}: expected kind:key with kind in #{allowed.join(', ')}"
39
+ end
40
+
41
+ kind == 'model' ? "use :model, '#{key}'" : "use :#{USE_KINDS[kind]}, '#{key}', as: :#{key.tr('./', '__')}"
42
+ end
43
+ end
44
+
45
+ # WARN: AI MADE
46
+ # The class of app/common the chunk's code inherits from, when the chunk has its file (g chunk writes them);
47
+ # the framework's class otherwise.
48
+ def parent_for(chunk, common, framework)
49
+ chunk.root_path.join('app/common').glob("**/#{common.underscore}.rb").any? ? common : framework
50
+ end
51
+
52
+ def chunk_for(name)
53
+ Thunder.application.chunk(name || default_chunk || raise(Error, 'no chunk given; pass -c CHUNK'))
54
+ end
55
+
56
+ # books.add -> ['books', 'add']; library/books.added -> ['library', 'books', 'added']
57
+ def segments(key)
58
+ key.split(%r{[./]})
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ # WARN: AI MADE
4
+ module Thunder
5
+ module CLI
6
+ module Commands
7
+ module Generate
8
+ class Event < Command
9
+ desc 'Create the event KEY (books.added) the chunk publishes'
10
+
11
+ argument :key, required: true, desc: 'Event key'
12
+ argument :fields, type: :array, required: false, desc: 'Payload fields, name:Type (id:Integer title:String)'
13
+
14
+ def call(key:, force:, chunk: nil, fields: [], **)
15
+ chunk = chunk_for(chunk)
16
+ *modules, name = segments(key).map(&:camelize)
17
+
18
+ Thunder::Generator.new(chunk.root_path, force:, out:).template(
19
+ 'event/event.rb.erb', "app/external/messaging/events/#{segments(key).join('/')}.rb",
20
+ namespace: ["#{chunk.name}::External", 'Messaging', 'Events', *modules], class_name: name,
21
+ fields: fields(fields)
22
+ )
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ # WARN: AI MADE
4
+ module Thunder
5
+ module CLI
6
+ module Commands
7
+ module Generate
8
+ class Model < Command
9
+ desc 'Create the model NAME and its migration; columns as name:type (title:string author:references)'
10
+
11
+ argument :name, required: true, desc: 'Model name'
12
+ argument :columns, type: :array, required: false, desc: 'Columns, name:type'
13
+
14
+ def call(name:, force:, chunk: nil, columns: [], **)
15
+ chunk = chunk_for(chunk)
16
+ *modules, class_name = segments(name).map(&:camelize)
17
+ columns = columns.map { |column| column.split(':', 2) }
18
+ table_name = segments(name).join('_').pluralize
19
+ generator = Thunder::Generator.new(chunk.root_path, force:, out:)
20
+
21
+ generator.template(
22
+ 'model/model.rb.erb', "app/internal/models/#{segments(name).join('/')}.rb",
23
+ namespace: ["#{chunk.name}::Internal", 'Models', *modules], class_name:,
24
+ references: columns.filter_map { |column, type| column if type == 'references' },
25
+ parent: parent_for(chunk, 'ApplicationModel', 'ThunderChunk::Model')
26
+ )
27
+ version = Time.now.utc.strftime('%Y%m%d%H%M%S')
28
+
29
+ generator.template(
30
+ 'model/migration.rb.erb', "database/migrate/#{version}_create_#{table_name}.rb",
31
+ migration_class: "Create#{table_name.camelize}", migration_version: ActiveRecord::Migration.current_version,
32
+ table_name:, columns:
33
+ )
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ # WARN: AI MADE
4
+ module Thunder
5
+ module CLI
6
+ module Commands
7
+ module Generate
8
+ class Operation < Command
9
+ desc 'Create the operation KEY (books.add); -e makes it external'
10
+
11
+ argument :key, required: true, desc: 'Operation key'
12
+ argument :params, type: :array, required: false,
13
+ desc: 'Params, name:Type[:optional] (title:String id:Integer:optional)'
14
+
15
+ option :external, type: :boolean, default: false, aliases: ['-e'], desc: 'External operation'
16
+ option :use, type: :array, default: [],
17
+ desc: 'Dependencies, kind:key comma-separated (model:book,operation:books.find,' \
18
+ 'event:books.added,external:library/books.show)'
19
+ option :returns, type: :array, default: [],
20
+ desc: 'Fields of the returned Hash, name:Type comma-separated (id:Integer,title)'
21
+
22
+ def call(key:, force:, external:, chunk: nil, params: [], use: [], returns: [], **)
23
+ chunk = chunk_for(chunk)
24
+ *modules, name = segments(key).map(&:camelize)
25
+ scope = external ? 'external' : 'internal'
26
+ allowed = external ? %w[operation external] : %w[model operation event external]
27
+
28
+ Thunder::Generator.new(chunk.root_path, force:, out:).template(
29
+ "operation/#{scope}.rb.erb", "app/#{scope}/operations/#{segments(key).join('/')}.rb",
30
+ namespace: ["#{chunk.name}::#{scope.camelize}", 'Operations', *modules], class_name: name,
31
+ params: fields(params), uses: uses(use, allowed), returns: fields(returns),
32
+ parent: parent_for(chunk, "#{scope.camelize}Operation", "ThunderChunk::Operations::#{scope.camelize}")
33
+ )
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # WARN: AI MADE
4
+ module Thunder
5
+ module CLI
6
+ module Commands
7
+ module Generate
8
+ # The migration of the table the outbox publisher writes the chunk's events to. One per chunk: a second run
9
+ # finds the first migration and stops, or rewrites it with --force.
10
+ class Outbox < Command
11
+ desc 'Create the migration of the outbox table, where the chunk writes the events it publishes'
12
+
13
+ option :table, default: 'thunder_outbox', desc: 'Table name (messaging.outbox.table_name)'
14
+
15
+ def call(force:, table:, chunk: nil, **)
16
+ chunk = chunk_for(chunk)
17
+ existing = chunk.root_path.glob("database/migrate/*_create_#{table}.rb").first
18
+ raise Error, "#{existing.relative_path_from(chunk.root_path)} already exists (--force overwrites)" if existing && !force
19
+
20
+ path = existing&.relative_path_from(chunk.root_path) ||
21
+ "database/migrate/#{Time.now.utc.strftime('%Y%m%d%H%M%S')}_create_#{table}.rb"
22
+
23
+ Thunder::Generator.new(chunk.root_path, force:, out:).template(
24
+ 'outbox/migration.rb.erb', path.to_s,
25
+ migration_class: "Create#{table.camelize}", migration_version: ActiveRecord::Migration.current_version,
26
+ table_name: table
27
+ )
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # WARN: AI MADE
4
+ module Thunder
5
+ module CLI
6
+ module Commands
7
+ module Generate
8
+ class Subscriber < Command
9
+ desc 'Create the subscriber of the event KEY of another chunk (library/books.added)'
10
+
11
+ argument :routing_key, required: true, desc: 'Routing key, <chunk>/<event>'
12
+ argument :params, type: :array, required: false, desc: 'Params, name:Type (id:Integer title:String)'
13
+
14
+ option :use, type: :array, default: [],
15
+ desc: 'Internal operations it calls, kind:key comma-separated (operation:reviews.create)'
16
+
17
+ def call(routing_key:, force:, chunk: nil, params: [], use: [], **)
18
+ chunk = chunk_for(chunk)
19
+ *modules, name = segments(routing_key).map(&:camelize)
20
+
21
+ path = "app/external/messaging/subscribers/#{segments(routing_key).join('/')}.rb"
22
+
23
+ Thunder::Generator.new(chunk.root_path, force:, out:).template(
24
+ 'subscriber/subscriber.rb.erb', path,
25
+ namespace: ["#{chunk.name}::External", 'Messaging', 'Subscribers', *modules], class_name: name,
26
+ params: fields(params), uses: uses(use, %w[operation])
27
+ )
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ # WARN: AI MADE
4
+ module Thunder
5
+ module CLI
6
+ module Commands
7
+ module Messaging
8
+ class Consumer < Command
9
+ desc 'Consumes the events the chunk subscribes to'
10
+
11
+ argument :chunk_name, required: true, desc: 'Chunk name'
12
+ argument :broker_name, required: true
13
+ argument :handler_name, required: true
14
+
15
+ def call(chunk_name:, broker_name:, handler_name:, **)
16
+ require 'thunderwire'
17
+ require 'thunderchunk/exporters/events'
18
+
19
+ # WARN: AI MADE
20
+ ThunderChunk::SenderDock.resolver = ThunderWire.method(:sender)
21
+ ThunderChunk::ExternalImporters::Events.resolver = ThunderWire.method(:publisher)
22
+ # WARN: ---
23
+
24
+ chunk = Thunder.application.chunk(chunk_name)
25
+ chunk.boot(boot: { exporters: [ThunderChunk::Exporters::Events] })
26
+
27
+ broker_module = ThunderWire::MessageBroker.get!(broker_name)
28
+
29
+ exporter = chunk.exporter.get(:event)
30
+ handler = ThunderWire::Handler.get!(broker_module, handler_name).new(exporter)
31
+
32
+ consumer = broker_module::Consumer.new(**{
33
+ url: chunk.config['messaging.broker.url'],
34
+ queue: "#{chunk.identifier}.events",
35
+ keys: exporter.items.keys,
36
+ handler:,
37
+ exchange: chunk.config['messaging.broker.exchange'],
38
+ dead_exchange: chunk.config['messaging.broker.dead_exchange'],
39
+ prefetch: chunk.config['messaging.consumer.prefetch'],
40
+ concurrency: chunk.config['messaging.consumer.concurrency'],
41
+ delivery_limit: chunk.config['messaging.consumer.delivery_limit']
42
+ }.compact)
43
+
44
+ %w[TERM INT].each { |signal| Signal.trap(signal) { consumer.stop } }
45
+
46
+ consumer.start
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thunder
4
+ module CLI
5
+ module Commands
6
+ module Messaging
7
+ class Outbox < Command
8
+ desc 'Starts the outbox relay'
9
+
10
+ argument :chunk_name, required: true, desc: 'Chunk name'
11
+
12
+ def call(chunk_name:, **)
13
+ require 'thunderwire'
14
+
15
+ chunk = Thunder.application.chunk(chunk_name)
16
+ chunk.boot
17
+
18
+ broker_module = ThunderWire::MessageBroker.get!(chunk.config['messaging.broker.type'])
19
+
20
+ outbox = broker_module.const_get('Outbox').new(**{
21
+ url: chunk.config['messaging.broker.url'],
22
+ chunk: chunk.identifier,
23
+ exchange: chunk.config['messaging.broker.exchange'],
24
+ batch_size: chunk.config['messaging.broker.batch_size'],
25
+ idle: chunk.config['messaging.broker.idle']
26
+ }.compact)
27
+
28
+ %w[TERM INT].each { |signal| Signal.trap(signal) { outbox.stop } }
29
+
30
+ outbox.start
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ # WARN: AI MADE
4
+ module Thunder
5
+ module CLI
6
+ module Commands
7
+ # Not a Thunder::CLI::Command: the application does not exist yet.
8
+ class New < Dry::CLI::Command
9
+ desc 'Create an application in the folder NAME'
10
+
11
+ argument :name, required: true, desc: 'Application name'
12
+
13
+ option :dev, type: :boolean, default: false, desc: 'Gemfile points to the repository of the running framework'
14
+ option :path, desc: 'Gemfile points to the framework repository at PATH'
15
+ option :skip_bundle, type: :boolean, default: false, desc: 'Does not run bundle install'
16
+ option :skip_git, type: :boolean, default: false, desc: 'Does not run git init'
17
+ option :force, type: :boolean, default: false, aliases: ['-f'], desc: 'Overwrites existing files'
18
+
19
+ def call(name:, dev:, skip_bundle:, skip_git:, force:, path: nil, **)
20
+ root = Pathname(name).expand_path
21
+ framework_path = path ? Pathname(path).expand_path : (Thunder::Generator::FRAMEWORK_ROOT if dev)
22
+
23
+ Thunder::Generator.new(root, force:, out:).directory(
24
+ 'new', '.', app_name: root.basename.to_s, framework_path:, ruby_version: RUBY_VERSION
25
+ )
26
+
27
+ unless skip_bundle
28
+ require 'bundler'
29
+ # The bundle of the running thunder would leak into the one of the new application.
30
+ Bundler.with_unbundled_env { system('bundle', 'install', chdir: root.to_s) }
31
+ end
32
+
33
+ unless skip_git
34
+ system('git', 'init', '--quiet', chdir: root.to_s)
35
+ system('git', 'add', '--all', chdir: root.to_s)
36
+ system('git', 'commit', '--quiet', '--message', 'thunder new', chdir: root.to_s)
37
+ end
38
+
39
+ out.puts "\ncd #{name} && thunder generate chunk NAME"
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thunder
4
+ module CLI
5
+ module Commands
6
+ class Serve < Command
7
+ desc 'Start Http Server'
8
+
9
+ argument :chunk_name, required: true, desc: 'Chunk name'
10
+ argument :protocol_name, required: true
11
+ argument :wire_name, required: true
12
+
13
+ option :ip, default: '0.0.0.0'
14
+ option :port, default: 3000
15
+
16
+ def call(chunk_name:, protocol_name:, wire_name:, ip:, port:, **)
17
+ require 'thunderwire'
18
+
19
+ # WARN: AI MADE
20
+ ThunderChunk::SenderDock.resolver = ThunderWire.method(:sender)
21
+ ThunderChunk::ExternalImporters::Events.resolver = ThunderWire.method(:publisher)
22
+ # WARN: ---
23
+
24
+ chunk = Thunder.application.chunk(chunk_name)
25
+ protocol = ThunderWire::Protocol.get!(protocol_name)
26
+ wire = ThunderWire::Wire.get!(protocol, wire_name)
27
+ exporter = wire.exporter
28
+
29
+ chunk.boot(boot: { exporters: [exporter] })
30
+
31
+ server = ThunderWire::Server.build(chunk, protocol, wire, ip, port)
32
+
33
+ server.up
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end