hanami-cli 2.2.1 → 2.3.0.beta1
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 +9 -0
- data/CHANGELOG.md +33 -0
- data/Gemfile +3 -1
- data/hanami-cli.gemspec +2 -0
- data/lib/hanami/cli/command.rb +5 -11
- data/lib/hanami/cli/commands/app/command.rb +5 -5
- data/lib/hanami/cli/commands/app/console.rb +1 -0
- data/lib/hanami/cli/commands/app/db/command.rb +0 -1
- data/lib/hanami/cli/commands/app/db/drop.rb +2 -2
- data/lib/hanami/cli/commands/app/db/rollback.rb +204 -0
- data/lib/hanami/cli/commands/app/db/utils/mysql.rb +3 -2
- data/lib/hanami/cli/commands/app/db/utils/postgres.rb +3 -1
- data/lib/hanami/cli/commands/app/generate/action.rb +32 -40
- data/lib/hanami/cli/commands/app/generate/command.rb +54 -17
- data/lib/hanami/cli/commands/app/generate/component.rb +4 -19
- data/lib/hanami/cli/commands/app/generate/part.rb +4 -21
- data/lib/hanami/cli/commands/app/generate/slice.rb +2 -2
- data/lib/hanami/cli/commands/app/generate/view.rb +5 -24
- data/lib/hanami/cli/commands/app/server.rb +0 -1
- data/lib/hanami/cli/commands/app.rb +1 -0
- data/lib/hanami/cli/commands/gem/new.rb +34 -3
- data/lib/hanami/cli/errors.rb +26 -0
- data/lib/hanami/cli/files.rb +20 -6
- data/lib/hanami/cli/generators/app/action.rb +78 -101
- data/lib/hanami/cli/generators/app/component.rb +11 -33
- data/lib/hanami/cli/generators/app/migration.rb +1 -1
- data/lib/hanami/cli/generators/app/operation.rb +4 -5
- data/lib/hanami/cli/generators/app/part.rb +42 -65
- data/lib/hanami/cli/generators/app/relation.rb +4 -5
- data/lib/hanami/cli/generators/app/repo.rb +3 -5
- data/lib/hanami/cli/generators/app/ruby_class_file.rb +32 -0
- data/lib/hanami/cli/generators/app/ruby_file.rb +128 -0
- data/lib/hanami/cli/generators/app/ruby_module_file.rb +28 -0
- data/lib/hanami/cli/generators/app/slice.rb +130 -37
- data/lib/hanami/cli/generators/app/struct.rb +3 -4
- data/lib/hanami/cli/generators/app/view.rb +40 -45
- data/lib/hanami/cli/generators/context.rb +6 -0
- data/lib/hanami/cli/generators/gem/app/assets.js +14 -13
- data/lib/hanami/cli/generators/gem/app/dev +1 -1
- data/lib/hanami/cli/generators/gem/app/gemfile.erb +5 -0
- data/lib/hanami/cli/generators/gem/app/gitignore.erb +3 -1
- data/lib/hanami/cli/generators/gem/app/rakefile.erb +3 -0
- data/lib/hanami/cli/generators/gem/app/readme.erb +14 -0
- data/lib/hanami/cli/generators/gem/app.rb +40 -37
- data/lib/hanami/cli/ruby_file_generator.rb +17 -8
- data/lib/hanami/cli/server.rb +15 -1
- data/lib/hanami/cli/version.rb +1 -1
- data/lib/hanami/console/context.rb +5 -0
- metadata +35 -41
- data/lib/hanami/cli/generators/app/action/action.erb +0 -17
- data/lib/hanami/cli/generators/app/action/slice_action.erb +0 -17
- data/lib/hanami/cli/generators/app/action/slice_template.html.erb +0 -1
- data/lib/hanami/cli/generators/app/action/slice_view.erb +0 -10
- data/lib/hanami/cli/generators/app/action/template.erb +0 -0
- data/lib/hanami/cli/generators/app/action/template.html.erb +0 -1
- data/lib/hanami/cli/generators/app/action/view.erb +0 -10
- data/lib/hanami/cli/generators/app/action_context.rb +0 -90
- data/lib/hanami/cli/generators/app/component/component.erb +0 -8
- data/lib/hanami/cli/generators/app/component/slice_component.erb +0 -8
- data/lib/hanami/cli/generators/app/component_context.rb +0 -82
- data/lib/hanami/cli/generators/app/part/app_base_part.erb +0 -9
- data/lib/hanami/cli/generators/app/part/app_part.erb +0 -13
- data/lib/hanami/cli/generators/app/part/slice_base_part.erb +0 -9
- data/lib/hanami/cli/generators/app/part/slice_part.erb +0 -13
- data/lib/hanami/cli/generators/app/part_context.rb +0 -82
- data/lib/hanami/cli/generators/app/ruby_file_writer.rb +0 -151
- data/lib/hanami/cli/generators/app/slice/action.erb +0 -7
- data/lib/hanami/cli/generators/app/slice/app_css.erb +0 -5
- data/lib/hanami/cli/generators/app/slice/app_js.erb +0 -1
- data/lib/hanami/cli/generators/app/slice/app_layout.erb +0 -18
- data/lib/hanami/cli/generators/app/slice/helpers.erb +0 -10
- data/lib/hanami/cli/generators/app/slice/keep.erb +0 -0
- data/lib/hanami/cli/generators/app/slice/operation.erb +0 -7
- data/lib/hanami/cli/generators/app/slice/relation.erb +0 -8
- data/lib/hanami/cli/generators/app/slice/repo.erb +0 -8
- data/lib/hanami/cli/generators/app/slice/routes.erb +0 -3
- data/lib/hanami/cli/generators/app/slice/struct.erb +0 -8
- data/lib/hanami/cli/generators/app/slice/view.erb +0 -7
- data/lib/hanami/cli/generators/app/slice_context.rb +0 -72
- data/lib/hanami/cli/generators/app/view/app_template.html.erb +0 -1
- data/lib/hanami/cli/generators/app/view/app_view.erb +0 -10
- data/lib/hanami/cli/generators/app/view/slice_template.html.erb +0 -1
- data/lib/hanami/cli/generators/app/view/slice_view.erb +0 -10
- data/lib/hanami/cli/generators/app/view_context.rb +0 -88
@@ -3,6 +3,7 @@
|
|
3
3
|
require "dry/inflector"
|
4
4
|
require "dry/files"
|
5
5
|
require "shellwords"
|
6
|
+
|
6
7
|
module Hanami
|
7
8
|
module CLI
|
8
9
|
module Commands
|
@@ -10,9 +11,8 @@ module Hanami
|
|
10
11
|
module Generate
|
11
12
|
# @api private
|
12
13
|
# @since 2.2.0
|
13
|
-
class Component <
|
14
|
+
class Component < Command
|
14
15
|
argument :name, required: true, desc: "Component name"
|
15
|
-
option :slice, required: false, desc: "Slice name"
|
16
16
|
|
17
17
|
example [
|
18
18
|
%(isbn_decoder (MyApp::IsbnDecoder)),
|
@@ -20,26 +20,11 @@ module Hanami
|
|
20
20
|
%(isbn_decoder --slice=admin (Admin::IsbnDecoder)),
|
21
21
|
%(Exporters::Complete::CSV (MyApp::Exporters::Complete::CSV)),
|
22
22
|
]
|
23
|
-
attr_reader :generator
|
24
|
-
private :generator
|
25
23
|
|
26
|
-
# @api private
|
27
24
|
# @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
25
|
# @api private
|
38
|
-
|
39
|
-
|
40
|
-
slice = inflector.underscore(Shellwords.shellescape(slice)) if slice
|
41
|
-
|
42
|
-
generator.call(app.namespace, name, slice)
|
26
|
+
def generator_class
|
27
|
+
Generators::App::Component
|
43
28
|
end
|
44
29
|
end
|
45
30
|
end
|
@@ -11,12 +11,12 @@ module Hanami
|
|
11
11
|
module Generate
|
12
12
|
# @since 2.1.0
|
13
13
|
# @api private
|
14
|
-
class Part <
|
14
|
+
class Part < Command
|
15
15
|
DEFAULT_SKIP_TESTS = false
|
16
16
|
private_constant :DEFAULT_SKIP_TESTS
|
17
17
|
|
18
18
|
argument :name, required: true, desc: "Part name"
|
19
|
-
|
19
|
+
|
20
20
|
option \
|
21
21
|
:skip_tests,
|
22
22
|
required: false,
|
@@ -28,26 +28,9 @@ module Hanami
|
|
28
28
|
%(book (MyApp::Views::Parts::Book)),
|
29
29
|
%(book --slice=admin (Admin::Views::Parts::Book)),
|
30
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
31
|
|
50
|
-
|
32
|
+
def generator_class
|
33
|
+
Generators::App::Part
|
51
34
|
end
|
52
35
|
end
|
53
36
|
end
|
@@ -49,11 +49,11 @@ module Hanami
|
|
49
49
|
# @since 2.0.0
|
50
50
|
# @api private
|
51
51
|
def initialize(
|
52
|
-
fs:,
|
52
|
+
fs:,
|
53
53
|
generator: Generators::App::Slice.new(fs: fs, inflector: inflector),
|
54
54
|
**opts
|
55
55
|
)
|
56
|
-
super(fs: fs,
|
56
|
+
super(fs: fs, **opts)
|
57
57
|
@generator = generator
|
58
58
|
end
|
59
59
|
|
@@ -13,40 +13,21 @@ module Hanami
|
|
13
13
|
module Generate
|
14
14
|
# @since 2.0.0
|
15
15
|
# @api private
|
16
|
-
class View <
|
17
|
-
# TODO: make
|
18
|
-
DEFAULT_FORMAT = "html"
|
19
|
-
private_constant :DEFAULT_FORMAT
|
20
|
-
|
16
|
+
class View < Command
|
17
|
+
# TODO: make format configurable
|
21
18
|
# TODO: make engine configurable
|
22
19
|
|
23
20
|
argument :name, required: true, desc: "View name"
|
24
|
-
option :slice, required: false, desc: "Slice name"
|
25
21
|
|
26
22
|
example [
|
27
23
|
%(books.index (MyApp::Actions::Books::Index)),
|
28
24
|
%(books.index --slice=admin (Admin::Actions::Books::Index)),
|
29
25
|
]
|
30
|
-
attr_reader :generator
|
31
|
-
private :generator
|
32
26
|
|
33
|
-
# @since 2.
|
27
|
+
# @since 2.2.0
|
34
28
|
# @api private
|
35
|
-
def
|
36
|
-
|
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)
|
29
|
+
def generator_class
|
30
|
+
Generators::App::View
|
50
31
|
end
|
51
32
|
end
|
52
33
|
end
|
@@ -32,6 +32,7 @@ module Hanami
|
|
32
32
|
db.register "create", DB::Create
|
33
33
|
db.register "drop", DB::Drop
|
34
34
|
db.register "migrate", DB::Migrate
|
35
|
+
db.register "rollback", DB::Rollback
|
35
36
|
db.register "structure dump", DB::Structure::Dump
|
36
37
|
db.register "structure load", DB::Structure::Load
|
37
38
|
db.register "seed", DB::Seed
|
@@ -30,6 +30,11 @@ module Hanami
|
|
30
30
|
SKIP_DB_DEFAULT = false
|
31
31
|
private_constant :SKIP_DB_DEFAULT
|
32
32
|
|
33
|
+
# @since 2.2.0
|
34
|
+
# @api private
|
35
|
+
SKIP_VIEW_DEFAULT = false
|
36
|
+
private_constant :SKIP_VIEW_DEFAULT
|
37
|
+
|
33
38
|
# @since 2.2.0
|
34
39
|
# @api private
|
35
40
|
DATABASE_SQLITE = "sqlite"
|
@@ -46,6 +51,9 @@ module Hanami
|
|
46
51
|
# @api private
|
47
52
|
SUPPORTED_DATABASES = [DATABASE_SQLITE, DATABASE_POSTGRES, DATABASE_MYSQL].freeze
|
48
53
|
|
54
|
+
# @api private
|
55
|
+
FORBIDDEN_APP_NAMES = %w[app slice].freeze
|
56
|
+
|
49
57
|
desc "Generate a new Hanami app"
|
50
58
|
|
51
59
|
# @since 2.0.0
|
@@ -76,6 +84,12 @@ module Hanami
|
|
76
84
|
default: SKIP_DB_DEFAULT,
|
77
85
|
desc: "Skip including hanami-db"
|
78
86
|
|
87
|
+
# @since 2.2.0
|
88
|
+
# @api private
|
89
|
+
option :skip_view, type: :flag, required: false,
|
90
|
+
default: SKIP_VIEW_DEFAULT,
|
91
|
+
desc: "Skip including hanami-view"
|
92
|
+
|
79
93
|
# @since 2.2.0
|
80
94
|
# @api private
|
81
95
|
option :database, type: :string, required: false,
|
@@ -87,8 +101,9 @@ module Hanami
|
|
87
101
|
"bookshelf # Generate a new Hanami app in `bookshelf/' directory, using `Bookshelf' namespace",
|
88
102
|
"bookshelf --head # Generate a new Hanami app, using Hanami HEAD version from GitHub `main' branches",
|
89
103
|
"bookshelf --skip-install # Generate a new Hanami app, but it skips Hanami installation",
|
90
|
-
"bookshelf --skip-assets # Generate a new Hanami app without
|
104
|
+
"bookshelf --skip-assets # Generate a new Hanami app without hanami-assets",
|
91
105
|
"bookshelf --skip-db # Generate a new Hanami app without hanami-db",
|
106
|
+
"bookshelf --skip-view # Generate a new Hanami app without hanami-view",
|
92
107
|
"bookshelf --database={sqlite|postgres|mysql} # Generate a new Hanami app with a specified database (default: sqlite)",
|
93
108
|
]
|
94
109
|
# rubocop:enable Layout/LineLength
|
@@ -98,13 +113,13 @@ module Hanami
|
|
98
113
|
# @since 2.0.0
|
99
114
|
# @api private
|
100
115
|
def initialize(
|
101
|
-
fs:,
|
116
|
+
fs:,
|
102
117
|
bundler: CLI::Bundler.new(fs: fs),
|
103
118
|
generator: Generators::Gem::App.new(fs: fs, inflector: inflector),
|
104
119
|
system_call: SystemCall.new,
|
105
120
|
**opts
|
106
121
|
)
|
107
|
-
super(fs: fs,
|
122
|
+
super(fs: fs, **opts)
|
108
123
|
@bundler = bundler
|
109
124
|
@generator = generator
|
110
125
|
@system_call = system_call
|
@@ -120,12 +135,14 @@ module Hanami
|
|
120
135
|
skip_install: SKIP_INSTALL_DEFAULT,
|
121
136
|
skip_assets: SKIP_ASSETS_DEFAULT,
|
122
137
|
skip_db: SKIP_DB_DEFAULT,
|
138
|
+
skip_view: SKIP_VIEW_DEFAULT,
|
123
139
|
database: nil
|
124
140
|
)
|
125
141
|
# rubocop:enable Metrics/ParameterLists
|
126
142
|
app = inflector.underscore(app)
|
127
143
|
|
128
144
|
raise PathAlreadyExistsError.new(app) if fs.exist?(app)
|
145
|
+
raise ForbiddenAppNameError.new(app) if FORBIDDEN_APP_NAMES.include?(app)
|
129
146
|
|
130
147
|
normalized_database ||= normalize_database(database)
|
131
148
|
|
@@ -137,6 +154,7 @@ module Hanami
|
|
137
154
|
head: head,
|
138
155
|
skip_assets: skip_assets,
|
139
156
|
skip_db: skip_db,
|
157
|
+
skip_view: skip_view,
|
140
158
|
database: normalized_database
|
141
159
|
)
|
142
160
|
generator.call(app, context: context) do
|
@@ -158,6 +176,9 @@ module Hanami
|
|
158
176
|
|
159
177
|
out.puts "Running hanami install..."
|
160
178
|
run_install_command!(head: head)
|
179
|
+
|
180
|
+
out.puts "Initializing git repository..."
|
181
|
+
init_git_repository
|
161
182
|
end
|
162
183
|
end
|
163
184
|
end
|
@@ -193,6 +214,16 @@ module Hanami
|
|
193
214
|
end
|
194
215
|
end
|
195
216
|
end
|
217
|
+
|
218
|
+
# @api private
|
219
|
+
def init_git_repository
|
220
|
+
system_call.call("git", ["init"]).tap do |result|
|
221
|
+
unless result.successful?
|
222
|
+
out.puts "WARNING: Failed to initialize git repository"
|
223
|
+
out.puts(result.err.lines.map { |line| line.prepend(" ") })
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
196
227
|
end
|
197
228
|
end
|
198
229
|
end
|
data/lib/hanami/cli/errors.rb
CHANGED
@@ -44,6 +44,24 @@ module Hanami
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
+
# @api public
|
48
|
+
class FileAlreadyExistsError < Error
|
49
|
+
ERROR_MESSAGE = <<~ERROR.chomp
|
50
|
+
The file `%{file_path}` could not be generated because it already exists.
|
51
|
+
ERROR
|
52
|
+
|
53
|
+
def initialize(file_path)
|
54
|
+
super(ERROR_MESSAGE % {file_path:})
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# @api public
|
59
|
+
class ForbiddenAppNameError < Error
|
60
|
+
def initialize(name)
|
61
|
+
super("Cannot create new Hanami app with the name: `#{name}'")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
47
65
|
# @since 2.0.0
|
48
66
|
# @api public
|
49
67
|
class MissingSliceError < Error
|
@@ -100,6 +118,14 @@ module Hanami
|
|
100
118
|
end
|
101
119
|
end
|
102
120
|
|
121
|
+
# @since 2.2.0
|
122
|
+
# @api public
|
123
|
+
class DatabaseExistenceCheckError < Error
|
124
|
+
def initialize(original_message)
|
125
|
+
super("Could not check if the database exists. Error message:\n#{original_message}")
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
103
129
|
# @since 2.2.0
|
104
130
|
# @api public
|
105
131
|
class ConflictingOptionsError < Error
|
data/lib/hanami/cli/files.rb
CHANGED
@@ -14,6 +14,13 @@ module Hanami
|
|
14
14
|
@out = out
|
15
15
|
end
|
16
16
|
|
17
|
+
# @api private
|
18
|
+
def create(path, *content)
|
19
|
+
raise FileAlreadyExistsError.new(path) if exist?(path)
|
20
|
+
|
21
|
+
write(path, *content)
|
22
|
+
end
|
23
|
+
|
17
24
|
# @since 2.0.0
|
18
25
|
# @api private
|
19
26
|
def write(path, *content)
|
@@ -33,10 +40,10 @@ module Hanami
|
|
33
40
|
# @since 2.0.0
|
34
41
|
# @api private
|
35
42
|
def mkdir(path)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
43
|
+
return if exist?(path)
|
44
|
+
|
45
|
+
super
|
46
|
+
created(dir_path(path))
|
40
47
|
end
|
41
48
|
|
42
49
|
# @since 2.0.0
|
@@ -46,6 +53,13 @@ module Hanami
|
|
46
53
|
super
|
47
54
|
end
|
48
55
|
|
56
|
+
def touch(path)
|
57
|
+
return if exist?(path)
|
58
|
+
|
59
|
+
super
|
60
|
+
created(path)
|
61
|
+
end
|
62
|
+
|
49
63
|
private
|
50
64
|
|
51
65
|
attr_reader :out
|
@@ -77,10 +91,10 @@ module Hanami
|
|
77
91
|
end
|
78
92
|
|
79
93
|
def within_folder(path)
|
80
|
-
out.puts "-> Within #{
|
94
|
+
out.puts "-> Within #{dir_path(path)}"
|
81
95
|
end
|
82
96
|
|
83
|
-
def
|
97
|
+
def dir_path(path)
|
84
98
|
path + ::File::SEPARATOR
|
85
99
|
end
|
86
100
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "erb"
|
4
3
|
require "dry/files"
|
4
|
+
require_relative "../constants"
|
5
5
|
require_relative "../../errors"
|
6
6
|
|
7
7
|
# rubocop:disable Metrics/ParameterLists
|
@@ -14,20 +14,25 @@ module Hanami
|
|
14
14
|
class Action
|
15
15
|
# @since 2.0.0
|
16
16
|
# @api private
|
17
|
-
def initialize(fs:, inflector:)
|
17
|
+
def initialize(fs:, inflector:, out: $stdout)
|
18
18
|
@fs = fs
|
19
19
|
@inflector = inflector
|
20
|
+
@out = out
|
21
|
+
@view_generator = Generators::App::View.new(
|
22
|
+
fs: fs,
|
23
|
+
inflector: inflector,
|
24
|
+
out: out
|
25
|
+
)
|
20
26
|
end
|
21
27
|
|
22
28
|
# @since 2.0.0
|
23
29
|
# @api private
|
24
|
-
def call(
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
30
|
+
def call(key:, namespace:, base_path:, url_path:, http_method:, skip_view:, skip_route:)
|
31
|
+
insert_route(key:, namespace:, url_path:, http_method:) unless skip_route
|
32
|
+
|
33
|
+
generate_action(key:, namespace:, base_path:, include_placeholder_body: skip_view)
|
34
|
+
|
35
|
+
generate_view(key:, namespace:, base_path:) unless skip_view
|
31
36
|
end
|
32
37
|
|
33
38
|
private
|
@@ -67,86 +72,76 @@ module Hanami
|
|
67
72
|
PATH_SEPARATOR = "/"
|
68
73
|
private_constant :PATH_SEPARATOR
|
69
74
|
|
70
|
-
attr_reader :fs
|
71
|
-
|
72
|
-
attr_reader :inflector
|
73
|
-
|
74
|
-
# rubocop:disable Metrics/AbcSize
|
75
|
-
def generate_for_slice(controller, action, url, http, format, skip_view, skip_route, slice, context)
|
76
|
-
slice_directory = fs.join("slices", slice)
|
77
|
-
raise MissingSliceError.new(slice) unless fs.directory?(slice_directory)
|
78
|
-
|
79
|
-
if generate_route?(skip_route)
|
80
|
-
fs.inject_line_at_block_bottom(
|
81
|
-
fs.join("config", "routes.rb"),
|
82
|
-
slice_matcher(slice),
|
83
|
-
route(controller, action, url, http)
|
84
|
-
)
|
85
|
-
end
|
86
|
-
|
87
|
-
fs.mkdir(directory = fs.join(slice_directory, "actions", controller))
|
88
|
-
fs.write(fs.join(directory, "#{action}.rb"), t("slice_action.erb", context))
|
75
|
+
attr_reader :fs, :inflector, :out, :view_generator
|
89
76
|
|
90
|
-
|
91
|
-
|
92
|
-
|
77
|
+
# @api private
|
78
|
+
# @since 2.2.2
|
79
|
+
def insert_route(key:, namespace:, url_path:, http_method:)
|
80
|
+
routes_location = fs.join("config", "routes.rb")
|
81
|
+
route = route_definition(key:, url_path:, http_method:)
|
93
82
|
|
94
|
-
|
95
|
-
fs.
|
96
|
-
|
83
|
+
if namespace == Hanami.app.namespace
|
84
|
+
fs.inject_line_at_class_bottom(routes_location, "class Routes", route)
|
85
|
+
else
|
86
|
+
slice_matcher = /slice[[:space:]]*:#{namespace}/
|
87
|
+
fs.inject_line_at_block_bottom(routes_location, slice_matcher, route)
|
97
88
|
end
|
98
89
|
end
|
99
90
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
91
|
+
# @api private
|
92
|
+
# @since 2.2.2
|
93
|
+
def generate_action(key:, namespace:, base_path:, include_placeholder_body:)
|
94
|
+
RubyClassFile.new(
|
95
|
+
fs: fs,
|
96
|
+
inflector: inflector,
|
97
|
+
namespace: namespace,
|
98
|
+
key: inflector.underscore(key),
|
99
|
+
base_path: base_path,
|
100
|
+
parent_class_name: "#{inflector.camelize(namespace)}::Action",
|
101
|
+
extra_namespace: "Actions",
|
102
|
+
body: [
|
103
|
+
"def handle(request, response)",
|
104
|
+
(" response.body = self.class.name" if include_placeholder_body),
|
105
|
+
"end"
|
106
|
+
].compact
|
107
|
+
).create
|
108
|
+
end
|
111
109
|
|
112
|
-
|
113
|
-
|
110
|
+
# @api private
|
111
|
+
# @since 2.2.2
|
112
|
+
def generate_view(key:, namespace:, base_path:)
|
113
|
+
*controller_name_parts, action_name = key.split(KEY_SEPARATOR)
|
114
114
|
|
115
|
-
|
116
|
-
fs.mkdir(view_directory)
|
117
|
-
fs.write(fs.join(view_directory, "#{view}.rb"), t("view.erb", context))
|
115
|
+
view_directory = fs.join(base_path, "views", controller_name_parts)
|
118
116
|
|
119
|
-
|
120
|
-
|
121
|
-
|
117
|
+
if generate_view?(action_name, view_directory)
|
118
|
+
view_generator.call(
|
119
|
+
key: key,
|
120
|
+
namespace: namespace,
|
121
|
+
base_path: base_path,
|
122
|
+
)
|
122
123
|
end
|
123
124
|
end
|
124
|
-
# rubocop:enable Metrics/AbcSize
|
125
|
-
|
126
|
-
def slice_matcher(slice)
|
127
|
-
/slice[[:space:]]*:#{slice}/
|
128
|
-
end
|
129
|
-
|
130
|
-
def route(controller, action, url, http)
|
131
|
-
%(#{route_http(action,
|
132
|
-
http)} "#{route_url(controller, action, url)}", to: "#{controller.join('.')}.#{action}")
|
133
|
-
end
|
134
125
|
|
135
126
|
# @api private
|
136
|
-
# @since 2.
|
137
|
-
def
|
138
|
-
|
139
|
-
|
127
|
+
# @since 2.2.2
|
128
|
+
def route_definition(key:, url_path:, http_method:)
|
129
|
+
*controller_name_parts, action_name = key.split(KEY_SEPARATOR)
|
130
|
+
|
131
|
+
method = route_http(action_name, http_method)
|
132
|
+
path = route_url(controller_name_parts, action_name, url_path)
|
140
133
|
|
141
|
-
|
134
|
+
%(#{method} "#{path}", to: "#{key}")
|
142
135
|
end
|
143
136
|
|
144
137
|
# @api private
|
145
|
-
# @since 2.
|
146
|
-
def
|
147
|
-
|
148
|
-
|
149
|
-
|
138
|
+
# @since 2.1.0
|
139
|
+
def generate_view?(action_name, directory)
|
140
|
+
if rest_view?(action_name)
|
141
|
+
generate_restful_view?(action_name, directory)
|
142
|
+
else
|
143
|
+
true
|
144
|
+
end
|
150
145
|
end
|
151
146
|
|
152
147
|
# @api private
|
@@ -169,40 +164,22 @@ module Hanami
|
|
169
164
|
RESTFUL_COUNTERPART_VIEWS.fetch(view, nil)
|
170
165
|
end
|
171
166
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
when :html
|
176
|
-
".html.erb"
|
177
|
-
else
|
178
|
-
".erb"
|
179
|
-
end
|
180
|
-
|
181
|
-
"#{name}#{ext}"
|
182
|
-
end
|
183
|
-
|
184
|
-
def template(path, context)
|
185
|
-
require "erb"
|
186
|
-
|
187
|
-
ERB.new(
|
188
|
-
File.read(__dir__ + "/action/#{path}"), trim_mode: "-",
|
189
|
-
).result(context.ctx)
|
190
|
-
end
|
191
|
-
|
192
|
-
alias_method :t, :template
|
193
|
-
|
194
|
-
def route_url(controller, action, url)
|
167
|
+
# @api private
|
168
|
+
# @since 2.1.0
|
169
|
+
def route_url(controller, action, url_path)
|
195
170
|
action = ROUTE_RESTFUL_URL_SUFFIXES.fetch(action) { [action] }
|
196
|
-
|
171
|
+
url_path ||= "#{PATH_SEPARATOR}#{(controller + action).join(PATH_SEPARATOR)}"
|
197
172
|
|
198
|
-
CLI::URL.call(
|
173
|
+
CLI::URL.call(url_path)
|
199
174
|
end
|
200
175
|
|
201
|
-
|
202
|
-
|
176
|
+
# @api private
|
177
|
+
# @since 2.1.0
|
178
|
+
def route_http(action, http_method)
|
179
|
+
result = (http_method ||= ROUTE_RESTFUL_HTTP_METHODS.fetch(action, ROUTE_DEFAULT_HTTP_METHOD)).downcase
|
203
180
|
|
204
181
|
unless ROUTE_HTTP_METHODS.include?(result)
|
205
|
-
raise UnknownHTTPMethodError.new(
|
182
|
+
raise UnknownHTTPMethodError.new(http_method)
|
206
183
|
end
|
207
184
|
|
208
185
|
result
|
@@ -12,49 +12,27 @@ module Hanami
|
|
12
12
|
class Component
|
13
13
|
# @api private
|
14
14
|
# @since 2.2.0
|
15
|
-
def initialize(fs:, inflector:)
|
15
|
+
def initialize(fs:, inflector:, out: $stdout)
|
16
16
|
@fs = fs
|
17
17
|
@inflector = inflector
|
18
|
+
@out = out
|
18
19
|
end
|
19
20
|
|
20
21
|
# @api private
|
21
22
|
# @since 2.2.0
|
22
|
-
def call(
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
23
|
+
def call(key:, namespace:, base_path:)
|
24
|
+
RubyClassFile.new(
|
25
|
+
fs: fs,
|
26
|
+
inflector: inflector,
|
27
|
+
key: inflector.underscore(key),
|
28
|
+
namespace: namespace,
|
29
|
+
base_path: base_path,
|
30
|
+
).create
|
30
31
|
end
|
31
32
|
|
32
33
|
private
|
33
34
|
|
34
|
-
attr_reader :fs
|
35
|
-
|
36
|
-
attr_reader :inflector
|
37
|
-
|
38
|
-
def generate_for_slice(context, slice)
|
39
|
-
slice_directory = fs.join("slices", slice)
|
40
|
-
raise MissingSliceError.new(slice) unless fs.directory?(slice_directory)
|
41
|
-
|
42
|
-
fs.mkdir(directory = fs.join(slice_directory, context.namespaces))
|
43
|
-
fs.write(fs.join(directory, "#{context.underscored_name}.rb"), t("slice_component.erb", context))
|
44
|
-
end
|
45
|
-
|
46
|
-
def generate_for_app(context)
|
47
|
-
fs.mkdir(directory = fs.join("app", context.namespaces))
|
48
|
-
fs.write(fs.join(directory, "#{context.underscored_name}.rb"), t("component.erb", context))
|
49
|
-
end
|
50
|
-
|
51
|
-
def template(path, context)
|
52
|
-
ERB.new(
|
53
|
-
File.read(__dir__ + "/component/#{path}")
|
54
|
-
).result(context.ctx)
|
55
|
-
end
|
56
|
-
|
57
|
-
alias_method :t, :template
|
35
|
+
attr_reader :fs, :inflector, :out
|
58
36
|
end
|
59
37
|
end
|
60
38
|
end
|