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.
- checksums.yaml +7 -0
- data/README.md +5 -0
- data/exe/thunder +11 -0
- data/lib/thunder/application.rb +35 -0
- data/lib/thunder/cli/command.rb +19 -0
- data/lib/thunder/cli/commands/console.rb +24 -0
- data/lib/thunder/cli/commands/db/create.rb +20 -0
- data/lib/thunder/cli/commands/db/drop.rb +20 -0
- data/lib/thunder/cli/commands/db/migrate.rb +20 -0
- data/lib/thunder/cli/commands/db/reset.rb +22 -0
- data/lib/thunder/cli/commands/db/setup.rb +21 -0
- data/lib/thunder/cli/commands/generate/chunk.rb +23 -0
- data/lib/thunder/cli/commands/generate/command.rb +64 -0
- data/lib/thunder/cli/commands/generate/event.rb +28 -0
- data/lib/thunder/cli/commands/generate/model.rb +39 -0
- data/lib/thunder/cli/commands/generate/operation.rb +39 -0
- data/lib/thunder/cli/commands/generate/outbox.rb +33 -0
- data/lib/thunder/cli/commands/generate/subscriber.rb +33 -0
- data/lib/thunder/cli/commands/messaging/consumer.rb +52 -0
- data/lib/thunder/cli/commands/messaging/outbox.rb +36 -0
- data/lib/thunder/cli/commands/new.rb +44 -0
- data/lib/thunder/cli/commands/serve.rb +38 -0
- data/lib/thunder/cli/commands/spec.rb +77 -0
- data/lib/thunder/cli/commands/version.rb +16 -0
- data/lib/thunder/cli.rb +78 -0
- data/lib/thunder/console/backtrace.rb +36 -0
- data/lib/thunder/console/banner.rb +93 -0
- data/lib/thunder/console/completion.rb +68 -0
- data/lib/thunder/console/constants.rb +57 -0
- data/lib/thunder/console/evaluation.rb +72 -0
- data/lib/thunder/console/help.rb +30 -0
- data/lib/thunder/console/helpers.rb +16 -0
- data/lib/thunder/console/inspector.rb +114 -0
- data/lib/thunder/console/pretty.rb +70 -0
- data/lib/thunder/console/prompt.rb +52 -0
- data/lib/thunder/console/theme.rb +85 -0
- data/lib/thunder/console/views/collection.rb +172 -0
- data/lib/thunder/console/views/format.rb +114 -0
- data/lib/thunder/console/views/layout.rb +72 -0
- data/lib/thunder/console/views/model.rb +92 -0
- data/lib/thunder/console/views/operation.rb +112 -0
- data/lib/thunder/console/views/record.rb +95 -0
- data/lib/thunder/console/views.rb +45 -0
- data/lib/thunder/console.rb +167 -0
- data/lib/thunder/generator.rb +73 -0
- data/lib/thunder/templates/chunk/app/common/models/application_model.rb.erb +4 -0
- data/lib/thunder/templates/chunk/app/common/operations/external_operation.rb.erb +3 -0
- data/lib/thunder/templates/chunk/app/common/operations/internal_operation.rb.erb +3 -0
- data/lib/thunder/templates/chunk/app/external/operations/_keep.erb +0 -0
- data/lib/thunder/templates/chunk/app/internal/models/_keep.erb +0 -0
- data/lib/thunder/templates/chunk/app/internal/operations/_keep.erb +0 -0
- data/lib/thunder/templates/chunk/config/chunk.rb.erb +9 -0
- data/lib/thunder/templates/chunk/database/migrate/_keep.erb +0 -0
- data/lib/thunder/templates/event/event.rb.erb +6 -0
- data/lib/thunder/templates/model/migration.rb.erb +17 -0
- data/lib/thunder/templates/model/model.rb.erb +6 -0
- data/lib/thunder/templates/new/Gemfile.erb +21 -0
- data/lib/thunder/templates/new/README.md.erb +8 -0
- data/lib/thunder/templates/new/_gitignore.erb +4 -0
- data/lib/thunder/templates/new/_ruby-version.erb +2 -0
- data/lib/thunder/templates/new/bin/thunder.erb +6 -0
- data/lib/thunder/templates/new/chunks/_keep.erb +0 -0
- data/lib/thunder/templates/operation/external.rb.erb +24 -0
- data/lib/thunder/templates/operation/internal.rb.erb +26 -0
- data/lib/thunder/templates/outbox/migration.rb.erb +21 -0
- data/lib/thunder/templates/subscriber/subscriber.rb.erb +18 -0
- data/lib/thunder/version.rb +5 -0
- data/lib/thunder.rb +32 -0
- 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
data/exe/thunder
ADDED
|
@@ -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
|