hanami-cli 2.0.3 → 2.1.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.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -0
  3. data/Gemfile +3 -2
  4. data/LICENSE.md +1 -1
  5. data/README.md +1 -1
  6. data/hanami-cli.gemspec +1 -1
  7. data/lib/hanami/cli/bundler.rb +21 -7
  8. data/lib/hanami/cli/command.rb +4 -4
  9. data/lib/hanami/cli/commands/app/assets/command.rb +82 -0
  10. data/lib/hanami/cli/commands/app/assets/compile.rb +32 -0
  11. data/lib/hanami/cli/commands/app/assets/watch.rb +36 -0
  12. data/lib/hanami/cli/commands/app/assets.rb +16 -0
  13. data/lib/hanami/cli/commands/app/command.rb +2 -2
  14. data/lib/hanami/cli/commands/app/db/rollback.rb +30 -6
  15. data/lib/hanami/cli/commands/app/db/utils/database.rb +5 -1
  16. data/lib/hanami/cli/commands/app/dev.rb +57 -0
  17. data/lib/hanami/cli/commands/app/generate/action.rb +5 -4
  18. data/lib/hanami/cli/commands/app/generate/view.rb +57 -0
  19. data/lib/hanami/cli/commands/app/generate.rb +2 -2
  20. data/lib/hanami/cli/commands/app/install.rb +44 -1
  21. data/lib/hanami/cli/commands/app.rb +9 -0
  22. data/lib/hanami/cli/commands/gem/new.rb +41 -7
  23. data/lib/hanami/cli/errors.rb +30 -0
  24. data/lib/hanami/cli/files.rb +8 -2
  25. data/lib/hanami/cli/generators/app/action/action.erb +5 -1
  26. data/lib/hanami/cli/generators/app/action/slice_action.erb +5 -1
  27. data/lib/hanami/cli/generators/app/action/slice_template.html.erb +1 -0
  28. data/lib/hanami/cli/generators/app/action/slice_view.erb +10 -0
  29. data/lib/hanami/cli/generators/app/action/template.html.erb +1 -2
  30. data/lib/hanami/cli/generators/app/action/view.erb +2 -5
  31. data/lib/hanami/cli/generators/app/action.rb +34 -22
  32. data/lib/hanami/cli/generators/app/action_context.rb +1 -1
  33. data/lib/hanami/cli/generators/app/slice/app_css.erb +5 -0
  34. data/lib/hanami/cli/generators/app/slice/app_js.erb +1 -0
  35. data/lib/hanami/cli/generators/app/slice/app_layout.erb +18 -0
  36. data/lib/hanami/cli/generators/app/slice/helpers.erb +10 -0
  37. data/lib/hanami/cli/generators/app/slice/view.erb +0 -2
  38. data/lib/hanami/cli/generators/app/slice.rb +15 -6
  39. data/lib/hanami/cli/generators/app/slice_context.rb +18 -0
  40. data/lib/hanami/cli/generators/app/view/app_template.html.erb +1 -0
  41. data/lib/hanami/cli/generators/app/view/app_view.erb +10 -0
  42. data/lib/hanami/cli/generators/app/view/slice_template.html.erb +1 -0
  43. data/lib/hanami/cli/generators/app/view/slice_view.erb +10 -0
  44. data/lib/hanami/cli/generators/app/view.rb +89 -0
  45. data/lib/hanami/cli/generators/app/view_context.rb +100 -0
  46. data/lib/hanami/cli/generators/context.rb +53 -4
  47. data/lib/hanami/cli/generators/gem/app/404.html +11 -0
  48. data/lib/hanami/cli/generators/gem/app/500.html +11 -0
  49. data/lib/hanami/cli/generators/gem/app/app_css.erb +5 -0
  50. data/lib/hanami/cli/generators/gem/app/app_js.erb +1 -0
  51. data/lib/hanami/cli/generators/gem/app/app_layout.erb +18 -0
  52. data/lib/hanami/cli/generators/gem/app/favicon.ico +0 -0
  53. data/lib/hanami/cli/generators/gem/app/gemfile.erb +14 -6
  54. data/lib/hanami/cli/generators/gem/app/gitignore.erb +4 -0
  55. data/lib/hanami/cli/generators/gem/app/helpers.erb +10 -0
  56. data/lib/hanami/cli/generators/gem/app/procfile.erb +4 -0
  57. data/lib/hanami/cli/generators/gem/app/puma.erb +5 -3
  58. data/lib/hanami/cli/generators/gem/app/view.erb +9 -0
  59. data/lib/hanami/cli/generators/gem/app.rb +15 -1
  60. data/lib/hanami/cli/interactive_system_call.rb +64 -0
  61. data/lib/hanami/cli/system_call.rb +8 -2
  62. data/lib/hanami/cli/url.rb +1 -1
  63. data/lib/hanami/cli/version.rb +1 -1
  64. metadata +35 -7
