rivulet-rb 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 (50) hide show
  1. checksums.yaml +7 -0
  2. data/bin/rivulet +4 -0
  3. data/lib/rivulet/application.rb +90 -0
  4. data/lib/rivulet/cli/console.rb +15 -0
  5. data/lib/rivulet/cli/db/migrate.rb +17 -0
  6. data/lib/rivulet/cli/generate/handler/operation.rb +75 -0
  7. data/lib/rivulet/cli/generate/handler/step.rb +91 -0
  8. data/lib/rivulet/cli/generate/handler.rb +135 -0
  9. data/lib/rivulet/cli/generate/migration.rb +31 -0
  10. data/lib/rivulet/cli/generate/operation.rb +111 -0
  11. data/lib/rivulet/cli/generate/resource.rb +25 -0
  12. data/lib/rivulet/cli/generate/service/operation.rb +118 -0
  13. data/lib/rivulet/cli/generate/service/projection.rb +89 -0
  14. data/lib/rivulet/cli/generate/service/step.rb +91 -0
  15. data/lib/rivulet/cli/generate/service.rb +143 -0
  16. data/lib/rivulet/cli/new.rb +191 -0
  17. data/lib/rivulet/cli/routes.rb +15 -0
  18. data/lib/rivulet/cli.rb +43 -0
  19. data/lib/rivulet/container.rb +28 -0
  20. data/lib/rivulet/operation.rb +21 -0
  21. data/lib/rivulet/operations/dispatch_request.rb +21 -0
  22. data/lib/rivulet/operations/migrate.rb +23 -0
  23. data/lib/rivulet/operations/print_routes.rb +17 -0
  24. data/lib/rivulet/operations/run_console.rb +25 -0
  25. data/lib/rivulet/operations/startup.rb +23 -0
  26. data/lib/rivulet/projection.rb +6 -0
  27. data/lib/rivulet/request.rb +18 -0
  28. data/lib/rivulet/response.rb +12 -0
  29. data/lib/rivulet/routing/mapper.rb +78 -0
  30. data/lib/rivulet/routing/route.rb +14 -0
  31. data/lib/rivulet/step.rb +22 -0
  32. data/lib/rivulet/steps/build_config.rb +26 -0
  33. data/lib/rivulet/steps/build_context.rb +76 -0
  34. data/lib/rivulet/steps/compile_response.rb +113 -0
  35. data/lib/rivulet/steps/dispatch.rb +17 -0
  36. data/lib/rivulet/steps/load_app.rb +20 -0
  37. data/lib/rivulet/steps/load_db.rb +24 -0
  38. data/lib/rivulet/steps/load_routes.rb +28 -0
  39. data/lib/rivulet/steps/load_settings.rb +42 -0
  40. data/lib/rivulet/steps/print_routes.rb +123 -0
  41. data/lib/rivulet/steps/run_console.rb +26 -0
  42. data/lib/rivulet/steps/run_migrations.rb +50 -0
  43. data/lib/rivulet/steps/validate_response.rb +42 -0
  44. data/lib/rivulet/telemetry/node.rb +8 -0
  45. data/lib/rivulet/telemetry/sequel_extension.rb +19 -0
  46. data/lib/rivulet/telemetry/timing_wrapper.rb +12 -0
  47. data/lib/rivulet/telemetry.rb +62 -0
  48. data/lib/rivulet/version.rb +3 -0
  49. data/lib/rivulet.rb +66 -0
  50. metadata +342 -0
