hanami-cli 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +2 -0
  3. data/{.action_hero.yml → .repobot.yml} +1 -3
  4. data/.rubocop.yml +6 -0
  5. data/CHANGELOG.md +115 -0
  6. data/Gemfile +7 -2
  7. data/LICENSE.md +1 -1
  8. data/README.md +1 -1
  9. data/hanami-cli.gemspec +1 -1
  10. data/lib/hanami/cli/bundler.rb +26 -9
  11. data/lib/hanami/cli/command.rb +28 -6
  12. data/lib/hanami/cli/commands/app/assets/command.rb +155 -0
  13. data/lib/hanami/cli/commands/app/assets/compile.rb +35 -0
  14. data/lib/hanami/cli/commands/app/assets/watch.rb +29 -0
  15. data/lib/hanami/cli/commands/app/assets.rb +16 -0
  16. data/lib/hanami/cli/commands/app/command.rb +5 -3
  17. data/lib/hanami/cli/commands/app/db/rollback.rb +30 -6
  18. data/lib/hanami/cli/commands/app/db/utils/database.rb +5 -1
  19. data/lib/hanami/cli/commands/app/dev.rb +50 -0
  20. data/lib/hanami/cli/commands/app/generate/action.rb +38 -9
  21. data/lib/hanami/cli/commands/app/generate/part.rb +57 -0
  22. data/lib/hanami/cli/commands/app/generate/slice.rb +6 -3
  23. data/lib/hanami/cli/commands/app/generate/view.rb +56 -0
  24. data/lib/hanami/cli/commands/app/generate.rb +2 -2
  25. data/lib/hanami/cli/commands/app/install.rb +16 -1
  26. data/lib/hanami/cli/commands/app/server.rb +6 -10
  27. data/lib/hanami/cli/commands/app.rb +10 -0
  28. data/lib/hanami/cli/commands/gem/new.rb +70 -12
  29. data/lib/hanami/cli/errors.rb +30 -0
  30. data/lib/hanami/cli/files.rb +8 -2
  31. data/lib/hanami/cli/generators/app/action/action.erb +5 -1
  32. data/lib/hanami/cli/generators/app/action/slice_action.erb +5 -1
  33. data/lib/hanami/cli/generators/app/action/slice_template.html.erb +1 -0
  34. data/lib/hanami/cli/generators/app/action/slice_view.erb +10 -0
  35. data/lib/hanami/cli/generators/app/action/template.html.erb +1 -2
  36. data/lib/hanami/cli/generators/app/action/view.erb +2 -5
  37. data/lib/hanami/cli/generators/app/action.rb +74 -22
  38. data/lib/hanami/cli/generators/app/action_context.rb +1 -1
  39. data/lib/hanami/cli/generators/app/part/app_base_part.erb +9 -0
  40. data/lib/hanami/cli/generators/app/part/app_part.erb +13 -0
  41. data/lib/hanami/cli/generators/app/part/slice_base_part.erb +9 -0
  42. data/lib/hanami/cli/generators/app/part/slice_part.erb +13 -0
  43. data/lib/hanami/cli/generators/app/part.rb +101 -0
  44. data/lib/hanami/cli/generators/app/part_context.rb +98 -0
  45. data/lib/hanami/cli/generators/app/slice/app_css.erb +5 -0
  46. data/lib/hanami/cli/generators/app/slice/app_js.erb +1 -0
  47. data/lib/hanami/cli/generators/app/slice/app_layout.erb +18 -0
  48. data/lib/hanami/cli/generators/app/slice/favicon.ico +0 -0
  49. data/lib/hanami/cli/generators/app/slice/helpers.erb +10 -0
  50. data/lib/hanami/cli/generators/app/slice/view.erb +0 -2
  51. data/lib/hanami/cli/generators/app/slice.rb +20 -6
  52. data/lib/hanami/cli/generators/app/slice_context.rb +18 -0
  53. data/lib/hanami/cli/generators/app/view/app_template.html.erb +1 -0
  54. data/lib/hanami/cli/generators/app/view/app_view.erb +10 -0
  55. data/lib/hanami/cli/generators/app/view/slice_template.html.erb +1 -0
  56. data/lib/hanami/cli/generators/app/view/slice_view.erb +10 -0
  57. data/lib/hanami/cli/generators/app/view.rb +89 -0
  58. data/lib/hanami/cli/generators/app/view_context.rb +100 -0
  59. data/lib/hanami/cli/generators/context.rb +72 -5
  60. data/lib/hanami/cli/generators/gem/app/404.html +82 -0
  61. data/lib/hanami/cli/generators/gem/app/500.html +82 -0
  62. data/lib/hanami/cli/generators/gem/app/app_css.erb +5 -0
  63. data/lib/hanami/cli/generators/gem/app/app_js.erb +1 -0
  64. data/lib/hanami/cli/generators/gem/app/app_layout.erb +18 -0
  65. data/lib/hanami/cli/generators/gem/app/assets.js +16 -0
  66. data/lib/hanami/cli/generators/gem/app/dev +8 -0
  67. data/lib/hanami/cli/generators/gem/app/favicon.ico +0 -0
  68. data/lib/hanami/cli/generators/gem/app/gemfile.erb +14 -6
  69. data/lib/hanami/cli/generators/gem/app/gitignore.erb +6 -0
  70. data/lib/hanami/cli/generators/gem/app/helpers.erb +10 -0
  71. data/lib/hanami/cli/generators/gem/app/package.json.erb +8 -0
  72. data/lib/hanami/cli/generators/gem/app/procfile.erb +4 -0
  73. data/lib/hanami/cli/generators/gem/app/puma.erb +39 -7
  74. data/lib/hanami/cli/generators/gem/app/routes.erb +1 -1
  75. data/lib/hanami/cli/generators/gem/app/view.erb +9 -0
  76. data/lib/hanami/cli/generators/gem/app.rb +25 -1
  77. data/lib/hanami/cli/generators/version.rb +14 -2
  78. data/lib/hanami/cli/interactive_system_call.rb +65 -0
  79. data/lib/hanami/cli/naming.rb +25 -0
  80. data/lib/hanami/cli/system_call.rb +8 -2
  81. data/lib/hanami/cli/url.rb +1 -1
  82. data/lib/hanami/cli/version.rb +1 -1
  83. metadata +47 -6