@@ -14,14 +14,23 @@ 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
25
34
  end
26
35
  end
27
36
  end
@@ -10,21 +10,53 @@ 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", # rubocop:disable Layout/LineLength
26
- "bookshelf --skip-install # Generate a new Hanami app, but it skips Hanami installation"
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
28
60
 
29
61
  # @since 2.0.0
30
62
  # @api private
@@ -42,21 +74,22 @@ module Hanami
42
74
 
43
75
  # @since 2.0.0
44
76
  # @api private
45
- def call(app:, skip_install: SKIP_INSTALL_DEFAULT, **)
77
+ def call(app:, head: HEAD_DEFAULT, skip_install: SKIP_INSTALL_DEFAULT, skip_assets: SKIP_ASSETS_DEFAULT, **)
46
78
  app = inflector.underscore(app)
47
79
 
48
80
  raise PathAlreadyExistsError.new(app) if fs.exist?(app)
49
81
 
50
82
  fs.mkdir(app)
51
83
  fs.chdir(app) do
52
- generator.call(app) do
84
+ context = Generators::Context.new(inflector, app, head: head, skip_assets: skip_assets)
85
+ generator.call(app, context: context) do
53
86
  if skip_install
54
87
  out.puts "Skipping installation, please enter `#{app}' directory and run `bundle exec hanami install'"
55
88
  else
56
89
  out.puts "Running Bundler install..."
57
90
  bundler.install!
58
91
  out.puts "Running Hanami install..."
59
- run_install_commmand!
92
+ run_install_commmand!(head: head)
60
93
  end
61
94
  end
62
95
  end
@@ -67,8 +100,9 @@ module Hanami
67
100
  attr_reader :bundler
68
101
  attr_reader :generator
69
102
 
70
- def run_install_commmand!
71
- bundler.exec("hanami install").tap do |result|
103
+ def run_install_commmand!(head:)
104
+ head_flag = head ? " --head" : ""
105
+ bundler.exec("hanami install#{head_flag}").tap do |result|
72
106
  raise HanamiInstallError.new(result.err) unless result.successful?
73
107
  end
74
108
  end
@@ -2,60 +2,90 @@
2
2
 
3
3
  module Hanami
4
4
  module CLI
5
+ # @since 0.1.0
6
+ # @api public
5
7
  class Error < StandardError
6
8
  end
7
9
 
10
+ # @since 2.0.0
11
+ # @api public
8
12
  class NotImplementedError < Error
9
13
  end
10
14
 
15
+ # @since 2.0.0
16
+ # @api public
11
17
  class BundleInstallError < Error
12
18
  def initialize(message)
13
19
  super("`bundle install' failed\n\n\n#{message.inspect}")
14
20
  end
15
21
  end
16
22
 
23
+ # @since 2.0.0
24
+ # @api public
17
25
  class HanamiInstallError < Error
18
26
  def initialize(message)
19
27
  super("`hanami install' failed\n\n\n#{message.inspect}")
