hanami-cli 2.0.3 → 2.1.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
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