hanami-cli 2.2.1 → 2.3.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +9 -0
  3. data/CHANGELOG.md +33 -0
  4. data/Gemfile +3 -1
  5. data/hanami-cli.gemspec +2 -0
  6. data/lib/hanami/cli/command.rb +5 -11
  7. data/lib/hanami/cli/commands/app/command.rb +5 -5
  8. data/lib/hanami/cli/commands/app/console.rb +1 -0
  9. data/lib/hanami/cli/commands/app/db/command.rb +0 -1
  10. data/lib/hanami/cli/commands/app/db/drop.rb +2 -2
  11. data/lib/hanami/cli/commands/app/db/rollback.rb +204 -0
  12. data/lib/hanami/cli/commands/app/db/utils/mysql.rb +3 -2
  13. data/lib/hanami/cli/commands/app/db/utils/postgres.rb +3 -1
  14. data/lib/hanami/cli/commands/app/generate/action.rb +32 -40
  15. data/lib/hanami/cli/commands/app/generate/command.rb +54 -17
  16. data/lib/hanami/cli/commands/app/generate/component.rb +4 -19
  17. data/lib/hanami/cli/commands/app/generate/part.rb +4 -21
  18. data/lib/hanami/cli/commands/app/generate/slice.rb +2 -2
  19. data/lib/hanami/cli/commands/app/generate/view.rb +5 -24
  20. data/lib/hanami/cli/commands/app/server.rb +0 -1
  21. data/lib/hanami/cli/commands/app.rb +1 -0
  22. data/lib/hanami/cli/commands/gem/new.rb +34 -3
  23. data/lib/hanami/cli/errors.rb +26 -0
  24. data/lib/hanami/cli/files.rb +20 -6
  25. data/lib/hanami/cli/generators/app/action.rb +78 -101
  26. data/lib/hanami/cli/generators/app/component.rb +11 -33
  27. data/lib/hanami/cli/generators/app/migration.rb +1 -1
  28. data/lib/hanami/cli/generators/app/operation.rb +4 -5
  29. data/lib/hanami/cli/generators/app/part.rb +42 -65
  30. data/lib/hanami/cli/generators/app/relation.rb +4 -5
  31. data/lib/hanami/cli/generators/app/repo.rb +3 -5
  32. data/lib/hanami/cli/generators/app/ruby_class_file.rb +32 -0
  33. data/lib/hanami/cli/generators/app/ruby_file.rb +128 -0
  34. data/lib/hanami/cli/generators/app/ruby_module_file.rb +28 -0
  35. data/lib/hanami/cli/generators/app/slice.rb +130 -37
  36. data/lib/hanami/cli/generators/app/struct.rb +3 -4
  37. data/lib/hanami/cli/generators/app/view.rb +40 -45
  38. data/lib/hanami/cli/generators/context.rb +6 -0
  39. data/lib/hanami/cli/generators/gem/app/assets.js +14 -13
  40. data/lib/hanami/cli/generators/gem/app/dev +1 -1
  41. data/lib/hanami/cli/generators/gem/app/gemfile.erb +5 -0
  42. data/lib/hanami/cli/generators/gem/app/gitignore.erb +3 -1
  43. data/lib/hanami/cli/generators/gem/app/rakefile.erb +3 -0
  44. data/lib/hanami/cli/generators/gem/app/readme.erb +14 -0
  45. data/lib/hanami/cli/generators/gem/app.rb +40 -37
  46. data/lib/hanami/cli/ruby_file_generator.rb +17 -8
  47. data/lib/hanami/cli/server.rb +15 -1
  48. data/lib/hanami/cli/version.rb +1 -1
  49. data/lib/hanami/console/context.rb +5 -0
  50. metadata +35 -41
  51. data/lib/hanami/cli/generators/app/action/action.erb +0 -17
  52. data/lib/hanami/cli/generators/app/action/slice_action.erb +0 -17
  53. data/lib/hanami/cli/generators/app/action/slice_template.html.erb +0 -1
  54. data/lib/hanami/cli/generators/app/action/slice_view.erb +0 -10
  55. data/lib/hanami/cli/generators/app/action/template.erb +0 -0
  56. data/lib/hanami/cli/generators/app/action/template.html.erb +0 -1
  57. data/lib/hanami/cli/generators/app/action/view.erb +0 -10
  58. data/lib/hanami/cli/generators/app/action_context.rb +0 -90
  59. data/lib/hanami/cli/generators/app/component/component.erb +0 -8
  60. data/lib/hanami/cli/generators/app/component/slice_component.erb +0 -8
  61. data/lib/hanami/cli/generators/app/component_context.rb +0 -82
  62. data/lib/hanami/cli/generators/app/part/app_base_part.erb +0 -9
  63. data/lib/hanami/cli/generators/app/part/app_part.erb +0 -13
  64. data/lib/hanami/cli/generators/app/part/slice_base_part.erb +0 -9
  65. data/lib/hanami/cli/generators/app/part/slice_part.erb +0 -13
  66. data/lib/hanami/cli/generators/app/part_context.rb +0 -82
  67. data/lib/hanami/cli/generators/app/ruby_file_writer.rb +0 -151
  68. data/lib/hanami/cli/generators/app/slice/action.erb +0 -7
  69. data/lib/hanami/cli/generators/app/slice/app_css.erb +0 -5
  70. data/lib/hanami/cli/generators/app/slice/app_js.erb +0 -1
  71. data/lib/hanami/cli/generators/app/slice/app_layout.erb +0 -18
  72. data/lib/hanami/cli/generators/app/slice/helpers.erb +0 -10
  73. data/lib/hanami/cli/generators/app/slice/keep.erb +0 -0
  74. data/lib/hanami/cli/generators/app/slice/operation.erb +0 -7
  75. data/lib/hanami/cli/generators/app/slice/relation.erb +0 -8
  76. data/lib/hanami/cli/generators/app/slice/repo.erb +0 -8
  77. data/lib/hanami/cli/generators/app/slice/routes.erb +0 -3
  78. data/lib/hanami/cli/generators/app/slice/struct.erb +0 -8
  79. data/lib/hanami/cli/generators/app/slice/view.erb +0 -7
  80. data/lib/hanami/cli/generators/app/slice_context.rb +0 -72
  81. data/lib/hanami/cli/generators/app/view/app_template.html.erb +0 -1
  82. data/lib/hanami/cli/generators/app/view/app_view.erb +0 -10
  83. data/lib/hanami/cli/generators/app/view/slice_template.html.erb +0 -1
  84. data/lib/hanami/cli/generators/app/view/slice_view.erb +0 -10
  85. data/lib/hanami/cli/generators/app/view_context.rb +0 -88
