hanami-cli 2.0.0 → 2.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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +2 -0
- data/{.action_hero.yml → .repobot.yml} +1 -3
- data/.rubocop.yml +6 -0
- data/CHANGELOG.md +115 -0
- data/Gemfile +7 -2
- data/LICENSE.md +1 -1
- data/README.md +1 -1
- data/hanami-cli.gemspec +1 -1
- data/lib/hanami/cli/bundler.rb +26 -9
- data/lib/hanami/cli/command.rb +28 -6
- data/lib/hanami/cli/commands/app/assets/command.rb +155 -0
- data/lib/hanami/cli/commands/app/assets/compile.rb +35 -0
- data/lib/hanami/cli/commands/app/assets/watch.rb +29 -0
- data/lib/hanami/cli/commands/app/assets.rb +16 -0
- data/lib/hanami/cli/commands/app/command.rb +5 -3
- data/lib/hanami/cli/commands/app/db/rollback.rb +30 -6
- data/lib/hanami/cli/commands/app/db/utils/database.rb +5 -1
- data/lib/hanami/cli/commands/app/dev.rb +50 -0
- data/lib/hanami/cli/commands/app/generate/action.rb +38 -9
- data/lib/hanami/cli/commands/app/generate/part.rb +57 -0
- data/lib/hanami/cli/commands/app/generate/slice.rb +6 -3
- data/lib/hanami/cli/commands/app/generate/view.rb +56 -0
- data/lib/hanami/cli/commands/app/generate.rb +2 -2
- data/lib/hanami/cli/commands/app/install.rb +16 -1
- data/lib/hanami/cli/commands/app/server.rb +6 -10
- data/lib/hanami/cli/commands/app.rb +10 -0
- data/lib/hanami/cli/commands/gem/new.rb +70 -12
- data/lib/hanami/cli/errors.rb +30 -0
- data/lib/hanami/cli/files.rb +8 -2
- data/lib/hanami/cli/generators/app/action/action.erb +5 -1
- data/lib/hanami/cli/generators/app/action/slice_action.erb +5 -1
- data/lib/hanami/cli/generators/app/action/slice_template.html.erb +1 -0
- data/lib/hanami/cli/generators/app/action/slice_view.erb +10 -0
- data/lib/hanami/cli/generators/app/action/template.html.erb +1 -2
- data/lib/hanami/cli/generators/app/action/view.erb +2 -5
- data/lib/hanami/cli/generators/app/action.rb +74 -22
- data/lib/hanami/cli/generators/app/action_context.rb +1 -1
- data/lib/hanami/cli/generators/app/part/app_base_part.erb +9 -0
- data/lib/hanami/cli/generators/app/part/app_part.erb +13 -0
- data/lib/hanami/cli/generators/app/part/slice_base_part.erb +9 -0
- data/lib/hanami/cli/generators/app/part/slice_part.erb +13 -0
- data/lib/hanami/cli/generators/app/part.rb +101 -0
- data/lib/hanami/cli/generators/app/part_context.rb +98 -0
- data/lib/hanami/cli/generators/app/slice/app_css.erb +5 -0
- data/lib/hanami/cli/generators/app/slice/app_js.erb +1 -0
- data/lib/hanami/cli/generators/app/slice/app_layout.erb +18 -0
- data/lib/hanami/cli/generators/app/slice/favicon.ico +0 -0
- data/lib/hanami/cli/generators/app/slice/helpers.erb +10 -0
- data/lib/hanami/cli/generators/app/slice/view.erb +0 -2
- data/lib/hanami/cli/generators/app/slice.rb +20 -6
- data/lib/hanami/cli/generators/app/slice_context.rb +18 -0
- data/lib/hanami/cli/generators/app/view/app_template.html.erb +1 -0
- data/lib/hanami/cli/generators/app/view/app_view.erb +10 -0
- data/lib/hanami/cli/generators/app/view/slice_template.html.erb +1 -0
- data/lib/hanami/cli/generators/app/view/slice_view.erb +10 -0
- data/lib/hanami/cli/generators/app/view.rb +89 -0
- data/lib/hanami/cli/generators/app/view_context.rb +100 -0
- data/lib/hanami/cli/generators/context.rb +72 -5
- data/lib/hanami/cli/generators/gem/app/404.html +82 -0
- data/lib/hanami/cli/generators/gem/app/500.html +82 -0
- data/lib/hanami/cli/generators/gem/app/app_css.erb +5 -0
- data/lib/hanami/cli/generators/gem/app/app_js.erb +1 -0
- data/lib/hanami/cli/generators/gem/app/app_layout.erb +18 -0
- data/lib/hanami/cli/generators/gem/app/assets.js +16 -0
- data/lib/hanami/cli/generators/gem/app/dev +8 -0
- data/lib/hanami/cli/generators/gem/app/favicon.ico +0 -0
- data/lib/hanami/cli/generators/gem/app/gemfile.erb +14 -6
- data/lib/hanami/cli/generators/gem/app/gitignore.erb +6 -0
- data/lib/hanami/cli/generators/gem/app/helpers.erb +10 -0
- data/lib/hanami/cli/generators/gem/app/package.json.erb +8 -0
- data/lib/hanami/cli/generators/gem/app/procfile.erb +4 -0
- data/lib/hanami/cli/generators/gem/app/puma.erb +39 -7
- data/lib/hanami/cli/generators/gem/app/routes.erb +1 -1
- data/lib/hanami/cli/generators/gem/app/view.erb +9 -0
- data/lib/hanami/cli/generators/gem/app.rb +25 -1
- data/lib/hanami/cli/generators/version.rb +14 -2
- data/lib/hanami/cli/interactive_system_call.rb +65 -0
- data/lib/hanami/cli/naming.rb +25 -0
- data/lib/hanami/cli/system_call.rb +8 -2
- data/lib/hanami/cli/url.rb +1 -1
- data/lib/hanami/cli/version.rb +1 -1
- metadata +47 -6
|
@@ -20,12 +20,20 @@ module Hanami
|
|
|
20
20
|
migration_code, migration_name = find_migration(target)
|
|
21
21
|
|
|
22
22
|
if migration_name.nil?
|
|
23
|
-
|
|
23
|
+
output = if target
|
|
24
|
+
"==> migration file for target #{target} was not found"
|
|
25
|
+
else
|
|
26
|
+
"==> no migrations to rollback"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
out.puts output
|
|
24
30
|
return
|
|
25
31
|
end
|
|
26
32
|
|
|
27
33
|
measure "database #{database.name} rolled back to #{migration_name}" do
|
|
28
34
|
database.run_migrations(target: Integer(migration_code))
|
|
35
|
+
|
|
36
|
+
true
|
|
29
37
|
end
|
|
30
38
|
|
|
31
39
|
run_command Structure::Dump if dump
|
|
@@ -33,14 +41,30 @@ module Hanami
|
|
|
33
41
|
|
|
34
42
|
private
|
|
35
43
|
|
|
36
|
-
def find_migration(code)
|
|
37
|
-
|
|
44
|
+
def find_migration(code) # rubocop:disable Metrics/PerceivedComplexity
|
|
45
|
+
applied_migrations = database.applied_migrations
|
|
46
|
+
|
|
47
|
+
return if applied_migrations.empty?
|
|
48
|
+
|
|
49
|
+
# Rollback to initial state if we have only one migration and
|
|
50
|
+
# no target is specified. In this case the rollback target
|
|
51
|
+
# will be the current migration timestamp minus 1
|
|
52
|
+
if applied_migrations.one? && code.nil?
|
|
53
|
+
migration = applied_migrations.first
|
|
54
|
+
|
|
55
|
+
migration_code = Integer(migration.split("_").first) - 1
|
|
56
|
+
migration_name = "initial state"
|
|
57
|
+
|
|
58
|
+
return [migration_code, migration_name]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Otherwise rollback to target or to previous migration
|
|
62
|
+
migration =
|
|
38
63
|
if code
|
|
39
|
-
|
|
64
|
+
applied_migrations.detect { |m| m.split("_").first == code }
|
|
40
65
|
else
|
|
41
|
-
|
|
66
|
+
applied_migrations[-2]
|
|
42
67
|
end
|
|
43
|
-
}
|
|
44
68
|
|
|
45
69
|
return unless migration
|
|
46
70
|
|
|
@@ -139,7 +139,11 @@ module Hanami
|
|
|
139
139
|
@sequel_migrator ||= begin
|
|
140
140
|
require "sequel"
|
|
141
141
|
Sequel.extension :migration
|
|
142
|
-
|
|
142
|
+
|
|
143
|
+
require "rom/sql"
|
|
144
|
+
ROM::SQL.with_gateway(gateway) do
|
|
145
|
+
Sequel::TimestampMigrator.new(migrator.connection, migrations_path, {})
|
|
146
|
+
end
|
|
143
147
|
end
|
|
144
148
|
end
|
|
145
149
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../interactive_system_call"
|
|
4
|
+
|
|
5
|
+
module Hanami
|
|
6
|
+
module CLI
|
|
7
|
+
module Commands
|
|
8
|
+
module App
|
|
9
|
+
# @since 2.1.0
|
|
10
|
+
# @api private
|
|
11
|
+
class Dev < App::Command
|
|
12
|
+
# @since 2.1.0
|
|
13
|
+
# @api private
|
|
14
|
+
desc "Start the application in development mode"
|
|
15
|
+
|
|
16
|
+
# @since 2.1.0
|
|
17
|
+
# @api private
|
|
18
|
+
def initialize(
|
|
19
|
+
out:, err:,
|
|
20
|
+
system_call: InteractiveSystemCall.new(out: out, err: err),
|
|
21
|
+
**opts
|
|
22
|
+
)
|
|
23
|
+
super(out: out, err: err, **opts)
|
|
24
|
+
|
|
25
|
+
@system_call = system_call
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @since 2.1.0
|
|
29
|
+
# @api private
|
|
30
|
+
def call(**)
|
|
31
|
+
bin, args = executable
|
|
32
|
+
system_call.call(bin, *args)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
# @since 2.1.0
|
|
38
|
+
# @api private
|
|
39
|
+
attr_reader :system_call
|
|
40
|
+
|
|
41
|
+
# @since 2.1.0
|
|
42
|
+
# @api private
|
|
43
|
+
def executable
|
|
44
|
+
[::File.join("bin", "dev")]
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "dry/inflector"
|
|
4
4
|
require "dry/files"
|
|
5
5
|
require "shellwords"
|
|
6
|
+
require_relative "../../../naming"
|
|
6
7
|
require_relative "../../../errors"
|
|
7
8
|
|
|
8
9
|
module Hanami
|
|
@@ -21,12 +22,25 @@ module Hanami
|
|
|
21
22
|
DEFAULT_SKIP_VIEW = false
|
|
22
23
|
private_constant :DEFAULT_SKIP_VIEW
|
|
23
24
|
|
|
25
|
+
DEFAULT_SKIP_TESTS = false
|
|
26
|
+
private_constant :DEFAULT_SKIP_TESTS
|
|
27
|
+
|
|
24
28
|
argument :name, required: true, desc: "Action name"
|
|
25
29
|
option :url, required: false, type: :string, desc: "Action URL"
|
|
26
30
|
option :http, required: false, type: :string, desc: "Action HTTP method"
|
|
27
31
|
# option :format, required: false, type: :string, default: DEFAULT_FORMAT, desc: "Template format"
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
option \
|
|
33
|
+
:skip_view,
|
|
34
|
+
required: false,
|
|
35
|
+
type: :boolean,
|
|
36
|
+
default: DEFAULT_SKIP_VIEW,
|
|
37
|
+
desc: "Skip view and template generation"
|
|
38
|
+
option \
|
|
39
|
+
:skip_tests,
|
|
40
|
+
required: false,
|
|
41
|
+
type: :boolean,
|
|
42
|
+
default: DEFAULT_SKIP_TESTS,
|
|
43
|
+
desc: "Skip test generation"
|
|
30
44
|
option :slice, required: false, desc: "Slice name"
|
|
31
45
|
|
|
32
46
|
# rubocop:disable Layout/LineLength
|
|
@@ -47,33 +61,48 @@ module Hanami
|
|
|
47
61
|
|
|
48
62
|
# @since 2.0.0
|
|
49
63
|
# @api private
|
|
50
|
-
def initialize(
|
|
51
|
-
|
|
64
|
+
def initialize(
|
|
65
|
+
fs:, inflector:,
|
|
66
|
+
naming: Naming.new(inflector: inflector),
|
|
67
|
+
generator: Generators::App::Action.new(fs: fs, inflector: inflector),
|
|
68
|
+
**opts
|
|
69
|
+
)
|
|
70
|
+
super(fs: fs, inflector: inflector, **opts)
|
|
71
|
+
|
|
72
|
+
@naming = naming
|
|
52
73
|
@generator = generator
|
|
53
|
-
super(fs: fs)
|
|
54
74
|
end
|
|
55
75
|
|
|
56
76
|
# rubocop:disable Metrics/ParameterLists
|
|
57
77
|
|
|
58
78
|
# @since 2.0.0
|
|
59
79
|
# @api private
|
|
60
|
-
def call(
|
|
80
|
+
def call(
|
|
81
|
+
name:,
|
|
82
|
+
url: nil,
|
|
83
|
+
http: nil,
|
|
84
|
+
format: DEFAULT_FORMAT,
|
|
85
|
+
skip_view: DEFAULT_SKIP_VIEW,
|
|
86
|
+
skip_tests: DEFAULT_SKIP_TESTS, # rubocop:disable Lint/UnusedMethodArgument
|
|
87
|
+
slice: nil,
|
|
88
|
+
context: nil, **
|
|
89
|
+
)
|
|
61
90
|
slice = inflector.underscore(Shellwords.shellescape(slice)) if slice
|
|
62
|
-
name =
|
|
91
|
+
name = naming.action_name(name)
|
|
63
92
|
*controller, action = name.split(ACTION_SEPARATOR)
|
|
64
93
|
|
|
65
94
|
if controller.empty?
|
|
66
95
|
raise InvalidActionNameError.new(name)
|
|
67
96
|
end
|
|
68
97
|
|
|
69
|
-
generator.call(app.namespace, controller, action, url, http, format, skip_view, slice)
|
|
98
|
+
generator.call(app.namespace, controller, action, url, http, format, skip_view, slice, context: context)
|
|
70
99
|
end
|
|
71
100
|
|
|
72
101
|
# rubocop:enable Metrics/ParameterLists
|
|
73
102
|
|
|
74
103
|
private
|
|
75
104
|
|
|
76
|
-
attr_reader :generator
|
|
105
|
+
attr_reader :naming, :generator
|
|
77
106
|
end
|
|
78
107
|
end
|
|
79
108
|
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/inflector"
|
|
4
|
+
require "dry/files"
|
|
5
|
+
require "shellwords"
|
|
6
|
+
|
|
7
|
+
module Hanami
|
|
8
|
+
module CLI
|
|
9
|
+
module Commands
|
|
10
|
+
module App
|
|
11
|
+
module Generate
|
|
12
|
+
# @since 2.1.0
|
|
13
|
+
# @api private
|
|
14
|
+
class Part < App::Command
|
|
15
|
+
DEFAULT_SKIP_TESTS = false
|
|
16
|
+
private_constant :DEFAULT_SKIP_TESTS
|
|
17
|
+
|
|
18
|
+
argument :name, required: true, desc: "Part name"
|
|
19
|
+
option :slice, required: false, desc: "Slice name"
|
|
20
|
+
option \
|
|
21
|
+
:skip_tests,
|
|
22
|
+
required: false,
|
|
23
|
+
type: :boolean,
|
|
24
|
+
default: DEFAULT_SKIP_TESTS,
|
|
25
|
+
desc: "Skip test generation"
|
|
26
|
+
|
|
27
|
+
example [
|
|
28
|
+
%(book (MyApp::Views::Parts::Book)),
|
|
29
|
+
%(book --slice=admin (Admin::Views::Parts::Book)),
|
|
30
|
+
]
|
|
31
|
+
attr_reader :generator
|
|
32
|
+
private :generator
|
|
33
|
+
|
|
34
|
+
# @since 2.0.0
|
|
35
|
+
# @api private
|
|
36
|
+
def initialize(
|
|
37
|
+
fs:, inflector:,
|
|
38
|
+
generator: Generators::App::Part.new(fs: fs, inflector: inflector),
|
|
39
|
+
**opts
|
|
40
|
+
)
|
|
41
|
+
super(fs: fs, inflector: inflector, **opts)
|
|
42
|
+
@generator = generator
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# @since 2.0.0
|
|
46
|
+
# @api private
|
|
47
|
+
def call(name:, slice: nil, skip_tests: DEFAULT_SKIP_TESTS, **) # rubocop:disable Lint/UnusedMethodArgument
|
|
48
|
+
slice = inflector.underscore(Shellwords.shellescape(slice)) if slice
|
|
49
|
+
|
|
50
|
+
generator.call(app.namespace, name, slice)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -23,10 +23,13 @@ module Hanami
|
|
|
23
23
|
|
|
24
24
|
# @since 2.0.0
|
|
25
25
|
# @api private
|
|
26
|
-
def initialize(
|
|
27
|
-
|
|
26
|
+
def initialize(
|
|
27
|
+
fs:, inflector:,
|
|
28
|
+
generator: Generators::App::Slice.new(fs: fs, inflector: inflector),
|
|
29
|
+
**opts
|
|
30
|
+
)
|
|
31
|
+
super(fs: fs, inflector: inflector, **opts)
|
|
28
32
|
@generator = generator
|
|
29
|
-
super(fs: fs)
|
|
30
33
|
end
|
|
31
34
|
|
|
32
35
|
# @since 2.0.0
|
|
@@ -0,0 +1,56 @@
|
|
|
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.0.0
|
|
15
|
+
# @api private
|
|
16
|
+
class View < App::Command
|
|
17
|
+
# TODO: make this configurable
|
|
18
|
+
DEFAULT_FORMAT = "html"
|
|
19
|
+
private_constant :DEFAULT_FORMAT
|
|
20
|
+
|
|
21
|
+
# TODO: make engine configurable
|
|
22
|
+
|
|
23
|
+
argument :name, required: true, desc: "View name"
|
|
24
|
+
option :slice, required: false, desc: "Slice name"
|
|
25
|
+
|
|
26
|
+
example [
|
|
27
|
+
%(books.index (MyApp::Actions::Books::Index)),
|
|
28
|
+
%(books.index --slice=admin (Admin::Actions::Books::Index)),
|
|
29
|
+
]
|
|
30
|
+
attr_reader :generator
|
|
31
|
+
private :generator
|
|
32
|
+
|
|
33
|
+
# @since 2.0.0
|
|
34
|
+
# @api private
|
|
35
|
+
def initialize(
|
|
36
|
+
fs:, inflector:,
|
|
37
|
+
generator: Generators::App::View.new(fs: fs, inflector: inflector),
|
|
38
|
+
**opts
|
|
39
|
+
)
|
|
40
|
+
super(fs: fs, inflector: inflector, **opts)
|
|
41
|
+
@generator = generator
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @since 2.0.0
|
|
45
|
+
# @api private
|
|
46
|
+
def call(name:, format: DEFAULT_FORMAT, slice: nil, **)
|
|
47
|
+
slice = inflector.underscore(Shellwords.shellescape(slice)) if slice
|
|
48
|
+
|
|
49
|
+
generator.call(app.namespace, name, format, slice)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "../../system_call"
|
|
4
|
+
|
|
3
5
|
module Hanami
|
|
4
6
|
module CLI
|
|
5
7
|
module Commands
|
|
@@ -18,9 +20,22 @@ module Hanami
|
|
|
18
20
|
# @since 2.0.0
|
|
19
21
|
# @api private
|
|
20
22
|
class Install < Command
|
|
23
|
+
# @since 2.1.0
|
|
24
|
+
# @api private
|
|
25
|
+
DEFAULT_HEAD = false
|
|
26
|
+
private_constant :DEFAULT_HEAD
|
|
27
|
+
|
|
28
|
+
# @since 2.1.0
|
|
29
|
+
# @api private
|
|
21
30
|
desc "Install Hanami third-party plugins"
|
|
22
31
|
|
|
23
|
-
|
|
32
|
+
# @since 2.1.0
|
|
33
|
+
# @api private
|
|
34
|
+
option :head, type: :boolean, desc: "Install head deps", default: DEFAULT_HEAD
|
|
35
|
+
|
|
36
|
+
# @since 2.0.0
|
|
37
|
+
# @api private
|
|
38
|
+
def call(head: DEFAULT_HEAD, **)
|
|
24
39
|
end
|
|
25
40
|
end
|
|
26
41
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "rack"
|
|
4
|
+
require "hanami/port"
|
|
4
5
|
require_relative "../app"
|
|
5
6
|
require_relative "../../server"
|
|
6
7
|
|
|
@@ -28,11 +29,6 @@ module Hanami
|
|
|
28
29
|
# @since 2.0.0
|
|
29
30
|
# @api private
|
|
30
31
|
class Server < Command
|
|
31
|
-
# @since 2.0.0
|
|
32
|
-
# @api private
|
|
33
|
-
DEFAULT_PORT = 2300
|
|
34
|
-
private_constant :DEFAULT_PORT
|
|
35
|
-
|
|
36
32
|
# @since 2.0.0
|
|
37
33
|
# @api private
|
|
38
34
|
DEFAULT_CONFIG_PATH = "config.ru"
|
|
@@ -42,7 +38,7 @@ module Hanami
|
|
|
42
38
|
|
|
43
39
|
option :host, default: nil, required: false,
|
|
44
40
|
desc: "The host address to bind to (falls back to the rack handler)"
|
|
45
|
-
option :port, default:
|
|
41
|
+
option :port, default: Hanami::Port::DEFAULT, required: false,
|
|
46
42
|
desc: "The port to run the server on (falls back to the rack handler)"
|
|
47
43
|
option :config, default: DEFAULT_CONFIG_PATH, required: false, desc: "Rack configuration file"
|
|
48
44
|
option :debug, default: false, required: false, desc: "Turn on/off debug output", type: :boolean
|
|
@@ -50,15 +46,15 @@ module Hanami
|
|
|
50
46
|
|
|
51
47
|
# @since 2.0.0
|
|
52
48
|
# @api private
|
|
53
|
-
def initialize(server: Hanami::CLI::Server.new)
|
|
54
|
-
super()
|
|
49
|
+
def initialize(server: Hanami::CLI::Server.new, **opts)
|
|
50
|
+
super(**opts)
|
|
55
51
|
@server = server
|
|
56
52
|
end
|
|
57
53
|
|
|
58
54
|
# @since 2.0.0
|
|
59
55
|
# @api private
|
|
60
|
-
def call(port:
|
|
61
|
-
server.call(port: port, **kwargs)
|
|
56
|
+
def call(port: Hanami::Port::DEFAULT, **kwargs)
|
|
57
|
+
server.call(port: Hanami::Port[port], **kwargs)
|
|
62
58
|
end
|
|
63
59
|
|
|
64
60
|
private
|
|
@@ -14,14 +14,24 @@ module Hanami
|
|
|
14
14
|
base.module_eval do
|
|
15
15
|
register "version", Commands::App::Version, aliases: ["v", "-v", "--version"]
|
|
16
16
|
register "install", Commands::App::Install
|
|
17
|
+
register "dev", Commands::App::Dev
|
|
17
18
|
register "console", Commands::App::Console, aliases: ["c"]
|
|
18
19
|
register "server", Commands::App::Server, aliases: ["s"]
|
|
19
20
|
register "routes", Commands::App::Routes
|
|
20
21
|
register "middleware", Commands::App::Middleware
|
|
21
22
|
|
|
23
|
+
if Hanami.bundled?("hanami-assets")
|
|
24
|
+
register "assets" do |prefix|
|
|
25
|
+
prefix.register "compile", Assets::Compile
|
|
26
|
+
prefix.register "watch", Assets::Watch
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
22
30
|
register "generate", aliases: ["g"] do |prefix|
|
|
23
31
|
prefix.register "slice", Generate::Slice
|
|
24
32
|
prefix.register "action", Generate::Action
|
|
33
|
+
prefix.register "view", Generate::View
|
|
34
|
+
prefix.register "part", Generate::Part
|
|
25
35
|
end
|
|
26
36
|
end
|
|
27
37
|
end
|
|
@@ -10,66 +10,124 @@ module Hanami
|
|
|
10
10
|
# @since 2.0.0
|
|
11
11
|
# @api private
|
|
12
12
|
class New < Command
|
|
13
|
+
# @since 2.0.0
|
|
14
|
+
# @api private
|
|
13
15
|
SKIP_INSTALL_DEFAULT = false
|
|
14
16
|
private_constant :SKIP_INSTALL_DEFAULT
|
|
15
17
|
|
|
18
|
+
# @since 2.1.0
|
|
19
|
+
# @api private
|
|
20
|
+
HEAD_DEFAULT = false
|
|
21
|
+
private_constant :HEAD_DEFAULT
|
|
22
|
+
|
|
23
|
+
# @since 2.1.0
|
|
24
|
+
# @api private
|
|
25
|
+
SKIP_ASSETS_DEFAULT = false
|
|
26
|
+
private_constant :SKIP_ASSETS_DEFAULT
|
|
27
|
+
|
|
16
28
|
desc "Generate a new Hanami app"
|
|
17
29
|
|
|
30
|
+
# @since 2.0.0
|
|
31
|
+
# @api private
|
|
18
32
|
argument :app, required: true, desc: "App name"
|
|
19
33
|
|
|
34
|
+
# @since 2.0.0
|
|
35
|
+
# @api private
|
|
20
36
|
option :skip_install, type: :boolean, required: false,
|
|
21
37
|
default: SKIP_INSTALL_DEFAULT,
|
|
22
38
|
desc: "Skip app installation (Bundler, third-party Hanami plugins)"
|
|
23
39
|
|
|
40
|
+
# @since 2.1.0
|
|
41
|
+
# @api private
|
|
42
|
+
option :head, type: :boolean, required: false,
|
|
43
|
+
default: HEAD_DEFAULT,
|
|
44
|
+
desc: "Use Hanami HEAD version (from GitHub `main` branches)"
|
|
45
|
+
|
|
46
|
+
# @since 2.1.0
|
|
47
|
+
# @api private
|
|
48
|
+
option :skip_assets, type: :boolean, required: false,
|
|
49
|
+
default: SKIP_ASSETS_DEFAULT,
|
|
50
|
+
desc: "Skip assets"
|
|
51
|
+
|
|
52
|
+
# rubocop:disable Layout/LineLength
|
|
24
53
|
example [
|
|
25
|
-
"bookshelf # Generate a new Hanami app in `bookshelf/' directory, using `Bookshelf' namespace",
|
|
26
|
-
"bookshelf --
|
|
54
|
+
"bookshelf # Generate a new Hanami app in `bookshelf/' directory, using `Bookshelf' namespace",
|
|
55
|
+
"bookshelf --head # Generate a new Hanami app, using Hanami HEAD version from GitHub `main' branches",
|
|
56
|
+
"bookshelf --skip-install # Generate a new Hanami app, but it skips Hanami installation",
|
|
57
|
+
"bookshelf --skip-assets # Generate a new Hanami app without assets"
|
|
27
58
|
]
|
|
59
|
+
# rubocop:enable Layout/LineLength
|
|
60
|
+
|
|
61
|
+
# rubocop:disable Metrics/ParameterLists
|
|
28
62
|
|
|
29
63
|
# @since 2.0.0
|
|
30
64
|
# @api private
|
|
31
65
|
def initialize(
|
|
32
|
-
fs
|
|
33
|
-
inflector: Dry::Inflector.new,
|
|
66
|
+
fs:, inflector:,
|
|
34
67
|
bundler: CLI::Bundler.new(fs: fs),
|
|
35
68
|
generator: Generators::Gem::App.new(fs: fs, inflector: inflector),
|
|
36
|
-
|
|
69
|
+
system_call: SystemCall.new,
|
|
70
|
+
**opts
|
|
37
71
|
)
|
|
72
|
+
super(fs: fs, inflector: inflector, **opts)
|
|
38
73
|
@bundler = bundler
|
|
39
74
|
@generator = generator
|
|
40
|
-
|
|
75
|
+
@system_call = system_call
|
|
41
76
|
end
|
|
42
77
|
|
|
78
|
+
# rubocop:enable Metrics/ParameterLists
|
|
79
|
+
|
|
80
|
+
# rubocop:disable Metrics/AbcSize
|
|
81
|
+
|
|
43
82
|
# @since 2.0.0
|
|
44
83
|
# @api private
|
|
45
|
-
def call(app:, skip_install: SKIP_INSTALL_DEFAULT, **)
|
|
84
|
+
def call(app:, head: HEAD_DEFAULT, skip_install: SKIP_INSTALL_DEFAULT, skip_assets: SKIP_ASSETS_DEFAULT, **)
|
|
46
85
|
app = inflector.underscore(app)
|
|
47
86
|
|
|
48
87
|
raise PathAlreadyExistsError.new(app) if fs.exist?(app)
|
|
49
88
|
|
|
50
89
|
fs.mkdir(app)
|
|
51
90
|
fs.chdir(app) do
|
|
52
|
-
|
|
91
|
+
context = Generators::Context.new(inflector, app, head: head, skip_assets: skip_assets)
|
|
92
|
+
generator.call(app, context: context) do
|
|
53
93
|
if skip_install
|
|
54
94
|
out.puts "Skipping installation, please enter `#{app}' directory and run `bundle exec hanami install'"
|
|
55
95
|
else
|
|
56
96
|
out.puts "Running Bundler install..."
|
|
57
97
|
bundler.install!
|
|
98
|
+
|
|
99
|
+
unless skip_assets
|
|
100
|
+
out.puts "Running NPM install..."
|
|
101
|
+
system_call.call("npm", ["install"]).tap do |result|
|
|
102
|
+
unless result.successful?
|
|
103
|
+
puts "NPM ERROR:"
|
|
104
|
+
puts(result.err.lines.map { |line| line.prepend(" ") })
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
58
109
|
out.puts "Running Hanami install..."
|
|
59
|
-
|
|
110
|
+
run_install_command!(head: head)
|
|
60
111
|
end
|
|
61
112
|
end
|
|
62
113
|
end
|
|
63
114
|
end
|
|
115
|
+
# rubocop:enable Metrics/AbcSize
|
|
64
116
|
|
|
65
117
|
private
|
|
66
118
|
|
|
67
119
|
attr_reader :bundler
|
|
68
120
|
attr_reader :generator
|
|
121
|
+
attr_reader :system_call
|
|
69
122
|
|
|
70
|
-
def
|
|
71
|
-
|
|
72
|
-
|
|
123
|
+
def run_install_command!(head:)
|
|
124
|
+
head_flag = head ? " --head" : ""
|
|
125
|
+
bundler.exec("hanami install#{head_flag}").tap do |result|
|
|
126
|
+
if result.successful?
|
|
127
|
+
bundler.exec("check").successful? || bundler.exec("install")
|
|
128
|
+
else
|
|
129
|
+
raise HanamiInstallError.new(result.err)
|
|
130
|
+
end
|
|
73
131
|
end
|
|
74
132
|
end
|
|
75
133
|
end
|