@@ -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,23 @@ 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
+ fs.write(fs.join(directory, "assets", "images", "favicon.ico"), file("favicon.ico"))
39
+ end
40
+
32
41
  # fs.write(fs.join(directory, "/entities.rb"), t("entities.erb", context))
33
42
  # fs.write(fs.join(directory, "/repository.rb"), t("repository.erb", context))
34
43
 
35
44
  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))
45
+ fs.write(fs.join(directory, "views/.keep"), t("keep.erb", context))
46
+ fs.write(fs.join(directory, "templates/.keep"), t("keep.erb", context))
47
+ fs.write(fs.join(directory, "templates/layouts/.keep"), t("keep.erb", context))
39
48
  # fs.write(fs.join(directory, entities/.keep"), t("keep.erb", context))
40
49
  # fs.write(fs.join(directory, repositories/.keep"), t("keep.erb", context))
41
50
  end
@@ -50,11 +59,16 @@ module Hanami
50
59
  require "erb"
51
60
 
52
61
  ERB.new(
53
- File.read(__dir__ + "/slice/#{path}")
62
+ File.read(__dir__ + "/slice/#{path}"),
63
+ trim_mode: "-"
54
64
  ).result(context.ctx)
55
65
  end
56
66
 
57
67
  alias_method :t, :template
68
+
69
+ def file(path)
70
+ File.read(File.join(__dir__, "slice", path))
71
+ end
58
72
  end
59
73
  end
60
74
  end