20
28
  end
21
29
  end
22
30
 
31
+ # @since 2.1.0
32
+ # @api public
33
+ class HanamiExecError < Error
34
+ def initialize(cmd, message)
35
+ super("`bundle exec hanami #{cmd}' failed\n\n\n#{message.inspect}")
36
+ end
37
+ end
38
+
39
+ # @since 2.0.0
40
+ # @api public
23
41
  class PathAlreadyExistsError < Error
24
42
  def initialize(path)
25
43
  super("Cannot create new Hanami app in an existing path: `#{path}'")
26
44
  end
27
45
  end
28
46
 
47
+ # @since 2.0.0
48
+ # @api public
29
49
  class MissingSliceError < Error
30
50
  def initialize(slice)
31
51
  super("slice `#{slice}' is missing, please generate with `hanami generate slice #{slice}'")
32
52
  end
33
53
  end
34
54
 
55
+ # @since 2.0.0
56
+ # @api public
35
57
  class InvalidURLError < Error
36
58
  def initialize(url)
37
59
  super("invalid URL: `#{url}'")
38
60
  end
39
61
  end
40
62
 
63
+ # @since 2.0.0
64
+ # @api public
41
65
  class InvalidURLPrefixError < Error
42
66
  def initialize(url)
43
67
  super("invalid URL prefix: `#{url}'")
44
68
  end
45
69
  end
46
70
 
71
+ # @since 2.0.0
72
+ # @api public
47
73
  class InvalidActionNameError < Error
48
74
  def initialize(name)
49
75
  super("cannot parse controller and action name: `#{name}'\n\texample: `hanami generate action users.show'")
50
76
  end
51
77
  end
52
78
 
79
+ # @since 2.0.0
80
+ # @api public
53
81
  class UnknownHTTPMethodError < Error
54
82
  def initialize(name)
55
83
  super("unknown HTTP method: `#{name}'")
56
84
  end
57
85
  end
58
86
 
87
+ # @since 2.0.0
88
+ # @api public
59
89
  class UnsupportedDatabaseSchemeError < Error
60
90
  def initialize(scheme)
61
91
  super("`#{scheme}' is not a supported db scheme")
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "dry/files"
4
+
3
5
  module Hanami
4
6
  module CLI
5
7
  # @since 2.0.0
@@ -29,7 +31,7 @@ module Hanami
29
31
  def mkdir(path)
30
32
  unless exist?(path)
31
33
  super
32
- created("#{path}/")
34
+ created(_path(path))
33
35
  end
34
36
  end
35
37
 
@@ -53,7 +55,11 @@ module Hanami
53
55
  end
54
56
 
55
57
  def within_folder(path)
56
- out.puts "-> Within #{path}/"
58
+ out.puts "-> Within #{_path(path)}"
59
+ end
60
+
61
+ def _path(path)
62
+ path + ::File::SEPARATOR
57
63
  end
58
64
  end
59
65
  end
@@ -4,8 +4,12 @@ module <%= camelized_app_name %>
4
4
  module Actions
5
5
  <%= module_controller_declaration %>
6
6
  <%= module_controller_offset %>class <%= camelized_action_name %> < <%= camelized_app_name %>::Action
7
- <%= module_controller_offset %> def handle(*, response)
7
+ <%- if bundled_views? -%>
8
+ <%= module_controller_offset %> def handle(request, response)
9
+ <%- else -%>
10
+ <%= module_controller_offset %> def handle(request, response)
8
11
  <%= module_controller_offset %> response.body = self.class.name
12
+ <%- end -%>
9
13
  <%= module_controller_offset %> end
10
14
  <%= module_controller_offset %>end
11
15
  <%= module_controller_end %>
@@ -4,8 +4,12 @@ module <%= camelized_slice_name %>
4
4
  module Actions
5
5
  <%= module_controller_declaration %>
6
6
  <%= module_controller_offset %>class <%= camelized_action_name %> < <%= camelized_slice_name %>::Action