@@ -26,7 +26,7 @@ module Hanami
26
26
 
27
27
  path = fs.join(*[base_path, "config", "db", migrate_dir, file_name(name)].compact)
28
28
 
29
- fs.write(path, FILE_CONTENTS)
29
+ fs.create(path, FILE_CONTENTS)
30
30
  end
31
31
 
32
32
  private
@@ -20,16 +20,15 @@ module Hanami
20
20
  # @since 2.2.0
21
21
  # @api private
22
22
  def call(key:, namespace:, base_path:)
23
- RubyFileWriter.new(
23
+ RubyClassFile.new(
24
24
  fs: fs,
25
25
  inflector: inflector,
26
- ).call(
26
+ key: key,
27
27
  namespace: namespace,
28
28
  base_path: base_path,
29
- key: key,
30
- relative_parent_class: "Operation",
29
+ parent_class_name: "#{inflector.camelize(namespace)}::Operation",
31
30
  body: ["def call", "end"],
32
- )
31
+ ).create
33
32
 
34
33
  unless key.match?(KEY_SEPARATOR)
35
34
  out.puts(
@@ -2,7 +2,6 @@
2
2
 
3
3
  require "erb"
4
4
  require "dry/files"
5
- require_relative "../../errors"
6
5
 
7
6
  module Hanami
8
7
  module CLI
@@ -13,87 +12,65 @@ module Hanami
13
12
  class Part
14
13
  # @since 2.1.0
15
14
  # @api private
16
- def initialize(fs:, inflector:)
15
+ def initialize(fs:, inflector:, out: $stdout)
17
16
  @fs = fs
18
17
  @inflector = inflector
18
+ @out = out
19
19
  end
20
20
 
21
21
  # @since 2.1.0
22
22
  # @api private
23
- def call(app, key, slice)
24
- context = PartContext.new(inflector, app, slice, key)
25
-
26
- if slice
27
- generate_for_slice(context, slice)
28
- else
29
- generate_for_app(context)
30
- end
23
+ def call(key:, namespace:, base_path:, **)
24
+ create_app_base_part_if_missing(key:, namespace:, base_path:)
25
+ create_slice_part_if_missing(key:, namespace:, base_path:) unless namespace == Hanami.app.namespace
26
+ create_generated_part(key:, namespace:, base_path:)
31
27
  end
32
28
 
33
29
  private
34
30
 
35
31
  # @since 2.1.0
36
32
  # @api private
37
- attr_reader :fs
38
-
39
- # @since 2.1.0
40
- # @api private
41
- attr_reader :inflector
42
-
43
- # @since 2.1.0
44
- # @api private
45
- def generate_for_slice(context, slice)
46
- slice_directory = fs.join("slices", slice)
47
- raise MissingSliceError.new(slice) unless fs.directory?(slice_directory)
48
-
49
- generate_base_part_for_app(context)
50
- generate_base_part_for_slice(context, slice)
51
-
52
- fs.mkdir(directory = fs.join(slice_directory, "views", "parts", *context.underscored_namespace))
53
- fs.write(fs.join(directory, "#{context.underscored_name}.rb"), t("slice_part.erb", context))
54
- end
55
-
56
- # @since 2.1.0
57
- # @api private
58
- def generate_for_app(context)
59
- generate_base_part_for_app(context)
60
-
61
- fs.mkdir(directory = fs.join("app", "views", "parts", *context.underscored_namespace))
62
- fs.write(fs.join(directory, "#{context.underscored_name}.rb"), t("app_part.erb", context))
63
- end
64
-
65
- # @since 2.1.0
66
- # @api private
67
- def generate_base_part_for_app(context)
68
- path = fs.join("app", "views", "part.rb")
69
- return if fs.exist?(path)
70
-
71
- fs.write(path, t("app_base_part.erb", context))
33
+ attr_reader :fs, :inflector, :out
34
+
35
+ def create_app_base_part_if_missing(key:, namespace:, base_path:)
36
+ return if fs.exist?(fs.join(base_path, "views", "part.rb"))
37
+
38
+ RubyClassFile.new(
39
+ fs: fs,
40
+ inflector: inflector,
41
+ namespace: Hanami.app.namespace,
42
+ key: "views.part",
43
+ base_path: APP_DIR,
44
+ parent_class_name: "Hanami::View::Part",
45
+ auto_register: false
46
+ ).create
72
47
  end
73
48
 
74
- # @since 2.1.0
75
- # @api private
76
- def generate_base_part_for_slice(context, slice)
77
- path = fs.join("slices", slice, "views", "part.rb")
78
- return if fs.exist?(path)
79
-
80
- fs.write(path, t("slice_base_part.erb", context))
49
+ def create_slice_part_if_missing(key:, namespace:, base_path:)
50
+ return if fs.exist?(fs.join(base_path, "views", "part.rb"))
51
+
52
+ RubyClassFile.new(
53
+ fs: fs,
54
+ inflector: inflector,
55
+ namespace: namespace,
56
+ key: "views.part",
57
+ base_path: base_path,
58
+ parent_class_name: "#{Hanami.app.namespace}::Views::Part",
59
+ auto_register: false
60
+ ).create
81
61
  end
82
62
 
83
- # @since 2.1.0
84
- # @api private
85
- def template(path, context)
86
- require "erb"
87
-
88
- ERB.new(
89
- File.read(__dir__ + "/part/#{path}"),
90
- trim_mode: "-"
91
- ).result(context.ctx)
63
+ def create_generated_part(key:, namespace:, base_path:)
64
+ RubyClassFile.new(
65
+ fs: fs,
66
+ inflector: inflector,
67
+ namespace: namespace,
68
+ key: inflector.underscore("views.parts.#{key}"),
69
+ base_path: base_path,
70
+ parent_class_name: "#{inflector.camelize(namespace)}::Views::Part",
71
+ auto_register: false
72
+ ).create
92
73
  end
93
-
94
- # @since 2.1.0
95
- # @api private
96
- alias_method :t, :template
97
74
  end
98
75
  end
99
76
  end
@@ -25,17 +25,16 @@ module Hanami
25
25
 
26
26
  body_content.prepend("gateway :#{gateway}") if gateway
27
27
 
28
- RubyFileWriter.new(
28
+ RubyClassFile.new(
29
29
  fs: fs,
30
30
  inflector: inflector,
31
- ).call(
32
- namespace: namespace,
33
31
  key: key,
32
+ namespace: namespace,
34
33
  base_path: base_path,
35
34
  extra_namespace: "Relations",
36
- relative_parent_class: "DB::Relation",
35
+ parent_class_name: "#{inflector.camelize(namespace)}::DB::Relation",
37
36
  body: body_content,
38
- )
37
+ ).create
39
38
  end
40
39
 
41
40
  private
@@ -18,17 +18,15 @@ module Hanami
18
18
  # @since 2.2.0
19
19
  # @api private
20
20
  def call(key:, namespace:, base_path:)
21
- RubyFileWriter.new(
21
+ RubyClassFile.new(
22
22
  fs: fs,
23
23
  inflector: inflector,
24
- ).call(
25
24
  key: key,
26
25
  namespace: namespace,
27
26
  base_path: base_path,
28
27
  extra_namespace: "Repos",
29
- relative_parent_class: "DB::Repo",
30
- body: [],
31
- )
28
+ parent_class_name: "#{inflector.camelize(namespace)}::DB::Repo",
29
+ ).create
32
30
  end
33
31
 
34
32
  private
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../constants"
4
+
5
+ module Hanami
6
+ module CLI
7
+ module Generators
8
+ module App
9
+ # @api private
10
+ class RubyClassFile < RubyFile
11
+ def initialize(parent_class_name: nil, **args)
12
+ super
13
+
14
+ @parent_class_name = parent_class_name
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :parent_class_name
20
+
21
+ def class_name
22
+ constant_name
23
+ end
24
+
25
+ def modules
26
+ namespace_modules
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../constants"
4
+
5
+ module Hanami
6
+ module CLI
7
+ module Generators
8
+ module App
9
+ # @api private
10
+ class RubyFile
11
+ def initialize(
12
+ fs:,
13
+ inflector:,
14
+ key:,
15
+ namespace:,
16
+ base_path:,
17
+ extra_namespace: nil,
18
+ auto_register: nil,
19
+ body: [],
20
+ **opts
21
+ )
22
+ @fs = fs
23
+ @inflector = inflector
24
+ @key = key
25
+ @namespace = namespace
26
+ @base_path = base_path
27
+ @extra_namespace = extra_namespace&.downcase
28
+ @auto_register = auto_register
29
+ @body = body
30
+ end
31
+
32
+ # @api private
33
+ def create
34
+ fs.create(path, file_contents)
35
+ end
36
+
37
+ # @api private
38
+ def write
39
+ fs.write(path, file_contents)
40
+ end
41
+
42
+ # @api private
43
+ def fully_qualified_name
44
+ inflector.camelize(
45
+ [namespace, extra_namespace, *key_parts].join("/"),
46
+ )
47
+ end
48
+
49
+ # @api private
50
+ def path
51
+ fs.join(directory, "#{key_parts.last}.rb")
52
+ end
53
+
54
+ private
55
+
56
+ # @api private
57
+ attr_reader(
58
+ :fs,
59
+ :inflector,
60
+ :key,
61
+ :base_path,
62
+ :namespace,
63
+ :extra_namespace,
64
+ :auto_register,
65
+ :body,
66
+ )
67
+
68
+ # @api private
69
+ def file_contents
70
+ RubyFileGenerator.new(
71
+ # These first three must be implemented by subclasses
72
+ class_name: class_name,
73
+ parent_class_name: parent_class_name,
74
+ modules: modules,
75
+ header: headers,
76
+ body: body
77
+ ).call
78
+ end
79
+
80
+ # @api private
81
+ def local_namespaces
82
+ Array(extra_namespace) + key_parts[..-2]
83
+ end
84
+
85
+ # @api private
86
+ def namespace_modules
87
+ [namespace, *local_namespaces]
88
+ .map { normalize(_1) }
89
+ .compact
90
+ end
91
+
92
+ # @api private
93
+ def directory
94
+ @directory ||= if local_namespaces.any?
95
+ fs.join(base_path, local_namespaces)
96
+ else
97
+ base_path
98
+ end
99
+ end
100
+
101
+ # @api private
102
+ def constant_name
103
+ normalize(key_parts.last)
104
+ end
105
+
106
+ # @api private
107
+ def headers
108
+ [
109
+ # Intentional ternary logic. Skip if nil, else 'true' or 'false'
110
+ ("# auto_register: #{auto_register}" unless auto_register.nil?),
111
+ "# frozen_string_literal: true",
112
+ ].compact
113
+ end
114
+
115
+ # @api private
116
+ def normalize(name)
117
+ inflector.camelize(name).gsub(/[^\p{Alnum}]/, "")
118
+ end
119
+
120
+ # @api private
121
+ def key_parts
122
+ key.split(KEY_SEPARATOR)
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../constants"
4
+
5
+ module Hanami
6
+ module CLI
7
+ module Generators
8
+ module App
9
+ # @api private
10
+ class RubyModuleFile < RubyFile
11
+ private
12
+
13
+ def modules
14
+ namespace_modules + [constant_name]
15
+ end
16
+
17
+ def class_name
18
+ nil
19
+ end
20
+
21
+ def parent_class_name
22
+ nil
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -19,44 +19,127 @@ module Hanami
19
19
 
20
20
  # @since 2.0.0
21
21
  # @api private
22
- def call(app, slice, url, context: nil, **opts)
23
- context ||= SliceContext.new(inflector, app, slice, url, **opts)
22
+ def call(app, slice, url, **opts)
23
+ skip_route = opts.fetch(:skip_route, false)
24
24
 
25
- if context.generate_route?
25
+ unless skip_route
26
26
  fs.inject_line_at_class_bottom(
27
- fs.join("config", "routes.rb"), "class Routes", t("routes.erb", context).chomp
27
+ fs.join("config", "routes.rb"),
28
+ "class Routes",
29
+ <<~ROUTES.chomp
30
+
31
+ slice :#{inflector.underscore(slice)}, at: "#{url}" do
32
+ end
33
+ ROUTES
28
34
  )
29
35
  end
30
36
 
31
37
  fs.mkdir(directory = "slices/#{slice}")
32
38
 
33
- fs.write(fs.join(directory, "action.rb"), t("action.erb", context))
34
- fs.write(fs.join(directory, "view.rb"), t("view.erb", context))
35
- fs.write(fs.join(directory, "views", "helpers.rb"), t("helpers.erb", context))
36
- fs.write(fs.join(directory, "templates", "layouts", "app.html.erb"), t("app_layout.erb", context))
37
- fs.write(fs.join(directory, "operation.rb"), t("operation.erb", context))
38
-
39
- if context.bundled_assets?
40
- fs.write(fs.join(directory, "assets", "js", "app.js"), t("app_js.erb", context))
41
- fs.write(fs.join(directory, "assets", "css", "app.css"), t("app_css.erb", context))
42
- fs.write(fs.join(directory, "assets", "images", "favicon.ico"), file("favicon.ico"))
39
+ RubyClassFile.new(
40
+ fs: fs,
41
+ inflector: inflector,
42
+ namespace: slice,
43
+ key: "action",
44
+ base_path: directory,
45
+ parent_class_name: "#{Hanami.app.namespace}::Action",
46
+ auto_register: false
47
+ ).create
48
+
49
+ RubyClassFile.new(
50
+ fs: fs,
51
+ inflector: inflector,
52
+ namespace: slice,
53
+ key: "view",
54
+ base_path: directory,
55
+ parent_class_name: "#{Hanami.app.namespace}::View",
56
+ auto_register: false
57
+ ).create
58
+
59
+ RubyModuleFile.new(
60
+ fs: fs,
61
+ inflector: inflector,
62
+ namespace: slice,
63
+ key: "views.helpers",
64
+ base_path: directory,
65
+ auto_register: false,
66
+ body: ["# Add your view helpers here"]
67
+ ).create
68
+
69
+ fs.create(
70
+ fs.join(directory, "templates", "layouts", "app.html.erb"),
71
+ app_layout_template(
72
+ page_title: "#{inflector.humanize(app)} - #{inflector.humanize(slice)}"
73
+ )
74
+ )
75
+
76
+ if Hanami.bundled?("dry-operation")
77
+ RubyClassFile.new(
78
+ fs: fs,
79
+ inflector: inflector,
80
+ namespace: slice,
81
+ key: "operation",
82
+ base_path: directory,
83
+ parent_class_name: "#{Hanami.app.namespace}::Operation",
84
+ auto_register: false
85
+ ).create
43
86
  end
44
87
 
45
- if context.generate_db?
46
- fs.write(fs.join(directory, "db", "relation.rb"), t("relation.erb", context))
47
- fs.write(fs.join(directory, "relations", ".keep"), t("keep.erb", context))
48
-
49
- fs.write(fs.join(directory, "db", "repo.rb"), t("repo.erb", context))
50
- fs.write(fs.join(directory, "repos", ".keep"), t("keep.erb", context))
88
+ if Hanami.bundled?("hanami-assets")
89
+ fs.create(
90
+ fs.join(directory, "assets", "js", "app.js"),
91
+ %(import "../css/app.css";\n)
92
+ )
93
+ fs.create(
94
+ fs.join(directory, "assets", "css", "app.css"),
95
+ <<~CSS
96
+ body {
97
+ background-color: #fff;
98
+ color: #000;
99
+ font-family: sans-serif;
100
+ }
101
+ CSS
102
+ )
103
+ fs.create(fs.join(directory, "assets", "images", "favicon.ico"), file("favicon.ico"))
104
+ end
51
105
 
52
- fs.write(fs.join(directory, "db", "struct.rb"), t("struct.erb", context))
53
- fs.write(fs.join(directory, "structs", ".keep"), t("keep.erb", context))
106
+ if Hanami.bundled?("hanami-db") && !opts.fetch(:skip_db, false)
107
+ RubyClassFile.new(
108
+ fs: fs,
109
+ inflector: inflector,
110
+ namespace: slice,
111
+ key: "db.relation",
112
+ base_path: directory,
113
+ parent_class_name: "#{Hanami.app.namespace}::DB::Relation",
114
+ ).create
115
+
116
+ RubyClassFile.new(
117
+ fs: fs,
118
+ inflector: inflector,
119
+ namespace: slice,
120
+ key: "db.repo",
121
+ base_path: directory,
122
+ parent_class_name: "#{Hanami.app.namespace}::DB::Repo",
123
+ ).create
124
+
125
+ RubyClassFile.new(
126
+ fs: fs,
127
+ inflector: inflector,
128
+ namespace: slice,
129
+ key: "db.struct",
130
+ base_path: directory,
131
+ parent_class_name: "#{Hanami.app.namespace}::DB::Struct",
132
+ ).create
133
+
134
+ fs.touch(fs.join(directory, "relations", ".keep"))
135
+ fs.touch(fs.join(directory, "repos", ".keep"))
136
+ fs.touch(fs.join(directory, "structs", ".keep"))
54
137
  end
55
138
 
56
- fs.write(fs.join(directory, "actions/.keep"), t("keep.erb", context))
57
- fs.write(fs.join(directory, "views/.keep"), t("keep.erb", context))
58
- fs.write(fs.join(directory, "templates/.keep"), t("keep.erb", context))
59
- fs.write(fs.join(directory, "templates/layouts/.keep"), t("keep.erb", context))
139
+ fs.touch(fs.join(directory, "actions/.keep"))
140
+ fs.touch(fs.join(directory, "views/.keep"))
141
+ fs.touch(fs.join(directory, "templates/.keep"))
142
+ fs.touch(fs.join(directory, "templates/layouts/.keep"))
60
143
  end
61
144
 
62
145
  private
@@ -65,20 +148,30 @@ module Hanami
65
148
 
66
149
  attr_reader :inflector
67
150
 
68
- def template(path, context)
69
- require "erb"
70
-
71
- ERB.new(
72
- File.read(__dir__ + "/slice/#{path}"),
73
- trim_mode: "-"
74
- ).result(context.ctx)
75
- end
76
-
77
- alias_method :t, :template
78
-
79
151
  def file(path)
80
152
  File.read(File.join(__dir__, "slice", path))
81
153
  end
154
+
155
+ def app_layout_template(page_title:)
156
+ bundled_assets = Hanami.bundled?("hanami-assets")
157
+
158
+ <<~LAYOUT
159
+ <!DOCTYPE html>
160
+ <html lang="en">
161
+ <head>
162
+ <meta charset="UTF-8">
163
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
164
+ <title>#{ page_title }</title>
165
+ #{'<%= favicon_tag %>' if bundled_assets }
166
+ #{'<%= stylesheet_tag "app" %>' if bundled_assets }
167
+ </head>
168
+ <body>
169
+ <%= yield %>
170
+ #{'<%= javascript_tag "app" %>' if bundled_assets}
171
+ </body>
172
+ </html>
173
+ LAYOUT
174
+ end
82
175
  end
83
176
  end
84
177
  end
@@ -18,16 +18,15 @@ module Hanami
18
18
  # @since 2.2.0
19
19
  # @api private
20
20
  def call(key:, namespace:, base_path:)
21
- RubyFileWriter.new(
21
+ RubyClassFile.new(
22
22
  fs: fs,
23
23
  inflector: inflector,
24
- ).call(
25
24
  key: key,
26
25
  namespace: namespace,
27
26
  base_path: base_path,
28
27
  extra_namespace: "Structs",
29
- relative_parent_class: "DB::Struct",
30
- )
28
+ parent_class_name: "#{inflector.camelize(namespace)}::DB::Struct",
29
+ ).create
31
30
  end
32
31
 
33
32
  private