hanami-cli 2.3.5 → 3.0.0.rc1
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/CHANGELOG.md +38 -5
- data/README.md +6 -1
- data/hanami-cli.gemspec +2 -3
- data/lib/hanami/cli/commands/app/db/migrate.rb +4 -4
- data/lib/hanami/cli/commands/app/db/utils/postgres.rb +4 -6
- data/lib/hanami/cli/commands/app/generate/action.rb +31 -7
- data/lib/hanami/cli/commands/app/generate/component.rb +2 -0
- data/lib/hanami/cli/commands/app/generate/mailer.rb +52 -0
- data/lib/hanami/cli/commands/app/generate/operation.rb +6 -0
- data/lib/hanami/cli/commands/app/generate/part.rb +4 -8
- data/lib/hanami/cli/commands/app/generate/provider.rb +27 -0
- data/lib/hanami/cli/commands/app/generate/relation.rb +7 -3
- data/lib/hanami/cli/commands/app/generate/repo.rb +3 -1
- data/lib/hanami/cli/commands/app/generate/slice.rb +16 -10
- data/lib/hanami/cli/commands/app/generate/struct.rb +2 -0
- data/lib/hanami/cli/commands/app/generate/view.rb +24 -1
- data/lib/hanami/cli/commands/app/middleware.rb +3 -3
- data/lib/hanami/cli/commands/app/routes.rb +5 -5
- data/lib/hanami/cli/commands/app/server.rb +2 -2
- data/lib/hanami/cli/commands/app.rb +6 -1
- data/lib/hanami/cli/commands/gem/new.rb +63 -82
- data/lib/hanami/cli/errors.rb +1 -1
- data/lib/hanami/cli/files.rb +9 -2
- data/lib/hanami/cli/generators/app/action.rb +39 -21
- data/lib/hanami/cli/generators/app/component.rb +2 -2
- data/lib/hanami/cli/generators/app/mailer.rb +81 -0
- data/lib/hanami/cli/generators/app/migration.rb +1 -1
- data/lib/hanami/cli/generators/app/operation.rb +2 -2
- data/lib/hanami/cli/generators/app/part.rb +4 -4
- data/lib/hanami/cli/generators/app/provider.rb +54 -0
- data/lib/hanami/cli/generators/app/relation.rb +2 -2
- data/lib/hanami/cli/generators/app/repo.rb +2 -2
- data/lib/hanami/cli/generators/app/ruby_file.rb +8 -4
- data/lib/hanami/cli/generators/app/slice.rb +37 -14
- data/lib/hanami/cli/generators/app/struct.rb +2 -2
- data/lib/hanami/cli/generators/app/view.rb +20 -10
- data/lib/hanami/cli/generators/context.rb +32 -52
- data/lib/hanami/cli/generators/gem/app/app.erb +3 -0
- data/lib/hanami/cli/generators/gem/app/app_layout.haml.erb +15 -0
- data/lib/hanami/cli/generators/gem/app/app_layout.slim.erb +15 -0
- data/lib/hanami/cli/generators/gem/app/assets.js +1 -1
- data/lib/hanami/cli/generators/gem/app/context.erb +1 -1
- data/lib/hanami/cli/generators/gem/app/env.erb +18 -1
- data/lib/hanami/cli/generators/gem/app/gemfile.erb +50 -6
- data/lib/hanami/cli/generators/gem/app/package.json.erb +1 -0
- data/lib/hanami/cli/generators/gem/app/puma.erb +0 -3
- data/lib/hanami/cli/generators/gem/app/readme.erb +2 -2
- data/lib/hanami/cli/generators/gem/app/routes.erb +1 -1
- data/lib/hanami/cli/generators/gem/app.rb +26 -1
- data/lib/hanami/cli/generators/i18n.yml +3 -0
- data/lib/hanami/cli/version.rb +1 -1
- metadata +11 -5
|
@@ -7,105 +7,78 @@ module Hanami
|
|
|
7
7
|
module CLI
|
|
8
8
|
module Commands
|
|
9
9
|
module Gem
|
|
10
|
-
# @since 2.0.0
|
|
11
10
|
# @api private
|
|
12
11
|
class New < Command
|
|
13
|
-
|
|
14
|
-
# @api private
|
|
15
|
-
HEAD_DEFAULT = false
|
|
16
|
-
private_constant :HEAD_DEFAULT
|
|
12
|
+
FORBIDDEN_APP_NAMES = %w[app slice].freeze
|
|
17
13
|
|
|
18
|
-
|
|
19
|
-
# @api private
|
|
14
|
+
HEAD_DEFAULT = false
|
|
20
15
|
GEM_SOURCE_DEFAULT = "rubygems.org"
|
|
21
|
-
private_constant :GEM_SOURCE_DEFAULT
|
|
22
16
|
|
|
23
|
-
# @since 2.0.0
|
|
24
|
-
# @api private
|
|
25
17
|
SKIP_INSTALL_DEFAULT = false
|
|
26
|
-
private_constant :SKIP_INSTALL_DEFAULT
|
|
27
|
-
|
|
28
|
-
# @since 2.1.0
|
|
29
|
-
# @api private
|
|
30
18
|
SKIP_ASSETS_DEFAULT = false
|
|
31
|
-
private_constant :SKIP_ASSETS_DEFAULT
|
|
32
|
-
|
|
33
|
-
# @since 2.2.0
|
|
34
|
-
# @api private
|
|
35
19
|
SKIP_DB_DEFAULT = false
|
|
36
|
-
private_constant :SKIP_DB_DEFAULT
|
|
37
|
-
|
|
38
|
-
# @since 2.2.0
|
|
39
|
-
# @api private
|
|
40
20
|
SKIP_VIEW_DEFAULT = false
|
|
41
|
-
|
|
21
|
+
SKIP_MAILER_DEFAULT = false
|
|
42
22
|
|
|
43
|
-
# @since 2.2.0
|
|
44
|
-
# @api private
|
|
45
23
|
DATABASE_SQLITE = "sqlite"
|
|
46
|
-
|
|
47
|
-
# @since 2.2.0
|
|
48
|
-
# @api private
|
|
49
24
|
DATABASE_POSTGRES = "postgres"
|
|
50
|
-
|
|
51
|
-
# @since 2.2.0
|
|
52
|
-
# @api private
|
|
53
25
|
DATABASE_MYSQL = "mysql"
|
|
54
|
-
|
|
55
|
-
# @since 2.2.0
|
|
56
|
-
# @api private
|
|
57
26
|
SUPPORTED_DATABASES = [DATABASE_SQLITE, DATABASE_POSTGRES, DATABASE_MYSQL].freeze
|
|
58
27
|
|
|
59
|
-
|
|
60
|
-
|
|
28
|
+
TEMPLATE_ENGINE_DEFAULT = "erb"
|
|
29
|
+
|
|
30
|
+
TEST_FRAMEWORK_DEFAULT = "rspec"
|
|
31
|
+
TEST_FRAMEWORK_RSPEC = "rspec"
|
|
32
|
+
TEST_FRAMEWORK_MINITEST = "minitest"
|
|
61
33
|
|
|
62
34
|
desc "Generate a new Hanami app"
|
|
63
35
|
|
|
64
|
-
# @since 2.0.0
|
|
65
|
-
# @api private
|
|
66
36
|
argument :app, required: true, desc: "App name"
|
|
67
37
|
|
|
68
|
-
# @since 2.1.0
|
|
69
|
-
# @api private
|
|
70
38
|
option :head, type: :flag, required: false,
|
|
71
|
-
|
|
72
|
-
|
|
39
|
+
default: HEAD_DEFAULT,
|
|
40
|
+
desc: "Use Hanami HEAD version (from GitHub `main` branches)"
|
|
73
41
|
|
|
74
|
-
# @since 2.3.0
|
|
75
|
-
# @api private
|
|
76
42
|
option :gem_source, required: true,
|
|
77
|
-
|
|
78
|
-
|
|
43
|
+
default: GEM_SOURCE_DEFAULT,
|
|
44
|
+
desc: "Where to source Ruby gems from"
|
|
79
45
|
|
|
80
|
-
# @since 2.0.0
|
|
81
|
-
# @api private
|
|
82
46
|
option :skip_install, type: :flag, required: false,
|
|
83
|
-
|
|
84
|
-
|
|
47
|
+
default: SKIP_INSTALL_DEFAULT,
|
|
48
|
+
desc: "Skip app installation (Bundler, third-party Hanami plugins)"
|
|
85
49
|
|
|
86
|
-
# @since 2.1.0
|
|
87
|
-
# @api private
|
|
88
50
|
option :skip_assets, type: :flag, required: false,
|
|
89
|
-
|
|
90
|
-
|
|
51
|
+
default: SKIP_ASSETS_DEFAULT,
|
|
52
|
+
desc: "Skip including hanami-assets"
|
|
91
53
|
|
|
92
|
-
# @since 2.2.0
|
|
93
|
-
# @api private
|
|
94
54
|
option :skip_db, type: :flag, required: false,
|
|
95
|
-
|
|
96
|
-
|
|
55
|
+
default: SKIP_DB_DEFAULT,
|
|
56
|
+
desc: "Skip including hanami-db"
|
|
97
57
|
|
|
98
|
-
# @since 2.2.0
|
|
99
|
-
# @api private
|
|
100
58
|
option :skip_view, type: :flag, required: false,
|
|
101
|
-
|
|
102
|
-
|
|
59
|
+
default: SKIP_VIEW_DEFAULT,
|
|
60
|
+
desc: "Skip including hanami-view"
|
|
61
|
+
|
|
62
|
+
option :skip_mailer, type: :flag, required: false,
|
|
63
|
+
default: SKIP_MAILER_DEFAULT,
|
|
64
|
+
desc: "Skip including hanami-mailer"
|
|
103
65
|
|
|
104
|
-
# @since 2.2.0
|
|
105
|
-
# @api private
|
|
106
66
|
option :database, type: :string, required: false,
|
|
107
|
-
|
|
108
|
-
|
|
67
|
+
default: DATABASE_SQLITE,
|
|
68
|
+
desc: "Database adapter (supported: sqlite, mysql, postgres)"
|
|
69
|
+
|
|
70
|
+
option :name, type: :string, required: false,
|
|
71
|
+
desc: "App name to use for the module namespace"
|
|
72
|
+
|
|
73
|
+
option :template_engine, type: :string, required: false,
|
|
74
|
+
values: %w[erb haml slim],
|
|
75
|
+
default: TEMPLATE_ENGINE_DEFAULT,
|
|
76
|
+
desc: "Default template engine to use with generators"
|
|
77
|
+
|
|
78
|
+
option :test, type: :string, required: false,
|
|
79
|
+
values: %w[rspec minitest],
|
|
80
|
+
default: TEST_FRAMEWORK_DEFAULT,
|
|
81
|
+
desc: "Test framework (supported: rspec, minitest)"
|
|
109
82
|
|
|
110
83
|
# rubocop:disable Layout/LineLength
|
|
111
84
|
example [
|
|
@@ -116,12 +89,14 @@ module Hanami
|
|
|
116
89
|
"bookshelf --skip-assets # Generate a new Hanami app without hanami-assets",
|
|
117
90
|
"bookshelf --skip-db # Generate a new Hanami app without hanami-db",
|
|
118
91
|
"bookshelf --skip-view # Generate a new Hanami app without hanami-view",
|
|
119
|
-
"bookshelf --
|
|
92
|
+
"bookshelf --skip-mailer # Generate a new Hanami app without hanami-mailer",
|
|
93
|
+
"bookshelf --database={sqlite|postgres|mysql} # Generate a new Hanami app with a specified database (default: sqlite)",
|
|
94
|
+
"bookshelf --template-engine={erb|haml|slim} # Generate a new Hanami app which will use HAML for templates by default (default: erb)",
|
|
95
|
+
"bookshelf --test={rspec|minitest} # Generate a new Hanami app with specified test framework (default: rspec)",
|
|
96
|
+
"my_bookshelf --name=bookshelf # Generate a new Hanami app in `my_bookshelf/' directory, using `Bookshelf' namespace"
|
|
120
97
|
]
|
|
121
98
|
# rubocop:enable Layout/LineLength
|
|
122
99
|
|
|
123
|
-
# @since 2.0.0
|
|
124
|
-
# @api private
|
|
125
100
|
def initialize(
|
|
126
101
|
fs:,
|
|
127
102
|
bundler: CLI::Bundler.new(fs: fs),
|
|
@@ -137,8 +112,6 @@ module Hanami
|
|
|
137
112
|
|
|
138
113
|
# rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
|
|
139
114
|
|
|
140
|
-
# @since 2.0.0
|
|
141
|
-
# @api private
|
|
142
115
|
def call(
|
|
143
116
|
app:,
|
|
144
117
|
head: HEAD_DEFAULT,
|
|
@@ -147,26 +120,34 @@ module Hanami
|
|
|
147
120
|
skip_assets: SKIP_ASSETS_DEFAULT,
|
|
148
121
|
skip_db: SKIP_DB_DEFAULT,
|
|
149
122
|
skip_view: SKIP_VIEW_DEFAULT,
|
|
150
|
-
|
|
123
|
+
skip_mailer: SKIP_MAILER_DEFAULT,
|
|
124
|
+
database: nil,
|
|
125
|
+
name: nil,
|
|
126
|
+
template_engine: TEMPLATE_ENGINE_DEFAULT,
|
|
127
|
+
test: TEST_FRAMEWORK_DEFAULT
|
|
151
128
|
)
|
|
152
|
-
|
|
129
|
+
directory = inflector.underscore(app)
|
|
130
|
+
app = inflector.underscore(name || app)
|
|
153
131
|
|
|
154
|
-
raise PathAlreadyExistsError.new(
|
|
132
|
+
raise PathAlreadyExistsError.new(directory) if fs.exist?(directory)
|
|
155
133
|
raise ForbiddenAppNameError.new(app) if FORBIDDEN_APP_NAMES.include?(app)
|
|
156
134
|
|
|
157
135
|
normalized_database ||= normalize_database(database)
|
|
158
136
|
|
|
159
|
-
fs.mkdir(
|
|
160
|
-
fs.chdir(
|
|
137
|
+
fs.mkdir(directory)
|
|
138
|
+
fs.chdir(directory) do
|
|
161
139
|
context = Generators::Context.new(
|
|
162
140
|
inflector,
|
|
163
141
|
app,
|
|
164
|
-
head
|
|
165
|
-
gem_source
|
|
166
|
-
skip_assets
|
|
167
|
-
skip_db
|
|
168
|
-
skip_view
|
|
169
|
-
|
|
142
|
+
head:,
|
|
143
|
+
gem_source:,
|
|
144
|
+
skip_assets:,
|
|
145
|
+
skip_db:,
|
|
146
|
+
skip_view:,
|
|
147
|
+
skip_mailer:,
|
|
148
|
+
database: normalized_database,
|
|
149
|
+
template_engine:,
|
|
150
|
+
test_framework: test
|
|
170
151
|
)
|
|
171
152
|
generator.call(app, context: context) do
|
|
172
153
|
if skip_install
|
data/lib/hanami/cli/errors.rb
CHANGED
|
@@ -90,7 +90,7 @@ module Hanami
|
|
|
90
90
|
# @api public
|
|
91
91
|
class InvalidActionNameError < Error
|
|
92
92
|
def initialize(name)
|
|
93
|
-
super("cannot parse
|
|
93
|
+
super("cannot parse action name: `#{name}'\n\texample: `hanami generate action users.show'")
|
|
94
94
|
end
|
|
95
95
|
end
|
|
96
96
|
|
data/lib/hanami/cli/files.rb
CHANGED
|
@@ -15,8 +15,8 @@ module Hanami
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
# @api private
|
|
18
|
-
def create(path, *content)
|
|
19
|
-
raise FileAlreadyExistsError.new(path) if exist?(path)
|
|
18
|
+
def create(path, *content, force: false)
|
|
19
|
+
raise FileAlreadyExistsError.new(path) if exist?(path) and !force
|
|
20
20
|
|
|
21
21
|
write(path, *content)
|
|
22
22
|
end
|
|
@@ -60,6 +60,13 @@ module Hanami
|
|
|
60
60
|
created(path)
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
+
def block_contains?(path, target, contents)
|
|
64
|
+
content = adapter.read(path)
|
|
65
|
+
class_start = content.index(target)
|
|
66
|
+
class_end = content.index(/(?:^|\s)#{CLOSE_BLOCK}(?:$|\s)/, class_start)
|
|
67
|
+
content[class_start..class_end].include? contents
|
|
68
|
+
end
|
|
69
|
+
|
|
63
70
|
private
|
|
64
71
|
|
|
65
72
|
attr_reader :out
|
|
@@ -11,6 +11,9 @@ module Hanami
|
|
|
11
11
|
# @since 2.0.0
|
|
12
12
|
# @api private
|
|
13
13
|
class Action
|
|
14
|
+
DEFAULT_TEMPLATE_ENGINE = "erb"
|
|
15
|
+
private_constant :DEFAULT_TEMPLATE_ENGINE
|
|
16
|
+
|
|
14
17
|
# @since 2.0.0
|
|
15
18
|
# @api private
|
|
16
19
|
def initialize(fs:, inflector:, out: $stdout)
|
|
@@ -26,12 +29,14 @@ module Hanami
|
|
|
26
29
|
|
|
27
30
|
# @since 2.0.0
|
|
28
31
|
# @api private
|
|
29
|
-
def call(key:, namespace:, base_path:, url_path:, http_method:, skip_view:, skip_route:, skip_tests
|
|
32
|
+
def call(key:, namespace:, base_path:, url_path:, http_method:, skip_view:, skip_route:, skip_tests:,
|
|
33
|
+
force:, template_engine: DEFAULT_TEMPLATE_ENGINE)
|
|
30
34
|
insert_route(key:, namespace:, url_path:, http_method:) unless skip_route
|
|
31
35
|
|
|
32
|
-
generate_action(key: key, namespace: namespace, base_path: base_path, include_placeholder_body: skip_view
|
|
36
|
+
generate_action(key: key, namespace: namespace, base_path: base_path, include_placeholder_body: skip_view,
|
|
37
|
+
force:)
|
|
33
38
|
|
|
34
|
-
generate_view(key:, namespace:, base_path:) unless skip_view
|
|
39
|
+
generate_view(key:, namespace:, base_path:, template_engine:, force:) unless skip_view
|
|
35
40
|
end
|
|
36
41
|
|
|
37
42
|
private
|
|
@@ -80,22 +85,21 @@ module Hanami
|
|
|
80
85
|
route = route_definition(key:, url_path:, http_method:)
|
|
81
86
|
|
|
82
87
|
if namespace == Hanami.app.namespace
|
|
83
|
-
|
|
88
|
+
add_route_to_file(file: routes_location, route:)
|
|
84
89
|
else
|
|
85
90
|
slice_routes = fs.join("slices", namespace, "config", "routes.rb")
|
|
86
91
|
|
|
87
92
|
if fs.exist?(slice_routes)
|
|
88
|
-
|
|
93
|
+
add_route_to_file(file: slice_routes, route:)
|
|
89
94
|
else
|
|
90
|
-
|
|
91
|
-
fs.inject_line_at_block_bottom(routes_location, slice_matcher, route)
|
|
95
|
+
add_route_to_block(file: routes_location, namespace:, route:)
|
|
92
96
|
end
|
|
93
97
|
end
|
|
94
98
|
end
|
|
95
99
|
|
|
96
100
|
# @api private
|
|
97
101
|
# @since 2.2.2
|
|
98
|
-
def generate_action(key:, namespace:, base_path:, include_placeholder_body:)
|
|
102
|
+
def generate_action(key:, namespace:, base_path:, include_placeholder_body:, force:)
|
|
99
103
|
RubyClassFile.new(
|
|
100
104
|
fs: fs,
|
|
101
105
|
inflector: inflector,
|
|
@@ -109,32 +113,28 @@ module Hanami
|
|
|
109
113
|
(" response.body = self.class.name" if include_placeholder_body),
|
|
110
114
|
"end"
|
|
111
115
|
].compact
|
|
112
|
-
).create
|
|
116
|
+
).create(force:)
|
|
113
117
|
end
|
|
114
118
|
|
|
115
119
|
# @api private
|
|
116
120
|
# @since 2.2.2
|
|
117
|
-
def generate_view(key:, namespace:, base_path:)
|
|
118
|
-
*
|
|
121
|
+
def generate_view(key:, namespace:, base_path:, template_engine:, force:)
|
|
122
|
+
*namespace_parts, action_name = key.split(KEY_SEPARATOR)
|
|
119
123
|
|
|
120
|
-
view_directory = fs.join(base_path, "views",
|
|
124
|
+
view_directory = fs.join(base_path, "views", namespace_parts)
|
|
121
125
|
|
|
122
126
|
if generate_view?(action_name, view_directory)
|
|
123
|
-
view_generator.call(
|
|
124
|
-
key: key,
|
|
125
|
-
namespace: namespace,
|
|
126
|
-
base_path: base_path
|
|
127
|
-
)
|
|
127
|
+
view_generator.call(key:, namespace:, base_path:, template_engine:, force:)
|
|
128
128
|
end
|
|
129
129
|
end
|
|
130
130
|
|
|
131
131
|
# @api private
|
|
132
132
|
# @since 2.2.2
|
|
133
133
|
def route_definition(key:, url_path:, http_method:)
|
|
134
|
-
*
|
|
134
|
+
*namespace_parts, action_name = key.split(KEY_SEPARATOR)
|
|
135
135
|
|
|
136
136
|
method = route_http(action_name, http_method)
|
|
137
|
-
path = route_url(
|
|
137
|
+
path = route_url(namespace_parts, action_name, url_path)
|
|
138
138
|
|
|
139
139
|
%(#{method} "#{path}", to: "#{key}")
|
|
140
140
|
end
|
|
@@ -171,9 +171,9 @@ module Hanami
|
|
|
171
171
|
|
|
172
172
|
# @api private
|
|
173
173
|
# @since 2.1.0
|
|
174
|
-
def route_url(
|
|
174
|
+
def route_url(namespace, action, url_path)
|
|
175
175
|
action = ROUTE_RESTFUL_URL_SUFFIXES.fetch(action) { [action] }
|
|
176
|
-
url_path ||= "#{PATH_SEPARATOR}#{(
|
|
176
|
+
url_path ||= "#{PATH_SEPARATOR}#{(namespace + action).join(PATH_SEPARATOR)}"
|
|
177
177
|
|
|
178
178
|
CLI::URL.call(url_path)
|
|
179
179
|
end
|
|
@@ -189,6 +189,24 @@ module Hanami
|
|
|
189
189
|
|
|
190
190
|
result
|
|
191
191
|
end
|
|
192
|
+
|
|
193
|
+
def add_route_to_file(file:, route:)
|
|
194
|
+
target_class = "class Routes"
|
|
195
|
+
if fs.block_contains?(file, target_class, route)
|
|
196
|
+
out.puts "Route (#{route}) already exists, skipping..."
|
|
197
|
+
else
|
|
198
|
+
fs.inject_line_at_class_bottom(file, "class Routes", route)
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def add_route_to_block(file:, namespace:, route:)
|
|
203
|
+
slice_matcher = /slice[[:space:]]*:#{namespace}/
|
|
204
|
+
if fs.block_contains?(file, slice_matcher, route)
|
|
205
|
+
out.puts "Route (#{route}) already exists, skipping..."
|
|
206
|
+
else
|
|
207
|
+
fs.inject_line_at_block_bottom(file, slice_matcher, route)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
192
210
|
end
|
|
193
211
|
end
|
|
194
212
|
end
|
|
@@ -20,14 +20,14 @@ module Hanami
|
|
|
20
20
|
|
|
21
21
|
# @api private
|
|
22
22
|
# @since 2.2.0
|
|
23
|
-
def call(key:, namespace:, base_path:)
|
|
23
|
+
def call(key:, namespace:, base_path:, force: false)
|
|
24
24
|
RubyClassFile.new(
|
|
25
25
|
fs: fs,
|
|
26
26
|
inflector: inflector,
|
|
27
27
|
key: key,
|
|
28
28
|
namespace: namespace,
|
|
29
29
|
base_path: base_path
|
|
30
|
-
).create
|
|
30
|
+
).create(force:)
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
private
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/files"
|
|
4
|
+
require_relative "../constants"
|
|
5
|
+
require_relative "../../errors"
|
|
6
|
+
|
|
7
|
+
module Hanami
|
|
8
|
+
module CLI
|
|
9
|
+
module Generators
|
|
10
|
+
module App
|
|
11
|
+
# @api private
|
|
12
|
+
class Mailer
|
|
13
|
+
TEMPLATES_FOLDER = "templates/mailers"
|
|
14
|
+
DEFAULT_TEMPLATE_ENGINE = "erb"
|
|
15
|
+
|
|
16
|
+
def initialize(fs:, inflector:, out: $stdout)
|
|
17
|
+
@fs = fs
|
|
18
|
+
@inflector = inflector
|
|
19
|
+
@out = out
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def call(key:, namespace:, base_path:, template_engine: DEFAULT_TEMPLATE_ENGINE, force: false, **)
|
|
23
|
+
mailer_class_file(key:, namespace:, base_path:).then do |mailer_class|
|
|
24
|
+
mailer_class.create(force:)
|
|
25
|
+
mailer_class_name = mailer_class.fully_qualified_name
|
|
26
|
+
create_template_files(key:, base_path:, mailer_class_name:, template_engine:, force:)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
attr_reader :fs, :inflector, :out
|
|
33
|
+
|
|
34
|
+
def mailer_class_file(key:, namespace:, base_path:)
|
|
35
|
+
RubyClassFile.new(
|
|
36
|
+
fs:, inflector:,
|
|
37
|
+
namespace:,
|
|
38
|
+
key:,
|
|
39
|
+
base_path:,
|
|
40
|
+
parent_class_name: "#{inflector.camelize(namespace)}::Mailer",
|
|
41
|
+
extra_namespace: "Mailers"
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def create_template_files(key:, base_path:, mailer_class_name:, template_engine:, force:)
|
|
46
|
+
key_parts = key.split(KEY_SEPARATOR)
|
|
47
|
+
class_name_from_key = key_parts.pop # takes last segment as the class name
|
|
48
|
+
module_names_from_key = key_parts # the rest of the segments are the module names
|
|
49
|
+
|
|
50
|
+
html_path = template_path(base_path, module_names_from_key, class_name_from_key, "html", template_engine)
|
|
51
|
+
fs.create(html_path, html_body(mailer_class_name, template_engine), force:)
|
|
52
|
+
|
|
53
|
+
text_path = template_path(base_path, module_names_from_key, class_name_from_key, "text", "erb")
|
|
54
|
+
fs.create(text_path, text_body(mailer_class_name), force:)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def template_path(base_path, module_names, name, format, engine)
|
|
58
|
+
fs.join(
|
|
59
|
+
base_path,
|
|
60
|
+
TEMPLATES_FOLDER,
|
|
61
|
+
module_names,
|
|
62
|
+
"#{name}.#{format}.#{engine}"
|
|
63
|
+
)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def html_body(mailer_class_name, engine)
|
|
67
|
+
case engine
|
|
68
|
+
when "erb" then "<h1>#{mailer_class_name}</h1>\n"
|
|
69
|
+
when "haml" then "%h1 #{mailer_class_name}\n"
|
|
70
|
+
when "slim" then "h1 #{mailer_class_name}\n"
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def text_body(mailer_class_name)
|
|
75
|
+
"#{mailer_class_name}\n"
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -19,7 +19,7 @@ module Hanami
|
|
|
19
19
|
|
|
20
20
|
# @since 2.2.0
|
|
21
21
|
# @api private
|
|
22
|
-
def call(key:, namespace:, base_path:)
|
|
22
|
+
def call(key:, namespace:, base_path:, force: false, **)
|
|
23
23
|
RubyClassFile.new(
|
|
24
24
|
fs: fs,
|
|
25
25
|
inflector: inflector,
|
|
@@ -28,7 +28,7 @@ module Hanami
|
|
|
28
28
|
base_path: base_path,
|
|
29
29
|
parent_class_name: "#{inflector.camelize(namespace)}::Operation",
|
|
30
30
|
body: ["def call", "end"]
|
|
31
|
-
).create
|
|
31
|
+
).create(force:)
|
|
32
32
|
|
|
33
33
|
unless key.match?(KEY_SEPARATOR)
|
|
34
34
|
out.puts(
|
|
@@ -20,10 +20,10 @@ module Hanami
|
|
|
20
20
|
|
|
21
21
|
# @since 2.1.0
|
|
22
22
|
# @api private
|
|
23
|
-
def call(key:, namespace:, base_path:, **)
|
|
23
|
+
def call(key:, namespace:, base_path:, force: false, **)
|
|
24
24
|
create_app_base_part_if_missing(key:, namespace:, base_path:)
|
|
25
25
|
create_slice_part_if_missing(key:, namespace:, base_path:) unless namespace == Hanami.app.namespace
|
|
26
|
-
create_generated_part(key:, namespace:, base_path:)
|
|
26
|
+
create_generated_part(key:, namespace:, base_path:, force:)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
private
|
|
@@ -60,7 +60,7 @@ module Hanami
|
|
|
60
60
|
).create
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
-
def create_generated_part(key:, namespace:, base_path:)
|
|
63
|
+
def create_generated_part(key:, namespace:, base_path:, force: false)
|
|
64
64
|
RubyClassFile.new(
|
|
65
65
|
fs: fs,
|
|
66
66
|
inflector: inflector,
|
|
@@ -69,7 +69,7 @@ module Hanami
|
|
|
69
69
|
base_path: base_path,
|
|
70
70
|
parent_class_name: "#{inflector.camelize(namespace)}::Views::Part",
|
|
71
71
|
auto_register: false
|
|
72
|
-
).create
|
|
72
|
+
).create(force:)
|
|
73
73
|
end
|
|
74
74
|
end
|
|
75
75
|
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hanami
|
|
4
|
+
module CLI
|
|
5
|
+
module Generators
|
|
6
|
+
module App
|
|
7
|
+
# @api private
|
|
8
|
+
class Provider
|
|
9
|
+
def initialize(fs:, inflector:, out: $stdout)
|
|
10
|
+
@fs = fs
|
|
11
|
+
@inflector = inflector
|
|
12
|
+
@out = out
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def call(key:, namespace:, base_path:, force: false, **_opts)
|
|
16
|
+
name = inflector.underscore(key)
|
|
17
|
+
|
|
18
|
+
registrar =
|
|
19
|
+
if base_path == "app"
|
|
20
|
+
"Hanami.app"
|
|
21
|
+
else
|
|
22
|
+
"#{inflector.camelize(namespace)}::Slice"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
base_path = nil if base_path == "app" # Providers live in config/, not app/
|
|
26
|
+
path = fs.join(*[base_path, "config", "providers", "#{name}.rb"].compact)
|
|
27
|
+
|
|
28
|
+
fs.create(path, file_contents(registrar, name), force:)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
attr_reader :fs, :inflector, :out
|
|
34
|
+
|
|
35
|
+
def file_contents(registrar, name)
|
|
36
|
+
<<~RUBY
|
|
37
|
+
# frozen_string_literal: true
|
|
38
|
+
|
|
39
|
+
#{registrar}.register_provider :#{name} do
|
|
40
|
+
# Define your provider here.
|
|
41
|
+
#
|
|
42
|
+
# See https://hanakai.org/learn/hanami/app/providers for details.
|
|
43
|
+
|
|
44
|
+
start do
|
|
45
|
+
# Set up and register the provider's components.
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
RUBY
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -19,7 +19,7 @@ module Hanami
|
|
|
19
19
|
|
|
20
20
|
# @since 2.2.0
|
|
21
21
|
# @api private
|
|
22
|
-
def call(key:, namespace:, base_path:, gateway:)
|
|
22
|
+
def call(key:, namespace:, base_path:, gateway:, force: false)
|
|
23
23
|
schema_name = inflector.underscore(key.split(KEY_SEPARATOR).last)
|
|
24
24
|
body_content = ["schema :#{schema_name}, infer: true"]
|
|
25
25
|
|
|
@@ -34,7 +34,7 @@ module Hanami
|
|
|
34
34
|
extra_namespace: "Relations",
|
|
35
35
|
parent_class_name: "#{inflector.camelize(namespace)}::DB::Relation",
|
|
36
36
|
body: body_content
|
|
37
|
-
).create
|
|
37
|
+
).create(force:)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
private
|
|
@@ -17,7 +17,7 @@ module Hanami
|
|
|
17
17
|
|
|
18
18
|
# @since 2.2.0
|
|
19
19
|
# @api private
|
|
20
|
-
def call(key:, namespace:, base_path:)
|
|
20
|
+
def call(key:, namespace:, base_path:, force: false)
|
|
21
21
|
RubyClassFile.new(
|
|
22
22
|
fs: fs,
|
|
23
23
|
inflector: inflector,
|
|
@@ -26,7 +26,7 @@ module Hanami
|
|
|
26
26
|
base_path: base_path,
|
|
27
27
|
extra_namespace: "Repos",
|
|
28
28
|
parent_class_name: "#{inflector.camelize(namespace)}::DB::Repo"
|
|
29
|
-
).create
|
|
29
|
+
).create(force:)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
private
|