7
- <%= module_controller_offset %> def handle(*, response)
7
+ <%- if bundled_views? -%>
8
+ <%= module_controller_offset %> def handle(request, response)
9
+ <%- else -%>
10
+ <%= module_controller_offset %> def handle(request, response)
8
11
  <%= module_controller_offset %> response.body = self.class.name
12
+ <%- end -%>
9
13
  <%= module_controller_offset %> end
10
14
  <%= module_controller_offset %>end
11
15
  <%= module_controller_end %>
@@ -0,0 +1 @@
1
+ <h1><%= camelized_slice_name %>::Views::<%= camelized_controller_name %>::<%= camelized_action_name %></h1>
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module <%= camelized_slice_name %>
4
+ module Views
5
+ <%= module_controller_declaration %>
6
+ <%= module_controller_offset %>class <%= camelized_action_name %> < <%= camelized_slice_name %>::View
7
+ <%= module_controller_offset %>end
8
+ <%= module_controller_end %>
9
+ end
10
+ end
@@ -1,2 +1 @@
1
- <h1><%= camelized_slice_name %>::Views::<%= camelized_controller_name %>::<%= camelized_action_name %></h1>
2
- <h2><%= template_path %></h2>
1
+ <h1><%= camelized_app_name %>::Views::<%= camelized_controller_name %>::<%= camelized_action_name %></h1>
@@ -1,12 +1,9 @@
1
- # auto_register: false
2
1
  # frozen_string_literal: true
3
2
 
4
- require "<%= underscored_slice_name %>/view"
5
-
6
- module <%= camelized_slice_name %>
3
+ module <%= camelized_app_name %>
7
4
  module Views
8
5
  <%= module_controller_declaration %>
9
- <%= module_controller_offset %>class <%= camelized_action_name %> < <%= camelized_slice_name %>::View
6
+ <%= module_controller_offset %>class <%= camelized_action_name %> < <%= camelized_app_name %>::View
10
7
  <%= module_controller_offset %>end
11
8
  <%= module_controller_end %>
12
9
  end
@@ -19,11 +19,10 @@ module Hanami
19
19
  @inflector = inflector
20
20
  end
21
21
 
22
- # rubocop:disable Layout/LineLength
23
-
24
22
  # @since 2.0.0
25
23
  # @api private
26
- def call(app, controller, action, url, http, format, skip_view, slice, context: ActionContext.new(inflector, app, slice, controller, action))
24
+ def call(app, controller, action, url, http, format, skip_view, slice, context: nil)
25
+ context ||= ActionContext.new(inflector, app, slice, controller, action)
27
26
  if slice
28
27
  generate_for_slice(controller, action, url, http, format, skip_view, slice, context)
29
28
  else
@@ -31,8 +30,6 @@ module Hanami
31
30
  end
32
31
  end
33
32
 
34
- # rubocop:enable Layout/LineLength
35
-
36
33
  private
37
34
 
38
35
  ROUTE_HTTP_METHODS = %w[get post delete put patch trace options link unlink].freeze
@@ -66,7 +63,8 @@ module Hanami
66
63
 
67
64
  attr_reader :inflector
68
65
 
69
- def generate_for_slice(controller, action, url, http, _format, _skip_view, slice, context)
66
+ # rubocop:disable Metrics/AbcSize
67
+ def generate_for_slice(controller, action, url, http, format, skip_view, slice, context)
70
68
  slice_directory = fs.join("slices", slice)
71
69
  raise MissingSliceError.new(slice) unless fs.directory?(slice_directory)
72
70
 
@@ -79,16 +77,17 @@ module Hanami
79
77
  fs.mkdir(directory = fs.join(slice_directory, "actions", controller))
80
78
  fs.write(fs.join(directory, "#{action}.rb"), t("slice_action.erb", context))
81
79
 