@@ -0,0 +1,118 @@
1
+ require 'fileutils'
2
+
3
+ module Rivulet
4
+ module CLI
5
+ module Commands
6
+ module Generate
7
+ class Service
8
+ class Operation < Dry::CLI::Command
9
+ desc "Generate an operation"
10
+ argument :name, required: true, desc: "Operation name in service.operation format (e.g. users.create)"
11
+
12
+ def call(name:, **)
13
+ service_name, operation_name = name.split('.')
14
+ service_dir = underscore(service_name)
15
+ operation_dir = underscore(operation_name)
16
+ service_module = camelize(service_dir)
17
+ operation_module = camelize(operation_dir)
18
+ base = "app/services/#{service_dir}"
19
+
20
+ mkdir "#{base}/projections"
21
+ write "#{base}/operations/#{operation_dir}.rb", operation_template(service_module, operation_module)
22
+ write "#{base}/contracts/#{operation_dir}.rb", contract_template(service_module, operation_module)
23
+ register_operation(base, service_dir, service_module, operation_dir, operation_module)
24
+ end
25
+
26
+ private
27
+
28
+ def underscore(str)
29
+ str
30
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
31
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
32
+ .downcase
33
+ end
34
+
35
+ def camelize(str)
36
+ str.split('_').map(&:capitalize).join
37
+ end
38
+
39
+ def mkdir(path)
40
+ Dir.mkdir(path) unless Dir.exist?(path)
41
+ end
42
+
43
+ def write(path, content)
44
+ File.write(path, content)
45
+ puts " create #{path}"
46
+ end
47
+
48
+ def operation_template(service_module, operation_module)
49
+ <<~RUBY
50
+ module Services
51
+ module #{service_module}
52
+ module Operations
53
+ class #{operation_module} < Rivulet::Operation
54
+ def call(input = {})
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ RUBY
61
+ end
62
+
63
+ def contract_template(service_module, operation_module)
64
+ <<~RUBY
65
+ module Services
66
+ module #{service_module}
67
+ module Contracts
68
+ class #{operation_module} < ApplicationContract
69
+ params do
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ RUBY
76
+ end
77
+
78
+ def register_operation(base, service_dir, service_module, operation_dir, operation_module)
79
+ container_file = "#{base}/container.rb"
80
+ op_registration = " register('#{operation_dir}') { Services::#{service_module}::Operations::#{operation_module}.new }\n"
81
+ contract_registration = " register('#{operation_dir}') { Services::#{service_module}::Contracts::#{operation_module}.new }\n"
82
+
83
+ if File.exist?(container_file)
84
+ content = File.read(container_file)
85
+ content.sub!(/( namespace\('operations'\) do\n)/, "\\1#{op_registration}")
86
+ content.sub!(/( namespace\('contracts'\) do\n)/, "\\1#{contract_registration}")
87
+ File.write(container_file, content)
88
+ puts " update #{container_file}"
89
+ else
90
+ write container_file, container_template(service_module, operation_dir, operation_module)
91
+ end
92
+ end
93
+
94
+ def container_template(service_module, operation_dir, operation_module)
95
+ <<~RUBY
96
+ module Services
97
+ module #{service_module}
98
+ class Container
99
+ extend Dry::Core::Container::Mixin
100
+
101
+ namespace('operations') do
102
+ register('#{operation_dir}') { Services::#{service_module}::Operations::#{operation_module}.new }
103
+ end
104
+
105
+ namespace('contracts') do
106
+ register('#{operation_dir}') { Services::#{service_module}::Contracts::#{operation_module}.new }
107
+ end
108
+ end
109
+ end
110
+ end
111
+ RUBY
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,89 @@
1
+ require 'fileutils'
2
+
3
+ module Rivulet
4
+ module CLI
5
+ module Commands
6
+ module Generate
7
+ class Service
8
+ class Projection < Dry::CLI::Command
9
+ desc "Generate a projection"
10
+ argument :name, required: true, desc: "Step name in service.projection format (e.g. users.common)"
11
+
12
+ def call(name:, **)
13
+ service_name, step_name = name.split('.')
14
+ service_dir = underscore(service_name)
15
+ projection_dir = underscore(step_name)
16
+ service_module = camelize(service_dir)
17
+ projection_class = camelize(projection_dir)
18
+ base = "app/services/#{service_dir}"
19
+
20
+ write "#{base}/projections/#{projection_dir}.rb", projection_template(service_module, projection_class)
21
+ register_projection(base, service_module, projection_dir, projection_class)
22
+ end
23
+
24
+ private
25
+
26
+ def underscore(str)
27
+ str
28
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
29
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
30
+ .downcase
31
+ end
32
+
33
+ def camelize(str)
34
+ str.split('_').map(&:capitalize).join
35
+ end
36
+
37
+ def write(path, content)
38
+ File.write(path, content)
39
+ puts " create #{path}"
40
+ end
41
+
42
+ def projection_template(service_module, projection_class)
43
+ <<~RUBY
44
+ module Services
45
+ module #{service_module}
46
+ module Projections
47
+ class #{projection_class} < Rivulet::Projection
48
+ end
49
+ end
50
+ end
51
+ end
52
+ RUBY
53
+ end
54
+
55
+ def register_projection(base, service_module, projection_name, projection_class)
56
+ container_file = "#{base}/container.rb"
57
+ projection_registration = " register('#{projection_name}') { Services::#{service_module}::Projections::#{projection_class}.new }\n"
58
+
59
+ if File.exist?(container_file)
60
+ content = File.read(container_file)
61
+ content.sub!(/( namespace\('projections'\) do\n)/, "\\1#{projection_registration}")
62
+ File.write(container_file, content)
63
+ puts " update #{container_file}"
64
+ else
65
+ write container_file, container_template(service_module, projection_name, projection_class)
66
+ end
67
+ end
68
+
69
+ def container_template(service_module, projection_name, projection_class)
70
+ <<~RUBY
71
+ module Services
72
+ module #{service_module}
73
+ class Container
74
+ extend Dry::Core::Container::Mixin
75
+
76
+ namespace('projections') do
77
+ register('#{projection_name}') { Services::#{service_module}::Projections::#{projection_class}.new }
78
+ end
79
+ end
80
+ end
81
+ end
82
+ RUBY
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,91 @@
1
+ require 'fileutils'
2
+
3
+ module Rivulet
4
+ module CLI
5
+ module Commands
6
+ module Generate
7
+ class Service
8
+ class Step < Dry::CLI::Command
9
+ desc "Generate a step"
10
+ argument :name, required: true, desc: "Step name in service.step format (e.g. users.create)"
11
+
12
+ def call(name:, **)
13
+ service_name, step_name = name.split('.')
14
+ service_dir = underscore(service_name)
15
+ step_dir = underscore(step_name)
16
+ service_module = camelize(service_dir)
17
+ step_class = camelize(step_dir)
18
+ base = "app/services/#{service_dir}"
19
+
20
+ write "#{base}/steps/#{step_dir}.rb", step_template(service_module, step_class)
21
+ register_step(base, service_module, step_dir, step_class)
22
+ end
23
+
24
+ private
25
+
26
+ def underscore(str)
27
+ str
28
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
29
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
30
+ .downcase
31
+ end
32
+
33
+ def camelize(str)
34
+ str.split('_').map(&:capitalize).join
35
+ end
36
+
37
+ def write(path, content)
38
+ File.write(path, content)
39
+ puts " create #{path}"
40
+ end
41
+
42
+ def step_template(service_module, step_class)
43
+ <<~RUBY
44
+ module Services
45
+ module #{service_module}
46
+ module Steps
47
+ class #{step_class} < Rivulet::Step
48
+ def call(input)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ RUBY
55
+ end
56
+
57
+ def register_step(base, service_module, step_name, step_class)
58
+ container_file = "#{base}/container.rb"
59
+ step_registration = " register('#{step_name}') { Services::#{service_module}::Steps::#{step_class}.new }\n"
60
+
61
+ if File.exist?(container_file)
62
+ content = File.read(container_file)
63
+ content.sub!(/( namespace\('steps'\) do\n)/, "\\1#{step_registration}")
64
+ File.write(container_file, content)
65
+ puts " update #{container_file}"
66
+ else
67
+ write container_file, container_template(service_module, step_name, step_class)
68
+ end
69
+ end
70
+
71
+ def container_template(service_module, step_name, step_class)
72
+ <<~RUBY
73
+ module Services
74
+ module #{service_module}
75
+ class Container
76
+ extend Dry::Core::Container::Mixin
77
+
78
+ namespace('steps') do
79
+ register('#{step_name}') { Services::#{service_module}::Steps::#{step_class}.new }
80
+ end
81
+ end
82
+ end
83
+ end
84
+ RUBY
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,143 @@
1
+ require 'dry/inflector'
2
+ require 'fileutils'
3
+
4
+ module Rivulet
5
+ module CLI
6
+ module Commands
7
+ module Generate
8
+ class Service < Dry::CLI::Command
9
+ desc "Generate a service"
10
+ argument :name, required: true, desc: "Service name (e.g. Users or users)"
11
+
12
+ option :create, type: :boolean, aliases: ['-c'], desc: 'Add "create" operation'
13
+ option :read, type: :boolean, aliases: ['-r'], desc: 'Add "get" operation'
14
+ option :update, type: :boolean, aliases: ['-u'], desc: 'Add "update" operation'
15
+ option :delete, type: :boolean, aliases: ['-d'], desc: 'Add "delete" operation'
16
+ option :list, type: :boolean, aliases: ['-l'], desc: 'Add "list" operation'
17
+
18
+ SUBDIRS = %w[contracts operations steps].freeze
19
+
20
+ def call(name:, **options)
21
+ dir_name = underscore(name)
22
+ module_name = camelize(dir_name)
23
+ base = "app/services/#{dir_name}"
24
+ singular_name = Dry::Inflector.new.singularize(name)
25
+
26
+ SUBDIRS.each { |d| create_dir "#{base}/#{d}" }
27
+ write "#{base}/service.rb", service_template(module_name)
28
+ create_container(base, module_name) unless File.exist?("#{base}/container.rb")
29
+ register_service(dir_name, module_name)
30
+
31
+ if options[:create]
32
+ Operation.new.call(name: "#{name}.create_#{singular_name}")
33
+ end
34
+
35
+ if options[:read]
36
+ Operation.new.call(name: "#{name}.get_#{singular_name}")
37
+ end
38
+
39
+ if options[:update]
40
+ Operation.new.call(name: "#{name}.update_#{singular_name}")
41
+ end
42
+
43
+ if options[:delete]
44
+ Operation.new.call(name: "#{name}.delete_#{singular_name}")
45
+ end
46
+
47
+ if options[:list]
48
+ Operation.new.call(name: "#{name}.list_#{name}")
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def underscore(str)
55
+ str
56
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
57
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
58
+ .downcase
59
+ end
60
+
61
+ def camelize(str)
62
+ str.split('_').map(&:capitalize).join
63
+ end
64
+
65
+ def create_dir(path)
66
+ FileUtils.mkdir_p(path)
67
+ puts " create #{path}/"
68
+ end
69
+
70
+ def create_container(base, module_name)
71
+ write "#{base}/container.rb", container_template(module_name)
72
+ end
73
+
74
+ def write(path, content)
75
+ File.write(path, content)
76
+ puts " create #{path}"
77
+ end
78
+
79
+ def service_template(module_name)
80
+ <<~RUBY
81
+ module Services
82
+ module #{module_name}
83
+ class Service
84
+ NAMESPACE = 'operations'
85
+
86
+ def method_missing(name, input = {}, options = {}, &block)
87
+ key = [NAMESPACE, name].join('.')
88
+ super unless Container.key?(key)
89
+
90
+ Container[key].call(input, **options, &block)
91
+ end
92
+
93
+ def respond_to_missing?(name, include_private = false)
94
+ key = [NAMESPACE, name].join('.')
95
+ Container.key?(key) || super
96
+ end
97
+ end
98
+ end
99
+ end
100
+ RUBY
101
+ end
102
+
103
+ def container_template(module_name)
104
+ <<~RUBY
105
+ module Services
106
+ module #{module_name}
107
+ class Container
108
+ extend Dry::Core::Container::Mixin
109
+ import Services::Shared::Namespace
110
+
111
+ namespace('operations') do
112
+ end
113
+
114
+ namespace('steps') do
115
+ end
116
+
117
+ namespace('contracts') do
118
+ end
119
+
120
+ namespace('projections') do
121
+ end
122
+ end
123
+ end
124
+ end
125
+ RUBY
126
+ end
127
+
128
+ def register_service(dir_name, module_name)
129
+ services_file = 'app/services.rb'
130
+ return unless File.exist?(services_file)
131
+
132
+ content = File.read(services_file)
133
+ registration = " register('#{dir_name}') { Services::#{module_name}::Service.new }\n"
134
+
135
+ updated = content.sub(/^end\s*\z/, "#{registration}end\n")
136
+ File.write(services_file, updated)
137
+ puts " update #{services_file}"
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,191 @@
1
+ require 'fileutils'
2
+
3
+ module Rivulet
4
+ module CLI
5
+ module Commands
6
+ class New < Dry::CLI::Command
7
+ desc "Create a new Rivulet application"
8
+ argument :name, required: true, desc: "Application name"
9
+
10
+ DIRS = %w[
11
+ app/handlers
12
+ app/handlers/shared
13
+ app/handlers/shared/steps
14
+ app/handlers/shared/utils
15
+ app/services
16
+ app/services/shared
17
+ app/services/shared/steps
18
+ app/services/shared/utils
19
+ app/models
20
+ config
21
+ db/migrations
22
+ ].freeze
23
+
24
+ def call(name:, **)
25
+ DIRS.each { |d| create_dir "#{name}/#{d}" }
26
+
27
+ write name, 'Gemfile', gemfile
28
+ write name, 'config.ru', config_ru
29
+ write name, 'config/application.rb', application_config(name)
30
+ write name, 'config/routes.rb', routes_config
31
+ write name, 'falcon.rb', falcon_config
32
+ write name, 'app/handlers.rb', handlers_container
33
+ write name, 'app/handlers/shared/container.rb', handlers_shared_container
34
+ write name, 'app/handlers/shared/namespace.rb', handlers_shared_namespace
35
+ write name, 'app/services.rb', services_container
36
+ write name, 'app/services/shared/container.rb', services_shared_container
37
+ write name, 'app/services/shared/namespace.rb', services_shared_namespace
38
+ write name, 'app/application_contract.rb', application_contract_template
39
+
40
+ puts "\nDone! Next steps:\n cd #{name}\n bundle install\n bundle exec falcon host falcon.rb"
41
+ end
42
+
43
+ private
44
+
45
+ def create_dir(path)
46
+ FileUtils.mkdir_p(path)
47
+ puts " create #{path}/"
48
+ end
49
+
50
+ def write(root, relative_path, content)
51
+ File.write(File.join(root, relative_path), content)
52
+ puts " create #{relative_path}"
53
+ end
54
+
55
+ def gemfile
56
+ <<~RUBY
57
+ source 'https://rubygems.org'
58
+
59
+ gem 'rivulet-rb'
60
+ gem 'falcon'
61
+ RUBY
62
+ end
63
+
64
+ def config_ru
65
+ <<~RUBY
66
+ require 'rivulet'
67
+
68
+ run Rivulet.app.startup
69
+ RUBY
70
+ end
71
+
72
+ def application_config(name)
73
+ <<~RUBY
74
+ Rivulet.configure do |config|
75
+ # config.database.dsn = ENV.fetch('DATABASE_URL', 'sqlite://db/#{name}.sqlite3')
76
+
77
+ # config.sendfile.enabled = true
78
+ # config.sendfile.variation = 'x-accel-redirect'
79
+ # config.sendfile.mappings = [['/var/www/', '/files/']]
80
+
81
+ config.logger.name = :#{name}
82
+ config.logger.level = :info
83
+ end
84
+ RUBY
85
+ end
86
+
87
+ def falcon_config
88
+ <<~RUBY
89
+ require 'falcon/environment/rack'
90
+ require 'falcon/environment/server'
91
+
92
+ service "app" do
93
+ include Falcon::Environment::Server
94
+ include Falcon::Environment::Rackup
95
+ end
96
+ RUBY
97
+ end
98
+
99
+ def routes_config
100
+ <<~RUBY
101
+ # get :posts, to: 'posts#index'
102
+
103
+ Rivulet.routes.draw do
104
+ end
105
+ RUBY
106
+ end
107
+
108
+ def handlers_container
109
+ <<~RUBY
110
+ module Handlers
111
+ extend Dry::Core::Container::Mixin
112
+ end
113
+ RUBY
114
+ end
115
+
116
+ def handlers_shared_container
117
+ <<~RUBY
118
+ module Handlers
119
+ module Shared
120
+ class Container
121
+ extend Dry::Core::Container::Mixin
122
+ import Namespace
123
+ end
124
+ end
125
+ end
126
+ RUBY
127
+ end
128
+
129
+ def handlers_shared_namespace
130
+ <<~RUBY
131
+ module Handlers
132
+ module Shared
133
+ Namespace = Dry::Core::Container::Namespace.new('shared') do
134
+ namespace('steps') do
135
+ end
136
+
137
+ namespace('utils') do
138
+ end
139
+ end
140
+ end
141
+ end
142
+ RUBY
143
+ end
144
+
145
+ def services_container
146
+ <<~RUBY
147
+ module Services
148
+ extend Dry::Core::Container::Mixin
149
+ end
150
+ RUBY
151
+ end
152
+
153
+ def services_shared_container
154
+ <<~RUBY
155
+ module Services
156
+ module Shared
157
+ class Container
158
+ extend Dry::Core::Container::Mixin
159
+ import Namespace
160
+ end
161
+ end
162
+ end
163
+ RUBY
164
+ end
165
+
166
+ def services_shared_namespace
167
+ <<~RUBY
168
+ module Services
169
+ module Shared
170
+ Namespace = Dry::Core::Container::Namespace.new('shared') do
171
+ namespace('steps') do
172
+ end
173
+
174
+ namespace('utils') do
175
+ end
176
+ end
177
+ end
178
+ end
179
+ RUBY
180
+ end
181
+
182
+ def application_contract_template
183
+ <<~RUBY
184
+ class ApplicationContract < Dry::Validation::Contract
185
+ end
186
+ RUBY
187
+ end
188
+ end
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,15 @@
1
+ require 'rivulet'
2
+
3
+ module Rivulet
4
+ module CLI
5
+ module Commands
6
+ class Routes < Dry::CLI::Command
7
+ desc 'Show routes'
8
+
9
+ def call(**)
10
+ Rivulet.app.print_routes
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,43 @@
1
+ require 'dry/cli'
2
+ require_relative 'cli/new'
3
+ require_relative 'cli/console'
4
+ require_relative 'cli/routes'
5
+ require_relative 'cli/db/migrate'
6
+ require_relative 'cli/generate/operation'
7
+ require_relative 'cli/generate/handler'
8
+ require_relative 'cli/generate/handler/operation'
9
+ require_relative 'cli/generate/handler/step'
10
+ require_relative 'cli/generate/migration'
11
+ require_relative 'cli/generate/resource'
12
+ require_relative 'cli/generate/service'
13
+ require_relative 'cli/generate/service/operation'
14
+ require_relative 'cli/generate/service/step'
15
+ require_relative 'cli/generate/service/projection'
16
+
17
+ module Rivulet
18
+ module CLI
19
+ module Commands
20
+ extend Dry::CLI::Registry
21
+
22
+ register 'new', Commands::New
23
+ register 'console', Commands::Console, aliases: ['c']
24
+ register 'routes', Commands::Routes
25
+
26
+ register 'generate', aliases: ['g'] do |prefix|
27
+ prefix.register 'resource', Commands::Generate::Resource
28
+ prefix.register 'service', Commands::Generate::Service
29
+ prefix.register 'service operation', Commands::Generate::Service::Operation
30
+ prefix.register 'service step', Commands::Generate::Service::Step
31
+ prefix.register 'service projection', Commands::Generate::Service::Projection
32
+ prefix.register 'handler', Commands::Generate::Handler
33
+ prefix.register 'handler operation', Commands::Generate::Handler::Operation
34
+ prefix.register 'handler step', Commands::Generate::Handler::Step
35
+ prefix.register 'migration', Commands::Generate::Migration
36
+ end
37
+
38
+ register 'db' do |prefix|
39
+ prefix.register 'migrate', Commands::DB::Migrate
40
+ end
41
+ end
42
+ end
43
+ end