@@ -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
+ %(<%= stylesheet_tag "app" %>)
42
+ end
43
+
44
+ # @since 2.1.0
45
+ # @api private
46
+ def javascript_erb_tag
47
+ %(<%= javascript_tag "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
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "slice_context"
4
+ require "dry/files/path"
5
+
6
+ module Hanami
7
+ module CLI
8
+ module Generators
9
+ # @since 2.1.0
10
+ # @api private
11
+ module App
12
+ # @since 2.1.0
13
+ # @api private
14
+ class ViewContext < SliceContext
15
+ # TODO: move these constants somewhere that will let us reuse them
16
+ KEY_SEPARATOR = "."
17
+ private_constant :KEY_SEPARATOR
18
+
19
+ NAMESPACE_SEPARATOR = "::"
20
+ private_constant :NAMESPACE_SEPARATOR
21
+
22
+ INDENTATION = " "
23
+ private_constant :INDENTATION
24
+
25
+ OFFSET = INDENTATION * 2
26
+ private_constant :OFFSET
27
+
28
+ # @since 2.1.0
29
+ # @api private
30
+ attr_reader :key
31
+
32
+ # @since 2.1.0
33
+ # @api private
34
+ def initialize(inflector, app, slice, key)
35
+ @key = key
36
+ super(inflector, app, slice, nil)
37
+ end
38
+
39
+ # @since 2.1.0
40
+ # @api private
41
+ def namespaces
42
+ @namespaces ||= key.split(KEY_SEPARATOR)[..-2]
43
+ end
44
+
45
+ # @since 2.1.0
46
+ # @api private
47
+ def name
48
+ @name ||= key.split(KEY_SEPARATOR)[-1]
49
+ end
50
+
51
+ # @since 2.1.0
52
+ # @api private
53
+ def camelized_namespace
54
+ namespaces.map { inflector.camelize(_1) }.join(NAMESPACE_SEPARATOR)
55
+ end
56
+
57
+ # @since 2.1.0
58
+ # @api private
59
+ def camelized_name
60
+ inflector.camelize(name)
61
+ end
62
+
63
+ # @since 2.1.0
64
+ # @api private
65
+ def underscored_namespace
66
+ namespaces.map { inflector.underscore(_1) }
67
+ end
68
+
69
+ # @since 2.1.0
70
+ # @api private
71
+ def underscored_name
72
+ inflector.underscore(name)
73
+ end
74
+
75
+ # @since 2.1.0
76
+ # @api private
77
+ def module_namespace_declaration
78
+ namespaces.each_with_index.map { |token, i|
79
+ "#{OFFSET}#{INDENTATION * i}module #{inflector.camelize(token)}"
80
+ }.join($/)
81
+ end
82
+
83
+ # @since 2.1.0
84
+ # @api private
85
+ def module_namespace_end
86
+ namespaces.each_with_index.map { |_, i|
87
+ "#{OFFSET}#{INDENTATION * i}end"
88
+ }.reverse.join($/)
89
+ end
90
+
91
+ # @since 2.1.0
92
+ # @api private
93
+ def module_namespace_offset
94
+ "#{OFFSET}#{INDENTATION * namespaces.count}"
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "./version"
3
+ require_relative "version"
4
4
 
5
5
  module Hanami
6
6
  module CLI
@@ -12,9 +12,10 @@ module Hanami
12
12
  class Context
13
13
  # @since 2.0.0
14
14
  # @api private
15
- def initialize(inflector, app)
15
+ def initialize(inflector, app, **options)
16
16
  @inflector = inflector
17
17
  @app = app
18
+ @options = options
18
19
  end
19
20
 
20
21
  # @since 2.0.0
@@ -23,16 +24,36 @@ module Hanami
23
24
  binding
24
25
  end
25
26
 
27
+ def hanami_gem(name)
28
+ gem_name = name == "hanami" ? "hanami" : "hanami-#{name}"
29
+
30
+ %(gem "#{gem_name}", #{hanami_gem_version(name)})
31
+ end
32
+
26
33
  # @since 2.0.0
27
34
  # @api private
28
- def hanami_version
29
- Version.gem_requirement
35
+ def hanami_gem_version(gem_name)
36
+ if hanami_head?
37
+ %(github: "hanami/#{gem_name}", branch: "main")
38
+ else
39
+ %("#{Version.gem_requirement}")
40
+ end
41
+ end
42
+
43
+ # @since 2.1.0
44
+ # @api private
45
+ def hanami_assets_npm_package
46
+ if hanami_head?
47
+ %("hanami-assets": "hanami/assets-js#main")
48
+ else
49
+ %("hanami-assets": "#{Version.npm_package_requirement}")
50
+ end
30
51
  end
31
52
 
32
53
  # @since 2.0.0
33
54
  # @api private
34
55
  def camelized_app_name
35
- inflector.camelize(app)
56
+ inflector.camelize(app).gsub(/[^\p{Alnum}]/, "")
36
57
  end
37
58
 
38
59
  # @since 2.0.0
@@ -41,11 +62,57 @@ module Hanami
41
62
  inflector.underscore(app)
42
63
  end
43
64
 
65
+ # @since 2.1.0
66
+ # @api private
67
+ def humanized_app_name
68
+ inflector.humanize(app)
69
+ end
70
+
71
+ # @since 2.1.0
72
+ # @api private
73
+ def hanami_head?
74
+ options.fetch(:head)
75
+ end
76
+
77
+ # @since 2.1.0
78
+ # @api private
79
+ def generate_assets?
80
+ !options.fetch(:skip_assets, false)
81
+ end
82
+
83
+ # @since 2.1.0
84
+ # @api private
85
+ def bundled_views?
86
+ Hanami.bundled?("hanami-view")
87
+ end
88
+
89
+ # @since 2.1.0
90
+ # @api private
91
+ def bundled_assets?
92
+ Hanami.bundled?("hanami-assets")
93
+ end
94
+
95
+ # @since 2.1.0
96
+ # @api private
97
+ #
98
+ # @see https://rubyreferences.github.io/rubychanges/3.1.html#values-in-hash-literals-and-keyword-arguments-can-be-omitted
99
+ def ruby_omit_hash_values?
100
+ RUBY_VERSION >= "3.1"
101
+ end
102
+
44
103
  private
45
104
 
105
+ # @since 2.0.0
106
+ # @api private
46
107
  attr_reader :inflector
47
108
 
109
+ # @since 2.0.0
110
+ # @api private
48
111
  attr_reader :app
112
+
113
+ # @since 2.1.0
114
+ # @api private
115
+ attr_reader :options
49
116
  end
50
117
  end
51
118
  end
@@ -0,0 +1,82 @@
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>The page you were looking for doesn’t exist (404)</title>
7
+ <style>
8
+ :root {
9
+ --foreground-rgb: 0, 0, 0;
10
+ --background-rgb: 255, 255, 255;
11
+ --font-sans: ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
12
+ }
13
+
14
+ @media (prefers-color-scheme: dark) {
15
+ :root {
16
+ --foreground-rgb: 255, 255, 255;
17
+ --background-rgb: 0, 0, 0;
18
+ }
19
+ }
20
+
21
+ * {
22
+ box-sizing: border-box;
23
+ margin: 0;
24
+ padding: 0;
25
+ }
26
+
27
+ body,
28
+ html {
29
+ max-width: 100vw;
30
+ overflow-x: hidden;
31
+ font-size: 100%;
32
+ }
33
+
34
+ body {
35
+ color: rgb(var(--foreground-rgb));
36
+ background: rgb(var(--background-rgb));
37
+ font-family: var(--font-sans);
38
+ font-style: normal;
39
+ }
40
+
41
+ main {
42
+ display: flex;
43
+ flex-direction: column;
44
+ align-items: center;
45
+ justify-content: center;
46
+ height: 100vh;
47
+ padding: 0 4vw;
48
+ }
49
+
50
+ .message {
51
+ display: flex;
52
+ gap: 1rem;
53
+ flex-direction: column;
54
+ text-align: center;
55
+ }
56
+
57
+ .message h1 {
58
+ font-size: 2rem;
59
+ font-weight: 500;
60
+ }
61
+
62
+ p {
63
+ line-height: 1.6;
64
+ }
65
+
66
+ @media (prefers-color-scheme: dark) {
67
+ html {
68
+ color-scheme: dark;
69
+ }
70
+ }
71
+ </style>
72
+ </head>
73
+ <body>
74
+ <!-- This file lives in public/404.html -->
75
+ <main>
76
+ <div class="message">
77
+ <h1>404</h1>
78
+ <p>The page you were looking for doesn’t exist.</p>
79
+ </div>
80
+ </main>
81
+ </body>
82
+ </html>
@@ -0,0 +1,82 @@
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>We’re sorry, but something went wrong (500)</title>
7
+ <style>
8
+ :root {
9
+ --foreground-rgb: 0, 0, 0;
10
+ --background-rgb: 255, 255, 255;
11
+ --font-sans: ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
12
+ }
13
+
14
+ @media (prefers-color-scheme: dark) {
15
+ :root {
16
+ --foreground-rgb: 255, 255, 255;
17
+ --background-rgb: 0, 0, 0;
18
+ }
19
+ }
20
+
21
+ * {
22
+ box-sizing: border-box;
23
+ margin: 0;
24
+ padding: 0;
25
+ }
26
+
27
+ body,
28
+ html {
29
+ max-width: 100vw;
30
+ overflow-x: hidden;
31
+ font-size: 100%;
32
+ }
33
+
34
+ body {
35
+ color: rgb(var(--foreground-rgb));
36
+ background: rgb(var(--background-rgb));
37
+ font-family: var(--font-sans);
38
+ font-style: normal;
39
+ }
40
+
41
+ main {
42
+ display: flex;
43
+ flex-direction: column;
44
+ align-items: center;
45
+ justify-content: center;
46
+ height: 100vh;
47
+ padding: 0 4vw;
48
+ }
49
+
50
+ .message {
51
+ display: flex;
52
+ gap: 1rem;
53
+ flex-direction: column;
54
+ text-align: center;
55
+ }
56
+
57
+ .message h1 {
58
+ font-size: 2rem;
59
+ font-weight: 500;
60
+ }
61
+
62
+ p {
63
+ line-height: 1.6;
64
+ }
65
+
66
+ @media (prefers-color-scheme: dark) {
67
+ html {
68
+ color-scheme: dark;
69
+ }
70
+ }
71
+ </style>
72
+ </head>
73
+ <body>
74
+ <!-- This file lives in public/500.html -->
75
+ <main>
76
+ <div class="message">
77
+ <h1>500</h1>
78
+ <p>We’re sorry, but something went wrong.</p>
79
+ </div>
80
+ </main>
81
+ </body>
82
+ </html>
@@ -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 %></title>
7
+ <%- if generate_assets? -%>
8
+ <%%= favicon_tag %>
9
+ <%%= stylesheet_tag "app" %>
10
+ <%- end -%>
11
+ </head>
12
+ <body>
13
+ <%%= yield %>
14
+ <%- if generate_assets? -%>
15
+ <%%= javascript_tag "app" %>
16
+ <%- end -%>
17
+ </body>
18
+ </html>
@@ -0,0 +1,16 @@
1
+ import * as assets from "hanami-assets";
2
+
3
+ await assets.run();
4
+
5
+ // To provide additional esbuild (https://esbuild.github.io) options, use the following:
6
+ //
7
+ // Read more at: https://guides.hanamirb.org/assets/customization/
8
+ //
9
+ // await assets.run({
10
+ // esbuildOptionsFn: (args, esbuildOptions) => {
11
+ // // Add to esbuildOptions here. Use `args.watch` as a condition for different options for
12
+ // // compile vs watch.
13
+ //
14
+ // return esbuildOptions;
15
+ // }
16
+ // });
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env sh
2
+
3
+ if ! gem list foreman -i --silent; then
4
+ echo "Installing foreman..."
5
+ gem install foreman
6
+ fi
7
+
8
+ exec foreman start -f Procfile.dev "$@"
@@ -2,23 +2,31 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "hanami", "<%= hanami_version %>"
6
- gem "hanami-router", "<%= hanami_version %>"
7
- gem "hanami-controller", "<%= hanami_version %>"
8
- gem "hanami-validations", "<%= hanami_version %>"
5
+ <%= hanami_gem("hanami") %>
6
+ <%- if generate_assets? -%>
7
+ <%= hanami_gem("assets") %>
8
+ <%- end -%>
9
+ <%= hanami_gem("controller") %>
10
+ <%= hanami_gem("router") %>
11
+ <%= hanami_gem("validations") %>
12
+ <%= hanami_gem("view") %>
9
13
 
10
14
  gem "dry-types", "~> 1.0", ">= 1.6.1"
11
15
  gem "puma"
12
16
  gem "rake"
13
17
 
18
+ group :development do
19
+ <%= hanami_gem("webconsole") %>
20
+ end
21
+
14
22
  group :development, :test do
15
23
  gem "dotenv"
16
24
  end
17
25
 
18
26
  group :cli, :development do
19
- gem "hanami-reloader"
27
+ <%= hanami_gem("reloader") %>
20
28
  end
21
29
 
22
30
  group :cli, :development, :test do
23
- gem "hanami-rspec"
31
+ <%= hanami_gem("rspec") %>
24
32
  end
@@ -0,0 +1,6 @@
1
+ .env
2
+ log/*
3
+ <%- if generate_assets? -%>
4
+ public/
5
+ node_modules/
6
+ <%- end -%>