82
- # unless skip_view
83
- # fs.mkdir(directory = fs.join(slice_directory, "views", controller))
84
- # fs.write(fs.join(directory, "#{action}.rb"), t("view.erb", context))
85
- #
86
- # fs.mkdir(directory = fs.join(slice_directory, "templates", controller))
87
- # fs.write(fs.join(directory, "#{action}.#{format}.erb"), t(template_format(format), context))
88
- # end
80
+ unless skip_view
81
+ fs.mkdir(directory = fs.join(slice_directory, "views", controller))
82
+ fs.write(fs.join(directory, "#{action}.rb"), t("slice_view.erb", context))
83
+
84
+ fs.mkdir(directory = fs.join(slice_directory, "templates", controller))
85
+ fs.write(fs.join(directory, "#{action}.#{format}.erb"),
86
+ t(template_with_format_ext("slice_template", format), context))
87
+ end
89
88
  end
90
89
 
91
- def generate_for_app(controller, action, url, http, _format, _skip_view, context)
90
+ def generate_for_app(controller, action, url, http, format, skip_view, context)
92
91
  fs.inject_line_at_class_bottom(
93
92
  fs.join("config", "routes.rb"),
94
93
  "class Routes",
@@ -97,7 +96,17 @@ module Hanami
97
96
 
98
97
  fs.mkdir(directory = fs.join("app", "actions", controller))
99
98
  fs.write(fs.join(directory, "#{action}.rb"), t("action.erb", context))
99
+
100
+ unless skip_view
101
+ fs.mkdir(directory = fs.join("app", "views", controller))
102
+ fs.write(fs.join(directory, "#{action}.rb"), t("view.erb", context))
103
+
104
+ fs.mkdir(directory = fs.join("app", "templates", controller))
105
+ fs.write(fs.join(directory, "#{action}.#{format}.erb"),
106
+ t(template_with_format_ext("template", format), context))
107
+ end
100
108
  end
109
+ # rubocop:enable Metrics/AbcSize
101
110
 
102
111
  def slice_matcher(slice)
103
112
  /slice[[:space:]]*:#{slice}/
@@ -108,20 +117,23 @@ module Hanami
108
117
  http)} "#{route_url(controller, action, url)}", to: "#{controller.join('.')}.#{action}")
109
118
  end
110
119
 
111
- def template_format(format)
112
- case format.to_sym
113
- when :html
114
- "template.html.erb"
115
- else
116
- "template.erb"
117
- end
120
+ def template_with_format_ext(name, format)
121
+ ext =
122
+ case format.to_sym
123
+ when :html
124
+ ".html.erb"
125
+ else
126
+ ".erb"
127
+ end
128
+
129
+ "#{name}#{ext}"
118
130
  end
119
131
 
120
132
  def template(path, context)
121
133
  require "erb"
122
134
 
123
135
  ERB.new(
124
- File.read(__dir__ + "/action/#{path}")
136
+ File.read(__dir__ + "/action/#{path}"), trim_mode: "-",
125
137
  ).result(context.ctx)
126
138
  end
127
139
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "./slice_context"
3
+ require_relative "slice_context"
4
4
  require "dry/files/path"
5
5
 
6
6
  module Hanami
@@ -0,0 +1,5 @@
1
+ body {
2
+ background-color: #fff;
3
+ color: #000;
4
+ font-family: sans-serif;
5
+ }
@@ -0,0 +1 @@
1
+ import "../css/app.css";
@@ -0,0 +1,18 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title><%= humanized_app_name %> - <%= humanized_slice_name %></title>
7
+ <%- if bundled_assets? -%>
8
+ <%%= favicon %>
9
+ <%= stylesheet_erb_tag %>
10
+ <%- end -%>
11
+ </head>
12
+ <body>
13
+ <%%= yield %>
14
+ <%- if bundled_assets? -%>
15
+ <%= javascript_erb_tag %>
16
+ <%- end -%>
17
+ </body>
18
+ </html>
@@ -0,0 +1,10 @@
1
+ # auto_register: false
2
+ # frozen_string_literal: true
3
+
4
+ module <%= camelized_slice_name %>
5
+ module Views
6
+ module Helpers
7
+ # Add your view helpers here
8
+ end
9
+ end
10
+ end
@@ -1,8 +1,6 @@
1
1
  # auto_register: false
