hanami-cli 2.1.1 → 2.2.0.beta2
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 +4 -4
- data/.github/workflows/ci.yml +29 -13
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +28 -0
- data/Gemfile +6 -1
- data/README.md +13 -7
- data/docker-compose.yml +14 -0
- data/hanami-cli.gemspec +2 -2
- data/lib/hanami/cli/command.rb +2 -2
- data/lib/hanami/cli/commands/app/command.rb +2 -16
- data/lib/hanami/cli/commands/app/db/command.rb +148 -0
- data/lib/hanami/cli/commands/app/db/create.rb +21 -11
- data/lib/hanami/cli/commands/app/db/drop.rb +21 -10
- data/lib/hanami/cli/commands/app/db/migrate.rb +50 -12
- data/lib/hanami/cli/commands/app/db/prepare.rb +68 -0
- data/lib/hanami/cli/commands/app/db/seed.rb +22 -21
- data/lib/hanami/cli/commands/app/db/structure/dump.rb +32 -7
- data/lib/hanami/cli/commands/app/db/structure/load.rb +54 -0
- data/lib/hanami/cli/commands/app/db/utils/database.rb +100 -75
- data/lib/hanami/cli/commands/app/db/utils/mysql.rb +59 -6
- data/lib/hanami/cli/commands/app/db/utils/postgres.rb +42 -21
- data/lib/hanami/cli/commands/app/db/utils/sqlite.rb +58 -10
- data/lib/hanami/cli/commands/app/db/version.rb +14 -8
- data/lib/hanami/cli/commands/app/generate/action.rb +4 -3
- data/lib/hanami/cli/commands/app/generate/command.rb +60 -0
- data/lib/hanami/cli/commands/app/generate/component.rb +49 -0
- data/lib/hanami/cli/commands/app/generate/migration.rb +47 -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 +29 -0
- data/lib/hanami/cli/commands/app/generate/repo.rb +42 -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/files.rb +22 -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 +66 -0
- data/lib/hanami/cli/generators/app/operation.rb +49 -0
- data/lib/hanami/cli/generators/app/part_context.rb +5 -21
- data/lib/hanami/cli/generators/app/relation.rb +45 -0
- data/lib/hanami/cli/generators/app/repo.rb +41 -0
- data/lib/hanami/cli/generators/app/ruby_file_writer.rb +151 -0
- data/lib/hanami/cli/generators/app/slice/{entities.erb → operation.erb} +1 -3
- 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 +40 -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 +4 -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 -17
- 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,24 +1,30 @@
|
|
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 :
|
12
|
+
option :gateway, required: false, desc: "Use database for gateway"
|
15
13
|
|
16
14
|
# @api private
|
17
|
-
def call(
|
18
|
-
|
19
|
-
|
15
|
+
def call(app: false, slice: nil, gateway: nil, **)
|
16
|
+
databases(app: app, slice: slice, gateway: gateway).each do |database|
|
17
|
+
unless database.migrations_dir?
|
18
|
+
relative_migrations_path = database.migrations_path.relative_path_from(database.slice.app.root)
|
19
|
+
out.puts "=> Cannot find version for database #{database.name}: no migrations directory at #{relative_migrations_path}/"
|
20
|
+
return
|
21
|
+
end
|
22
|
+
|
23
|
+
migration = database.applied_migrations.last
|
24
|
+
version = migration ? File.basename(migration, ".*") : "not available"
|
20
25
|
|
21
|
-
|
26
|
+
out.puts "=> #{database.name} current schema version is #{version}"
|
27
|
+
end
|
22
28
|
end
|
23
29
|
end
|
24
30
|
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,60 @@
|
|
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
|
+
if slice
|
42
|
+
generator.call(
|
43
|
+
key: name,
|
44
|
+
namespace: slice,
|
45
|
+
base_path: fs.join("slices", inflector.underscore(slice))
|
46
|
+
)
|
47
|
+
else
|
48
|
+
generator.call(
|
49
|
+
key: name,
|
50
|
+
namespace: app.namespace,
|
51
|
+
base_path: "app"
|
52
|
+
)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
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,47 @@
|
|
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
|
+
option :gateway, desc: "Generate migration for gateway"
|
13
|
+
|
14
|
+
example [
|
15
|
+
%(create_posts),
|
16
|
+
%(add_published_at_to_posts),
|
17
|
+
%(create_users --slice=admin),
|
18
|
+
%(create_comments --slice=admin --gateway=extra),
|
19
|
+
]
|
20
|
+
|
21
|
+
def generator_class
|
22
|
+
Generators::App::Migration
|
23
|
+
end
|
24
|
+
|
25
|
+
def call(name:, slice: nil, gateway: nil)
|
26
|
+
if slice
|
27
|
+
generator.call(
|
28
|
+
key: name,
|
29
|
+
namespace: slice,
|
30
|
+
base_path: fs.join("slices", inflector.underscore(slice)),
|
31
|
+
gateway: gateway
|
32
|
+
)
|
33
|
+
else
|
34
|
+
generator.call(
|
35
|
+
key: name,
|
36
|
+
namespace: app.namespace,
|
37
|
+
base_path: "app",
|
38
|
+
gateway: gateway
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
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,29 @@
|
|
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
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,42 @@
|
|
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 Repo < Command
|
17
|
+
argument :name, required: true, desc: "Repo name"
|
18
|
+
|
19
|
+
example [
|
20
|
+
%(books (MyApp::Repos::BooksRepo)),
|
21
|
+
%(books/drafts_repo (MyApp::Repos::Books::DraftsRepo)),
|
22
|
+
%(books --slice=admin (Admin::Repos::BooksRepo)),
|
23
|
+
]
|
24
|
+
|
25
|
+
# @since 2.2.0
|
26
|
+
# @api private
|
27
|
+
def generator_class
|
28
|
+
Generators::App::Repo
|
29
|
+
end
|
30
|
+
|
31
|
+
# @since 2.2.0
|
32
|
+
# @api private
|
33
|
+
def call(name:, **opts)
|
34
|
+
name = "#{inflector.singularize(name)}_repo" unless name.end_with?("_repo")
|
35
|
+
super
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -12,10 +12,23 @@ module Hanami
|
|
12
12
|
module Generate
|
13
13
|
# @since 2.0.0
|
14
14
|
# @api private
|
15
|
-
class Slice < Command
|
15
|
+
class Slice < App::Command
|
16
16
|
argument :name, required: true, desc: "The slice name"
|
17
17
|
option :url, required: false, type: :string, desc: "The slice URL prefix"
|
18
18
|
|
19
|
+
# @since 2.2.0
|
20
|
+
# @api private
|
21
|
+
SKIP_DB_DEFAULT = false
|
22
|
+
private_constant :SKIP_DB_DEFAULT
|
23
|
+
|
24
|
+
# @since 2.2.0
|
25
|
+
# @api private
|
26
|
+
option :skip_db,
|
27
|
+
type: :boolean,
|
28
|
+
required: false,
|
29
|
+
default: SKIP_DB_DEFAULT,
|
30
|
+
desc: "Skip database"
|
31
|
+
|
19
32
|
example [
|
20
33
|
"admin # Admin slice (/admin URL prefix)",
|
21
34
|
"users --url=/u # Users slice (/u URL prefix)",
|
@@ -34,14 +47,18 @@ module Hanami
|
|
34
47
|
|
35
48
|
# @since 2.0.0
|
36
49
|
# @api private
|
37
|
-
def call(
|
50
|
+
def call(
|
51
|
+
name:,
|
52
|
+
url: nil,
|
53
|
+
skip_db: SKIP_DB_DEFAULT
|
54
|
+
)
|
38
55
|
require "hanami/setup"
|
39
56
|
|
40
57
|
app = inflector.underscore(Hanami.app.namespace)
|
41
58
|
name = inflector.underscore(Shellwords.shellescape(name))
|
42
59
|
url = sanitize_url_prefix(name, url)
|
43
60
|
|
44
|
-
generator.call(app, name, url)
|
61
|
+
generator.call(app, name, url, skip_db: skip_db)
|
45
62
|
end
|
46
63
|
|
47
64
|
private
|
@@ -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 Struct < Command
|
11
|
+
argument :name, required: true, desc: "Struct name"
|
12
|
+
|
13
|
+
example [
|
14
|
+
%(book (MyApp::Structs::Book)),
|
15
|
+
%(book/published_book (MyApp::Structs::Book::PublishedBook)),
|
16
|
+
%(book --slice=admin (Admin::Structs::Book)),
|
17
|
+
]
|
18
|
+
|
19
|
+
def generator_class
|
20
|
+
Generators::App::Struct
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -34,7 +34,7 @@ module Hanami
|
|
34
34
|
private_constant :DEFAULT_WITH_ARGUMENTS
|
35
35
|
|
36
36
|
option :with_arguments, default: DEFAULT_WITH_ARGUMENTS, required: false,
|
37
|
-
desc: "Include inspected arguments", type: :
|
37
|
+
desc: "Include inspected arguments", type: :flag
|
38
38
|
|
39
39
|
example [
|
40
40
|
"middleware # Print app Rack middleware stack",
|
@@ -41,8 +41,8 @@ module Hanami
|
|
41
41
|
option :port, default: Hanami::Port::DEFAULT, required: false,
|
42
42
|
desc: "The port to run the server on (falls back to the rack handler)"
|
43
43
|
option :config, default: DEFAULT_CONFIG_PATH, required: false, desc: "Rack configuration file"
|
44
|
-
option :debug, default: false, required: false, desc: "Turn on/off debug output", type: :
|
45
|
-
option :warn, default: false, required: false, desc: "Turn on/off warnings", type: :
|
44
|
+
option :debug, default: false, required: false, desc: "Turn on/off debug output", type: :flag
|
45
|
+
option :warn, default: false, required: false, desc: "Turn on/off warnings", type: :flag
|
46
46
|
|
47
47
|
# @since 2.0.0
|
48
48
|
# @api private
|
@@ -27,11 +27,30 @@ module Hanami
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
+
if Hanami.bundled?("hanami-db")
|
31
|
+
register "db" do |db|
|
32
|
+
db.register "create", DB::Create
|
33
|
+
db.register "drop", DB::Drop
|
34
|
+
db.register "migrate", DB::Migrate
|
35
|
+
db.register "structure dump", DB::Structure::Dump
|
36
|
+
db.register "structure load", DB::Structure::Load
|
37
|
+
db.register "seed", DB::Seed
|
38
|
+
db.register "prepare", DB::Prepare
|
39
|
+
db.register "version", DB::Version
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
30
43
|
register "generate", aliases: ["g"] do |prefix|
|
31
|
-
prefix.register "slice", Generate::Slice
|
32
44
|
prefix.register "action", Generate::Action
|
33
|
-
prefix.register "
|
45
|
+
prefix.register "component", Generate::Component
|
46
|
+
prefix.register "migration", Generate::Migration
|
47
|
+
prefix.register "operation", Generate::Operation
|
34
48
|
prefix.register "part", Generate::Part
|
49
|
+
prefix.register "relation", Generate::Relation
|
50
|
+
prefix.register "repo", Generate::Repo
|
51
|
+
prefix.register "slice", Generate::Slice
|
52
|
+
prefix.register "struct", Generate::Struct
|
53
|
+
prefix.register "view", Generate::View
|
35
54
|
end
|
36
55
|
end
|
37
56
|
end
|
@@ -25,6 +25,27 @@ module Hanami
|
|
25
25
|
SKIP_ASSETS_DEFAULT = false
|
26
26
|
private_constant :SKIP_ASSETS_DEFAULT
|
27
27
|
|
28
|
+
# @since 2.2.0
|
29
|
+
# @api private
|
30
|
+
SKIP_DB_DEFAULT = false
|
31
|
+
private_constant :SKIP_DB_DEFAULT
|
32
|
+
|
33
|
+
# @since 2.2.0
|
34
|
+
# @api private
|
35
|
+
DATABASE_SQLITE = "sqlite"
|
36
|
+
|
37
|
+
# @since 2.2.0
|
38
|
+
# @api private
|
39
|
+
DATABASE_POSTGRES = "postgres"
|
40
|
+
|
41
|
+
# @since 2.2.0
|
42
|
+
# @api private
|
43
|
+
DATABASE_MYSQL = "mysql"
|
44
|
+
|
45
|
+
# @since 2.2.0
|
46
|
+
# @api private
|
47
|
+
SUPPORTED_DATABASES = [DATABASE_SQLITE, DATABASE_POSTGRES, DATABASE_MYSQL].freeze
|
48
|
+
|
28
49
|
desc "Generate a new Hanami app"
|
29
50
|
|
30
51
|
# @since 2.0.0
|
@@ -33,28 +54,42 @@ module Hanami
|
|
33
54
|
|
34
55
|
# @since 2.0.0
|
35
56
|
# @api private
|
36
|
-
option :skip_install, type: :
|
57
|
+
option :skip_install, type: :flag, required: false,
|
37
58
|
default: SKIP_INSTALL_DEFAULT,
|
38
59
|
desc: "Skip app installation (Bundler, third-party Hanami plugins)"
|
39
60
|
|
40
61
|
# @since 2.1.0
|
41
62
|
# @api private
|
42
|
-
option :head, type: :
|
63
|
+
option :head, type: :flag, required: false,
|
43
64
|
default: HEAD_DEFAULT,
|
44
65
|
desc: "Use Hanami HEAD version (from GitHub `main` branches)"
|
45
66
|
|
46
67
|
# @since 2.1.0
|
47
68
|
# @api private
|
48
|
-
option :skip_assets, type: :
|
69
|
+
option :skip_assets, type: :flag, required: false,
|
49
70
|
default: SKIP_ASSETS_DEFAULT,
|
50
|
-
desc: "Skip assets"
|
71
|
+
desc: "Skip including hanami-assets"
|
72
|
+
|
73
|
+
# @since 2.2.0
|
74
|
+
# @api private
|
75
|
+
option :skip_db, type: :flag, required: false,
|
76
|
+
default: SKIP_DB_DEFAULT,
|
77
|
+
desc: "Skip including hanami-db"
|
78
|
+
|
79
|
+
# @since 2.2.0
|
80
|
+
# @api private
|
81
|
+
option :database, type: :string, required: false,
|
82
|
+
default: DATABASE_SQLITE,
|
83
|
+
desc: "Database adapter (supported: sqlite, mysql, postgres)"
|
51
84
|
|
52
85
|
# rubocop:disable Layout/LineLength
|
53
86
|
example [
|
54
|
-
"bookshelf
|
55
|
-
"bookshelf --head
|
56
|
-
"bookshelf --skip-install
|
57
|
-
"bookshelf --skip-assets
|
87
|
+
"bookshelf # Generate a new Hanami app in `bookshelf/' directory, using `Bookshelf' namespace",
|
88
|
+
"bookshelf --head # Generate a new Hanami app, using Hanami HEAD version from GitHub `main' branches",
|
89
|
+
"bookshelf --skip-install # Generate a new Hanami app, but it skips Hanami installation",
|
90
|
+
"bookshelf --skip-assets # Generate a new Hanami app without hanmai-assets",
|
91
|
+
"bookshelf --skip-db # Generate a new Hanami app without hanami-db",
|
92
|
+
"bookshelf --database={sqlite|postgres|mysql} # Generate a new Hanami app with a specified database (default: sqlite)",
|
58
93
|
]
|
59
94
|
# rubocop:enable Layout/LineLength
|
60
95
|
|
@@ -75,20 +110,36 @@ module Hanami
|
|
75
110
|
@system_call = system_call
|
76
111
|
end
|
77
112
|
|
78
|
-
# rubocop:
|
79
|
-
|
80
|
-
# rubocop:disable Metrics/AbcSize
|
113
|
+
# rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
|
81
114
|
|
82
115
|
# @since 2.0.0
|
83
116
|
# @api private
|
84
|
-
def call(
|
117
|
+
def call(
|
118
|
+
app:,
|
119
|
+
head: HEAD_DEFAULT,
|
120
|
+
skip_install: SKIP_INSTALL_DEFAULT,
|
121
|
+
skip_assets: SKIP_ASSETS_DEFAULT,
|
122
|
+
skip_db: SKIP_DB_DEFAULT,
|
123
|
+
database: nil
|
124
|
+
)
|
125
|
+
# rubocop:enable Metrics/ParameterLists
|
85
126
|
app = inflector.underscore(app)
|
86
127
|
|
87
128
|
raise PathAlreadyExistsError.new(app) if fs.exist?(app)
|
129
|
+
raise ConflictingOptionsError.new("--skip-db", "--database") if skip_db && database
|
130
|
+
|
131
|
+
normalized_database ||= normalize_database(database)
|
88
132
|
|
89
133
|
fs.mkdir(app)
|
90
134
|
fs.chdir(app) do
|
91
|
-
context = Generators::Context.new(
|
135
|
+
context = Generators::Context.new(
|
136
|
+
inflector,
|
137
|
+
app,
|
138
|
+
head: head,
|
139
|
+
skip_assets: skip_assets,
|
140
|
+
skip_db: skip_db,
|
141
|
+
database: normalized_database
|
142
|
+
)
|
92
143
|
generator.call(app, context: context) do
|
93
144
|
if skip_install
|
94
145
|
out.puts "Skipping installation, please enter `#{app}' directory and run `bundle exec hanami install'"
|
@@ -112,7 +163,7 @@ module Hanami
|
|
112
163
|
end
|
113
164
|
end
|
114
165
|
end
|
115
|
-
# rubocop:enable Metrics/AbcSize
|
166
|
+
# rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity
|
116
167
|
|
117
168
|
private
|
118
169
|
|
@@ -120,6 +171,19 @@ module Hanami
|
|
120
171
|
attr_reader :generator
|
121
172
|
attr_reader :system_call
|
122
173
|
|
174
|
+
def normalize_database(database)
|
175
|
+
case database
|
176
|
+
when nil, "sqlite", "sqlite3"
|
177
|
+
DATABASE_SQLITE
|
178
|
+
when "mysql", "mysql2"
|
179
|
+
DATABASE_MYSQL
|
180
|
+
when "postgres", "postgresql", "pg"
|
181
|
+
DATABASE_POSTGRES
|
182
|
+
else
|
183
|
+
raise DatabaseNotSupportedError.new(database, SUPPORTED_DATABASES)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
123
187
|
def run_install_command!(head:)
|
124
188
|
head_flag = head ? " --head" : ""
|
125
189
|
bundler.exec("hanami install#{head_flag}").tap do |result|
|
data/lib/hanami/cli/errors.rb
CHANGED
@@ -91,5 +91,33 @@ module Hanami
|
|
91
91
|
super("`#{scheme}' is not a supported db scheme")
|
92
92
|
end
|
93
93
|
end
|
94
|
+
|
95
|
+
# @since 2.2.0
|
96
|
+
# @api public
|
97
|
+
class DatabaseNotSupportedError < Error
|
98
|
+
def initialize(invalid_database, supported_databases)
|
99
|
+
super("`#{invalid_database}' is not a supported database. Supported databases are: #{supported_databases.join(', ')}")
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# @since 2.2.0
|
104
|
+
# @api public
|
105
|
+
class ConflictingOptionsError < Error
|
106
|
+
def initialize(option1, option2)
|
107
|
+
super("`#{option1}' and `#{option2}' cannot be used together")
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# @since 2.2.0
|
112
|
+
# @api public
|
113
|
+
class InvalidMigrationNameError < Error
|
114
|
+
def initialize(name)
|
115
|
+
super(<<~TEXT)
|
116
|
+
Invalid migration name: #{name}
|
117
|
+
|
118
|
+
Name must contain only letters, numbers, and underscores.
|
119
|
+
TEXT
|
120
|
+
end
|
121
|
+
end
|
94
122
|
end
|
95
123
|
end
|