hanami-cli 2.1.1 → 2.2.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +18 -13
- data/CHANGELOG.md +15 -0
- data/Gemfile +4 -2
- data/README.md +11 -7
- data/docker-compose.yml +9 -0
- data/hanami-cli.gemspec +2 -2
- data/lib/hanami/cli/command.rb +1 -1
- data/lib/hanami/cli/commands/app/command.rb +2 -16
- data/lib/hanami/cli/commands/app/db/command.rb +116 -0
- data/lib/hanami/cli/commands/app/db/create.rb +19 -11
- data/lib/hanami/cli/commands/app/db/drop.rb +19 -10
- data/lib/hanami/cli/commands/app/db/migrate.rb +19 -13
- data/lib/hanami/cli/commands/app/db/prepare.rb +42 -0
- data/lib/hanami/cli/commands/app/db/seed.rb +11 -22
- data/lib/hanami/cli/commands/app/db/structure/dump.rb +30 -7
- data/lib/hanami/cli/commands/app/db/structure/load.rb +52 -0
- data/lib/hanami/cli/commands/app/db/utils/database.rb +68 -73
- data/lib/hanami/cli/commands/app/db/utils/mysql.rb +2 -2
- data/lib/hanami/cli/commands/app/db/utils/postgres.rb +38 -19
- data/lib/hanami/cli/commands/app/db/utils/sqlite.rb +58 -10
- data/lib/hanami/cli/commands/app/db/version.rb +12 -9
- data/lib/hanami/cli/commands/app/generate/action.rb +4 -3
- data/lib/hanami/cli/commands/app/generate/command.rb +49 -0
- data/lib/hanami/cli/commands/app/generate/component.rb +49 -0
- data/lib/hanami/cli/commands/app/generate/migration.rb +27 -0
- data/lib/hanami/cli/commands/app/generate/operation.rb +26 -0
- data/lib/hanami/cli/commands/app/generate/part.rb +1 -1
- data/lib/hanami/cli/commands/app/generate/relation.rb +35 -0
- data/lib/hanami/cli/commands/app/generate/repo.rb +46 -0
- data/lib/hanami/cli/commands/app/generate/slice.rb +20 -3
- data/lib/hanami/cli/commands/app/generate/struct.rb +27 -0
- data/lib/hanami/cli/commands/app/install.rb +1 -1
- data/lib/hanami/cli/commands/app/middleware.rb +1 -1
- data/lib/hanami/cli/commands/app/server.rb +2 -2
- data/lib/hanami/cli/commands/app.rb +21 -2
- data/lib/hanami/cli/commands/gem/new.rb +78 -14
- data/lib/hanami/cli/errors.rb +28 -0
- data/lib/hanami/cli/generators/app/action_context.rb +5 -13
- data/lib/hanami/cli/generators/app/component/component.erb +8 -0
- data/lib/hanami/cli/generators/app/component/slice_component.erb +8 -0
- data/lib/hanami/cli/generators/app/component.rb +61 -0
- data/lib/hanami/cli/generators/app/component_context.rb +82 -0
- data/lib/hanami/cli/generators/app/migration.rb +69 -0
- data/lib/hanami/cli/generators/app/operation.rb +48 -0
- data/lib/hanami/cli/generators/app/part_context.rb +5 -21
- data/lib/hanami/cli/generators/app/relation.rb +44 -0
- data/lib/hanami/cli/generators/app/repo.rb +40 -0
- data/lib/hanami/cli/generators/app/ruby_file_writer.rb +149 -0
- data/lib/hanami/cli/generators/app/slice/operation.erb +7 -0
- data/lib/hanami/cli/generators/app/slice/relation.erb +8 -0
- data/lib/hanami/cli/generators/app/slice/{slice.erb → repo.erb} +3 -1
- data/lib/hanami/cli/generators/app/slice/struct.erb +8 -0
- data/lib/hanami/cli/generators/app/slice.rb +14 -6
- data/lib/hanami/cli/generators/app/slice_context.rb +9 -2
- data/lib/hanami/cli/generators/app/struct.rb +39 -0
- data/lib/hanami/cli/generators/app/view_context.rb +4 -16
- data/lib/hanami/cli/generators/constants.rb +39 -0
- data/lib/hanami/cli/generators/context.rb +48 -0
- data/lib/hanami/cli/generators/gem/app/action.erb +3 -0
- data/lib/hanami/cli/generators/gem/app/env.erb +4 -0
- data/lib/hanami/cli/generators/gem/app/gemfile.erb +11 -0
- data/lib/hanami/cli/generators/gem/app/gitignore.erb +1 -1
- data/lib/hanami/cli/generators/gem/app/operation.erb +13 -0
- data/lib/hanami/cli/generators/gem/app/relation.erb +10 -0
- data/lib/hanami/cli/generators/gem/app/repo.erb +10 -0
- data/lib/hanami/cli/generators/gem/app/struct.erb +10 -0
- data/lib/hanami/cli/generators/gem/app.rb +19 -0
- data/lib/hanami/cli/ruby_file_generator.rb +123 -0
- data/lib/hanami/cli/version.rb +1 -1
- metadata +39 -16
- data/lib/hanami/cli/commands/app/db/create_migration.rb +0 -32
- data/lib/hanami/cli/commands/app/db/reset.rb +0 -28
- data/lib/hanami/cli/commands/app/db/rollback.rb +0 -81
- data/lib/hanami/cli/commands/app/db/sample_data.rb +0 -42
- data/lib/hanami/cli/commands/app/db/setup.rb +0 -26
- data/lib/hanami/cli/commands/app/db/utils/database_config.rb +0 -60
- data/lib/hanami/cli/generators/app/slice/repository.erb +0 -10
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require "uri"
|
4
4
|
|
5
5
|
module Hanami
|
6
6
|
module CLI
|
@@ -9,15 +9,14 @@ module Hanami
|
|
9
9
|
module DB
|
10
10
|
module Utils
|
11
11
|
# @api private
|
12
|
+
# @since 2.2.0
|
12
13
|
class Database
|
13
|
-
|
14
|
-
|
14
|
+
MIGRATIONS_DIR = "config/db/migrate"
|
15
|
+
private_constant :MIGRATIONS_DIR
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
# @api private
|
20
|
-
SCHEME_MAP = {
|
17
|
+
DATABASE_CLASS_RESOLVER = Hash.new { |_, key|
|
18
|
+
raise "#{key} is not a supported db scheme"
|
19
|
+
}.update(
|
21
20
|
"sqlite" => -> {
|
22
21
|
require_relative("sqlite")
|
23
22
|
Sqlite
|
@@ -34,84 +33,66 @@ module Hanami
|
|
34
33
|
require_relative("mysql")
|
35
34
|
Mysql
|
36
35
|
}
|
37
|
-
|
38
|
-
|
39
|
-
# @api private
|
40
|
-
def self.[](app)
|
41
|
-
database_url =
|
42
|
-
if app.key?(:settings) && app[:settings].respond_to?(:database_url)
|
43
|
-
app[:settings].database_url
|
44
|
-
else
|
45
|
-
ENV.fetch("DATABASE_URL")
|
46
|
-
end
|
36
|
+
).freeze
|
47
37
|
|
48
|
-
|
38
|
+
def self.[](slice, system_call:)
|
39
|
+
provider = slice.container.providers[:db]
|
40
|
+
raise "this is not a db slice" unless provider
|
49
41
|
|
50
|
-
|
51
|
-
|
52
|
-
|
42
|
+
database_scheme = provider.source.database_url.then { URI(_1).scheme }
|
43
|
+
database_class = DATABASE_CLASS_RESOLVER[database_scheme].call
|
44
|
+
database_class.new(slice: slice, system_call: system_call)
|
45
|
+
end
|
46
|
+
|
47
|
+
attr_reader :slice
|
53
48
|
|
54
|
-
|
49
|
+
attr_reader :system_call
|
55
50
|
|
56
|
-
|
51
|
+
def initialize(slice:, system_call:)
|
52
|
+
@slice = slice
|
53
|
+
@system_call = system_call
|
57
54
|
end
|
58
55
|
|
59
|
-
|
60
|
-
|
61
|
-
@app = app
|
62
|
-
@config = config
|
56
|
+
def name
|
57
|
+
database_uri.path.sub(%r{^/}, "")
|
63
58
|
end
|
64
59
|
|
65
|
-
|
66
|
-
|
67
|
-
raise Hanami::CLI::NotImplementedError
|
60
|
+
def database_url
|
61
|
+
slice.container.providers[:db].source.database_url
|
68
62
|
end
|
69
63
|
|
70
|
-
|
71
|
-
|
72
|
-
raise Hanami::CLI::NotImplementedError
|
64
|
+
def database_uri
|
65
|
+
@database_uri ||= URI(database_url)
|
73
66
|
end
|
74
67
|
|
75
|
-
|
76
|
-
|
77
|
-
raise Hanami::CLI::NotImplementedError
|
68
|
+
def gateway
|
69
|
+
slice["db.config"].gateways[:default]
|
78
70
|
end
|
79
71
|
|
80
|
-
|
81
|
-
|
82
|
-
raise Hanami::CLI::NotImplementedError
|
72
|
+
def connection
|
73
|
+
gateway.connection
|
83
74
|
end
|
84
75
|
|
85
|
-
|
86
|
-
|
87
|
-
app.root
|
76
|
+
def exec_create_command
|
77
|
+
raise Hanami::CLI::NotImplementedError
|
88
78
|
end
|
89
79
|
|
90
|
-
|
91
|
-
|
92
|
-
@rom_config ||=
|
93
|
-
begin
|
94
|
-
app.prepare(:persistence)
|
95
|
-
app.container["persistence.config"]
|
96
|
-
end
|
80
|
+
def exec_drop_command
|
81
|
+
raise Hanami::CLI::NotImplementedError
|
97
82
|
end
|
98
83
|
|
99
|
-
|
100
|
-
|
101
|
-
config.db_name
|
84
|
+
def exists?
|
85
|
+
raise Hanami::CLI::NotImplementedError
|
102
86
|
end
|
103
87
|
|
104
|
-
|
105
|
-
|
106
|
-
rom_config.gateways[:default]
|
88
|
+
def exec_dump_command
|
89
|
+
raise Hanami::CLI::NotImplementedError
|
107
90
|
end
|
108
91
|
|
109
|
-
|
110
|
-
|
111
|
-
gateway.connection
|
92
|
+
def exec_load_command
|
93
|
+
raise Hanami::CLI::NotImplementedError
|
112
94
|
end
|
113
95
|
|
114
|
-
# @api private
|
115
96
|
def run_migrations(**options)
|
116
97
|
require "rom/sql"
|
117
98
|
ROM::SQL.with_gateway(gateway) do
|
@@ -119,24 +100,19 @@ module Hanami
|
|
119
100
|
end
|
120
101
|
end
|
121
102
|
|
122
|
-
# @api private
|
123
103
|
def migrator
|
124
|
-
@migrator ||=
|
125
|
-
|
126
|
-
require "rom/sql"
|
127
|
-
ROM::SQL::Migration::Migrator.new(connection, path: File.join(root_path, "db/migrate"))
|
128
|
-
end
|
129
|
-
end
|
104
|
+
@migrator ||= begin
|
105
|
+
slice.prepare :db
|
130
106
|
|
131
|
-
|
132
|
-
|
133
|
-
|
107
|
+
require "rom/sql"
|
108
|
+
ROM::SQL::Migration::Migrator.new(connection, path: migrations_path)
|
109
|
+
end
|
134
110
|
end
|
135
111
|
|
136
|
-
private
|
137
|
-
|
138
112
|
def sequel_migrator
|
139
113
|
@sequel_migrator ||= begin
|
114
|
+
slice.prepare :db
|
115
|
+
|
140
116
|
require "sequel"
|
141
117
|
Sequel.extension :migration
|
142
118
|
|
@@ -147,8 +123,27 @@ module Hanami
|
|
147
123
|
end
|
148
124
|
end
|
149
125
|
|
126
|
+
def applied_migrations
|
127
|
+
sequel_migrator.applied_migrations
|
128
|
+
end
|
129
|
+
|
150
130
|
def migrations_path
|
151
|
-
|
131
|
+
slice.root.join(MIGRATIONS_DIR)
|
132
|
+
end
|
133
|
+
|
134
|
+
def migrations_dir?
|
135
|
+
migrations_path.directory?
|
136
|
+
end
|
137
|
+
|
138
|
+
def structure_file
|
139
|
+
slice.root.join("config/db/structure.sql")
|
140
|
+
end
|
141
|
+
|
142
|
+
def schema_migrations_sql_dump
|
143
|
+
sql = +"INSERT INTO schema_migrations (filename) VALUES\n"
|
144
|
+
sql << applied_migrations.map { |v| "('#{v}')" }.join(",\n")
|
145
|
+
sql << ";"
|
146
|
+
sql
|
152
147
|
end
|
153
148
|
end
|
154
149
|
end
|
@@ -16,12 +16,12 @@ module Hanami
|
|
16
16
|
end
|
17
17
|
|
18
18
|
# @api private
|
19
|
-
def
|
19
|
+
def exec_dump_command
|
20
20
|
raise Hanami::CLI::NotImplementedError
|
21
21
|
end
|
22
22
|
|
23
23
|
# @api private
|
24
|
-
def
|
24
|
+
def exec_load_command
|
25
25
|
raise Hanami::CLI::NotImplementedError
|
26
26
|
end
|
27
27
|
end
|
@@ -11,49 +11,68 @@ module Hanami
|
|
11
11
|
module DB
|
12
12
|
module Utils
|
13
13
|
# @api private
|
14
|
+
# @since 2.2.0
|
14
15
|
class Postgres < Database
|
15
16
|
# @api private
|
16
|
-
|
17
|
-
|
17
|
+
# @since 2.2.0
|
18
|
+
def exec_create_command
|
19
|
+
return true if exists?
|
18
20
|
|
19
|
-
|
20
|
-
|
21
|
-
system(cli_env_vars, "createdb #{escaped_name}")
|
21
|
+
system_call.call("createdb #{escaped_name}", env: cli_env_vars)
|
22
22
|
end
|
23
23
|
|
24
24
|
# @api private
|
25
|
-
|
26
|
-
|
25
|
+
# @since 2.2.0
|
26
|
+
def exec_drop_command
|
27
|
+
return true unless exists?
|
28
|
+
|
29
|
+
system_call.call("dropdb #{escaped_name}", env: cli_env_vars)
|
27
30
|
end
|
28
31
|
|
29
32
|
# @api private
|
30
|
-
|
31
|
-
|
33
|
+
# @since 2.2.0
|
34
|
+
def exists?
|
35
|
+
result = system_call.call("psql -t -A -c '\\list #{escaped_name}'", env: cli_env_vars)
|
36
|
+
result.successful? && result.out.include?("#{name}|") # start_with?
|
32
37
|
end
|
33
38
|
|
34
39
|
# @api private
|
35
|
-
|
36
|
-
|
40
|
+
# @since 2.2.0
|
41
|
+
def exec_dump_command
|
42
|
+
system_call.call(
|
43
|
+
"pg_dump --schema-only --no-privileges --no-owner --file #{structure_file} #{escaped_name}",
|
44
|
+
env: cli_env_vars
|
45
|
+
)
|
37
46
|
end
|
38
47
|
|
39
48
|
# @api private
|
49
|
+
# @since 2.2.0
|
50
|
+
def exec_load_command
|
51
|
+
system_call.call(
|
52
|
+
"psql --set ON_ERROR_STOP=1 --quiet --no-psqlrc --output #{File::NULL} --file #{structure_file} #{escaped_name}",
|
53
|
+
env: cli_env_vars
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
40
57
|
def escaped_name
|
41
58
|
Shellwords.escape(name)
|
42
59
|
end
|
43
60
|
|
44
|
-
# @api private
|
45
61
|
def cli_env_vars
|
46
62
|
@cli_env_vars ||= {}.tap do |vars|
|
47
|
-
vars["PGHOST"] =
|
48
|
-
vars["PGPORT"] =
|
49
|
-
vars["PGUSER"] =
|
50
|
-
vars["PGPASSWORD"] =
|
63
|
+
vars["PGHOST"] = database_uri.host.to_s if database_uri.host
|
64
|
+
vars["PGPORT"] = database_uri.port.to_s if database_uri.port
|
65
|
+
vars["PGUSER"] = database_uri.user.to_s if database_uri.user
|
66
|
+
vars["PGPASSWORD"] = database_uri.password.to_s if database_uri.password
|
51
67
|
end
|
52
68
|
end
|
53
69
|
|
54
|
-
|
55
|
-
|
56
|
-
|
70
|
+
def schema_migrations_sql_dump
|
71
|
+
search_path = slice["db.gateway"].connection
|
72
|
+
.fetch("SHOW search_path").to_a.first
|
73
|
+
.fetch(:search_path)
|
74
|
+
|
75
|
+
+"SET search_path TO #{search_path};\n\n" << super
|
57
76
|
end
|
58
77
|
end
|
59
78
|
end
|
@@ -9,32 +9,80 @@ module Hanami
|
|
9
9
|
module DB
|
10
10
|
module Utils
|
11
11
|
# @api private
|
12
|
+
# @since 2.2.0
|
12
13
|
class Sqlite < Database
|
13
14
|
# @api private
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
# @since 2.2.0
|
16
|
+
Failure = Struct.new(:err) do
|
17
|
+
def successful?
|
18
|
+
false
|
19
|
+
end
|
20
|
+
|
21
|
+
def exit_code
|
22
|
+
1
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# @api private
|
27
|
+
# @since 2.2.0
|
28
|
+
def exec_create_command
|
29
|
+
return true if exists?
|
30
|
+
|
31
|
+
FileUtils.mkdir_p(File.dirname(file_path))
|
32
|
+
|
33
|
+
system_call.call(%(sqlite3 #{file_path} "VACUUM;"))
|
17
34
|
end
|
18
35
|
|
19
36
|
# @api private
|
20
|
-
|
21
|
-
|
37
|
+
# @since 2.2.0
|
38
|
+
def exec_drop_command
|
39
|
+
begin
|
40
|
+
File.unlink(file_path) if exists?
|
41
|
+
rescue => e
|
42
|
+
# Mimic a system_call result
|
43
|
+
return Failure.new(e.message)
|
44
|
+
end
|
45
|
+
|
22
46
|
true
|
23
47
|
end
|
24
48
|
|
25
49
|
# @api private
|
26
|
-
|
27
|
-
|
50
|
+
# @since 2.2.0
|
51
|
+
def exists?
|
52
|
+
File.exist?(file_path)
|
53
|
+
end
|
54
|
+
|
55
|
+
# @api private
|
56
|
+
# @since 2.2.0
|
57
|
+
def exec_dump_command
|
58
|
+
system_call.call(%(sqlite3 #{file_path} ".schema --indent --nosys" > #{structure_file}))
|
28
59
|
end
|
29
60
|
|
30
61
|
# @api private
|
31
|
-
|
32
|
-
|
62
|
+
# @since 2.2.0
|
63
|
+
def exec_load_command
|
64
|
+
system_call.call("sqlite3 #{file_path} < #{structure_file}")
|
33
65
|
end
|
34
66
|
|
35
67
|
# @api private
|
68
|
+
# @since 2.2.0
|
69
|
+
def name
|
70
|
+
# Sequel expects sqlite:// URIs to operate the same as file:// URIs: 2 slashes for
|
71
|
+
# a relative path, 3 for an absolute path. In the case of 2 slashes, the first part
|
72
|
+
# of the path is considered by Ruby's `URI` as the `#host`.
|
73
|
+
@name ||= "#{database_uri.host}#{database_uri.path}"
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
36
78
|
def file_path
|
37
|
-
@file_path ||=
|
79
|
+
@file_path ||= begin
|
80
|
+
if File.absolute_path?(name)
|
81
|
+
name
|
82
|
+
else
|
83
|
+
slice.app.root.join(name).to_s
|
84
|
+
end
|
85
|
+
end
|
38
86
|
end
|
39
87
|
end
|
40
88
|
end
|
@@ -1,24 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "../../app/command"
|
4
|
-
|
5
3
|
module Hanami
|
6
4
|
module CLI
|
7
5
|
module Commands
|
8
6
|
module App
|
9
7
|
module DB
|
10
8
|
# @api private
|
11
|
-
class Version <
|
9
|
+
class Version < DB::Command
|
12
10
|
desc "Print schema version"
|
13
11
|
|
14
|
-
option :target, desc: "Target migration number", aliases: ["-t"]
|
15
|
-
|
16
12
|
# @api private
|
17
|
-
def call(
|
18
|
-
|
19
|
-
|
13
|
+
def call(app: false, slice: nil, **)
|
14
|
+
databases(app: app, slice: slice).each do |database|
|
15
|
+
unless database.migrations_dir?
|
16
|
+
out.puts "=> Cannot find version for slice #{database.slice.slice_name.to_s.inspect}: missing config/db/migrate/ dir"
|
17
|
+
return
|
18
|
+
end
|
19
|
+
|
20
|
+
migration = database.applied_migrations.last
|
21
|
+
version = migration ? File.basename(migration, ".*") : "not available"
|
20
22
|
|
21
|
-
|
23
|
+
out.puts "=> #{database.name} current schema version is #{version}"
|
24
|
+
end
|
22
25
|
end
|
23
26
|
end
|
24
27
|
end
|
@@ -32,13 +32,13 @@ module Hanami
|
|
32
32
|
option \
|
33
33
|
:skip_view,
|
34
34
|
required: false,
|
35
|
-
type: :
|
35
|
+
type: :flag,
|
36
36
|
default: DEFAULT_SKIP_VIEW,
|
37
37
|
desc: "Skip view and template generation"
|
38
38
|
option \
|
39
39
|
:skip_tests,
|
40
40
|
required: false,
|
41
|
-
type: :
|
41
|
+
type: :flag,
|
42
42
|
default: DEFAULT_SKIP_TESTS,
|
43
43
|
desc: "Skip test generation"
|
44
44
|
option :slice, required: false, desc: "Slice name"
|
@@ -85,7 +85,8 @@ module Hanami
|
|
85
85
|
skip_view: DEFAULT_SKIP_VIEW,
|
86
86
|
skip_tests: DEFAULT_SKIP_TESTS, # rubocop:disable Lint/UnusedMethodArgument
|
87
87
|
slice: nil,
|
88
|
-
context: nil,
|
88
|
+
context: nil,
|
89
|
+
**
|
89
90
|
)
|
90
91
|
slice = inflector.underscore(Shellwords.shellescape(slice)) if slice
|
91
92
|
name = naming.action_name(name)
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/inflector"
|
4
|
+
require "dry/files"
|
5
|
+
require "shellwords"
|
6
|
+
require_relative "../../../naming"
|
7
|
+
require_relative "../../../errors"
|
8
|
+
|
9
|
+
module Hanami
|
10
|
+
module CLI
|
11
|
+
module Commands
|
12
|
+
module App
|
13
|
+
module Generate
|
14
|
+
# @since 2.2.0
|
15
|
+
# @api private
|
16
|
+
class Command < App::Command
|
17
|
+
option :slice, required: false, desc: "Slice name"
|
18
|
+
|
19
|
+
attr_reader :generator
|
20
|
+
private :generator
|
21
|
+
|
22
|
+
# @since 2.2.0
|
23
|
+
# @api private
|
24
|
+
def initialize(
|
25
|
+
fs:,
|
26
|
+
inflector:,
|
27
|
+
**opts
|
28
|
+
)
|
29
|
+
super
|
30
|
+
@generator = generator_class.new(fs: fs, inflector: inflector, out: out)
|
31
|
+
end
|
32
|
+
|
33
|
+
def generator_class
|
34
|
+
# Must be implemented by subclasses, with class that takes:
|
35
|
+
# fs:, inflector:, out:
|
36
|
+
end
|
37
|
+
|
38
|
+
# @since 2.2.0
|
39
|
+
# @api private
|
40
|
+
def call(name:, slice: nil, **)
|
41
|
+
normalized_slice = inflector.underscore(slice) if slice
|
42
|
+
generator.call(app.namespace, name, normalized_slice)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/inflector"
|
4
|
+
require "dry/files"
|
5
|
+
require "shellwords"
|
6
|
+
module Hanami
|
7
|
+
module CLI
|
8
|
+
module Commands
|
9
|
+
module App
|
10
|
+
module Generate
|
11
|
+
# @api private
|
12
|
+
# @since 2.2.0
|
13
|
+
class Component < App::Command
|
14
|
+
argument :name, required: true, desc: "Component name"
|
15
|
+
option :slice, required: false, desc: "Slice name"
|
16
|
+
|
17
|
+
example [
|
18
|
+
%(operations.create_user (MyApp::Operations::CreateUser)),
|
19
|
+
%(operations.user.create (MyApp::Operations::Create::User)),
|
20
|
+
%(operations.create_user --slice=admin (Admin::Operations::CreateUser)),
|
21
|
+
%(Operations::CreateUser (MyApp::Operations::CreateUser)),
|
22
|
+
]
|
23
|
+
attr_reader :generator
|
24
|
+
private :generator
|
25
|
+
|
26
|
+
# @api private
|
27
|
+
# @since 2.2.0
|
28
|
+
def initialize(
|
29
|
+
fs:, inflector:,
|
30
|
+
generator: Generators::App::Component.new(fs: fs, inflector: inflector),
|
31
|
+
**opts
|
32
|
+
)
|
33
|
+
@generator = generator
|
34
|
+
super(fs: fs, inflector: inflector, **opts)
|
35
|
+
end
|
36
|
+
|
37
|
+
# @api private
|
38
|
+
# @since 2.2.0
|
39
|
+
def call(name:, slice: nil, **)
|
40
|
+
slice = inflector.underscore(Shellwords.shellescape(slice)) if slice
|
41
|
+
|
42
|
+
generator.call(app.namespace, name, slice)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hanami
|
4
|
+
module CLI
|
5
|
+
module Commands
|
6
|
+
module App
|
7
|
+
module Generate
|
8
|
+
# @since 2.2.0
|
9
|
+
# @api private
|
10
|
+
class Migration < Command
|
11
|
+
argument :name, required: true, desc: "Migration name"
|
12
|
+
|
13
|
+
example [
|
14
|
+
%(create_posts),
|
15
|
+
%(add_published_at_to_posts),
|
16
|
+
%(create_users --slice=admin),
|
17
|
+
]
|
18
|
+
|
19
|
+
def generator_class
|
20
|
+
Generators::App::Migration
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hanami
|
4
|
+
module CLI
|
5
|
+
module Commands
|
6
|
+
module App
|
7
|
+
module Generate
|
8
|
+
# @since 2.2.0
|
9
|
+
# @api private
|
10
|
+
class Operation < Command
|
11
|
+
argument :name, required: true, desc: "Operation name"
|
12
|
+
|
13
|
+
example [
|
14
|
+
%(books.add (MyApp::Books::Add)),
|
15
|
+
%(books.add --slice=admin (Admin::Books::Add)),
|
16
|
+
]
|
17
|
+
|
18
|
+
def generator_class
|
19
|
+
Generators::App::Operation
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hanami
|
4
|
+
module CLI
|
5
|
+
module Commands
|
6
|
+
module App
|
7
|
+
module Generate
|
8
|
+
# @since 2.2.0
|
9
|
+
# @api private
|
10
|
+
class Relation < Command
|
11
|
+
argument :name, required: true, desc: "Relation name"
|
12
|
+
|
13
|
+
example [
|
14
|
+
%(books (MyApp::Relation::Book)),
|
15
|
+
%(books/drafts (MyApp::Relations::Books::Drafts)),
|
16
|
+
%(books --slice=admin (Admin::Relations::Books)),
|
17
|
+
]
|
18
|
+
|
19
|
+
# @since 2.2.0
|
20
|
+
# @api private
|
21
|
+
def generator_class
|
22
|
+
Generators::App::Relation
|
23
|
+
end
|
24
|
+
|
25
|
+
# @since 2.2.0
|
26
|
+
# @api private
|
27
|
+
def call(name:, slice: nil, **opts)
|
28
|
+
super(name: name, slice: slice, **opts)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|