2
2
  # frozen_string_literal: true
3
3
 
4
- require "<%= underscored_app_name %>/view"
5
-
6
4
  module <%= camelized_slice_name %>
7
5
  class View < <%= camelized_app_name %>::View
8
6
  end
@@ -19,7 +19,7 @@ module Hanami
19
19
 
20
20
  # @since 2.0.0
21
21
  # @api private
22
- def call(app, slice, url, context: SliceContext.new(inflector, app, slice, url))
22
+ def call(app, slice, url, context: SliceContext.new(inflector, app, slice, url)) # rubocop:disable Metrics/AbcSize
23
23
  fs.inject_line_at_class_bottom(
24
24
  fs.join("config", "routes.rb"), "class Routes", t("routes.erb", context).chomp
25
25
  )
@@ -28,14 +28,22 @@ module Hanami
28
28
 
29
29
  # fs.write("#{directory}/config/slice.rb", t("slice.erb", context))
30
30
  fs.write(fs.join(directory, "action.rb"), t("action.erb", context))
31
- # fs.write(fs.join(directory, "/view.rb"), t("view.erb", context))
31
+ fs.write(fs.join(directory, "view.rb"), t("view.erb", context))
32
+ fs.write(fs.join(directory, "views", "helpers.rb"), t("helpers.erb", context))
33
+ fs.write(fs.join(directory, "templates", "layouts", "app.html.erb"), t("app_layout.erb", context))
34
+
35
+ if context.bundled_assets?
36
+ fs.write(fs.join(directory, "assets", "js", "app.js"), t("app_js.erb", context))
37
+ fs.write(fs.join(directory, "assets", "css", "app.css"), t("app_css.erb", context))
38
+ end
39
+
32
40
  # fs.write(fs.join(directory, "/entities.rb"), t("entities.erb", context))
33
41
  # fs.write(fs.join(directory, "/repository.rb"), t("repository.erb", context))
34
42
 
35
43
  fs.write(fs.join(directory, "actions/.keep"), t("keep.erb", context))
36
- # fs.write(fs.join(directory, views/.keep"), t("keep.erb", context))
37
- # fs.write(fs.join(directory, templates/.keep"), t("keep.erb", context))
38
- # fs.write(fs.join(directory, templates/layouts/.keep"), t("keep.erb", context))
44
+ fs.write(fs.join(directory, "views/.keep"), t("keep.erb", context))
45
+ fs.write(fs.join(directory, "templates/.keep"), t("keep.erb", context))
46
+ fs.write(fs.join(directory, "templates/layouts/.keep"), t("keep.erb", context))
39
47
  # fs.write(fs.join(directory, entities/.keep"), t("keep.erb", context))
40
48
  # fs.write(fs.join(directory, repositories/.keep"), t("keep.erb", context))
41
49
  end
@@ -50,7 +58,8 @@ module Hanami
50
58
  require "erb"
51
59
 
52
60
  ERB.new(
53
- File.read(__dir__ + "/slice/#{path}")
61
+ File.read(__dir__ + "/slice/#{path}"),
62
+ trim_mode: "-"
54
63
  ).result(context.ctx)
55
64
  end
56
65
 
@@ -29,6 +29,24 @@ module Hanami
29
29
  inflector.underscore(slice)
30
30
  end
31
31
 
32
+ # @since 2.1.0
33
+ # @api private
34
+ def humanized_slice_name
35
+ inflector.humanize(slice)
36
+ end
37
+
38
+ # @since 2.1.0
39
+ # @api private
40
+ def stylesheet_erb_tag
41
+ %(<%= css "#{slice}/app" %>)
42
+ end
43
+
44
+ # @since 2.1.0
45
+ # @api private
46
+ def javascript_erb_tag
47
+ %(<%= js "#{slice}/app" %>)
48
+ end
49
+
32
50
  private
33
51
 
34
52
  attr_reader :slice
@@ -0,0 +1 @@
1
+ <h1><%= camelized_app_name %>::Views::<%= camelized_namespace %>::<%= camelized_name %></h1>
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module <%= camelized_app_name %>
4
+ module Views
5
+ <%= module_namespace_declaration %>
6
+ <%= module_namespace_offset %>class <%= camelized_name %> < <%= camelized_app_name %>::View
7
+ <%= module_namespace_offset %>end
8
+ <%= module_namespace_end %>
9
+ end
10
+ end
@@ -0,0 +1 @@
1
+ <h1><%= camelized_slice_name %>::Views::<%= camelized_namespace %>::<%= camelized_name %></h1>
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module <%= camelized_slice_name %>
4
+ module Views
5
+ <%= module_namespace_declaration %>
6
+ <%= module_namespace_offset %>class <%= camelized_name %> < <%= camelized_slice_name %>::View
7
+ <%= module_namespace_offset %>end
8
+ <%= module_namespace_end %>
9
+ end
10
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+ require "dry/files"
5
+ require_relative "../../errors"
6
+
7
+ module Hanami
8
+ module CLI
9
+ module Generators
10
+ module App
11
+ # @since 2.0.0
12
+ # @api private
13
+ class View
14
+ # @since 2.0.0
15
+ # @api private
16
+ def initialize(fs:, inflector:)
17
+ @fs = fs
18
+ @inflector = inflector
19
+ end
20
+
21
+ # @since 2.0.0
22
+ # @api private
23
+ def call(app, key, format, slice)
24
+ context = ViewContext.new(inflector, app, slice, key)
25
+
26
+ if slice
27
+ generate_for_slice(context, format, slice)
28
+ else
29
+ generate_for_app(context, format, slice)
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ attr_reader :fs
36
+
37
+ attr_reader :inflector
38
+
39
+ # rubocop:disable Metrics/AbcSize
40
+
41
+ def generate_for_slice(context, format, slice)
42
+ slice_directory = fs.join("slices", slice)
43
+ raise MissingSliceError.new(slice) unless fs.directory?(slice_directory)
44
+
45
+ fs.mkdir(directory = fs.join(slice_directory, "views", context.namespaces))
46
+ fs.write(fs.join(directory, "#{context.name}.rb"), t("slice_view.erb", context))
47
+
48
+ fs.mkdir(directory = fs.join(slice_directory, "templates", context.namespaces))
49
+ fs.write(fs.join(directory, "#{context.name}.#{format}.erb"),
50
+ t(template_with_format_ext("slice_template", format), context))
51
+ end
52
+
53
+ def generate_for_app(context, format, _slice)
54
+ fs.mkdir(directory = fs.join("app", "views", context.namespaces))
55
+ fs.write(fs.join(directory, "#{context.name}.rb"), t("app_view.erb", context))
56
+
57
+ fs.mkdir(directory = fs.join("app", "templates", context.namespaces))
58
+ fs.write(fs.join(directory, "#{context.name}.#{format}.erb"),
59
+ t(template_with_format_ext("app_template", format), context))
60
+ end
61
+
62
+ # rubocop:enable Metrics/AbcSize
63
+
64
+ def template_with_format_ext(name, format)
65
+ ext =
66
+ case format.to_sym
67
+ when :html
68
+ ".html.erb"
69
+ else
70
+ ".erb"
71
+ end
72
+
73
+ "#{name}#{ext}"
74
+ end
75
+
76
+ def template(path, context)
77
+ require "erb"
78
+
79
+ ERB.new(
80
+ File.read(__dir__ + "/view/#{path}")
81
+ ).result(context.ctx)
82
+ end
83
+
84
+ alias_method :t, :template
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end