hanami-cli 2.1.0.beta2 → 2.1.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -0
- data/Gemfile +2 -0
- data/lib/hanami/cli/commands/app/assets/command.rb +2 -15
- data/lib/hanami/cli/commands/app/assets/watch.rb +1 -5
- data/lib/hanami/cli/commands/app/dev.rb +4 -16
- data/lib/hanami/cli/commands/app/generate/part.rb +49 -0
- data/lib/hanami/cli/commands/app/install.rb +0 -28
- data/lib/hanami/cli/commands/app.rb +1 -0
- data/lib/hanami/cli/commands/gem/new.rb +16 -0
- data/lib/hanami/cli/generators/app/action.rb +46 -6
- data/lib/hanami/cli/generators/app/part/app_base_part.erb +9 -0
- data/lib/hanami/cli/generators/app/part/app_part.erb +13 -0
- data/lib/hanami/cli/generators/app/part/slice_base_part.erb +9 -0
- data/lib/hanami/cli/generators/app/part/slice_part.erb +13 -0
- data/lib/hanami/cli/generators/app/part.rb +101 -0
- data/lib/hanami/cli/generators/app/part_context.rb +98 -0
- data/lib/hanami/cli/generators/context.rb +20 -2
- data/lib/hanami/cli/generators/gem/app/404.html +76 -5
- data/lib/hanami/cli/generators/gem/app/500.html +76 -5
- data/lib/hanami/cli/generators/gem/app/assets.mjs +14 -0
- data/lib/hanami/cli/generators/gem/app/dev +8 -0
- data/lib/hanami/cli/generators/gem/app/package.json.erb +10 -0
- data/lib/hanami/cli/generators/gem/app/puma.erb +37 -7
- data/lib/hanami/cli/generators/gem/app/routes.erb +1 -1
- data/lib/hanami/cli/generators/gem/app.rb +12 -3
- data/lib/hanami/cli/generators/version.rb +12 -0
- data/lib/hanami/cli/version.rb +1 -1
- metadata +12 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '054178514bf7e420e00cd8da8a148cd4d479609668c180507fadbcd7a086f15d'
|
|
4
|
+
data.tar.gz: 3ef8c388ad8c8734179ce042d3eb966dea6df881b72e7615f0b104a1890106a5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c8e1abc0ded22d56ee2acd5dec6d9f7079c0331d187b1d4ff4b26858faf50ff870016e1adfa6b785673d39b6d0dcf097dca7c1b0526da442e1939ce0952f3799
|
|
7
|
+
data.tar.gz: 8ecc3ceefc10856cbd2f60ec24bd1ecde442764610dbb7d179f064080ac434fa4829374efb05513c67420ab8b76b0280ad594fd566824fdfba0e8d14afc209fb
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
Hanami Command Line Interface
|
|
4
4
|
|
|
5
|
+
## v2.1.0.rc1 - 2023-11-01
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- [Tim Riley] `hanami new` to generate `bin/dev` as configuration for `hanami dev`
|
|
10
|
+
- [Luca Guidi] Introducing `hanami generate part` to generate view parts
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- [Luca Guidi] `hanami new` generates a fully documented Puma configuration in `config/puma.rb`
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- [Tim Riley] `hanami new` generates a `config/assets.mjs` as Assets configuration
|
|
19
|
+
- [Tim Riley] `hanami new` generates a leaner `package.json`
|
|
20
|
+
- [Tim Riley] `hanami new` doesn't generate a default root route anymore
|
|
21
|
+
- [Aaron Moodie & Tim Riley] `hanami new` to generate a redesigned 404 and 500 error pages
|
|
22
|
+
- [Luca Guidi] When generating a RESTful action, skip `create`, if `new` is present, and `update`, if `edit` is present
|
|
23
|
+
|
|
5
24
|
## v2.1.0.beta2 - 2023-10-04
|
|
6
25
|
|
|
7
26
|
### Added
|
data/Gemfile
CHANGED
|
@@ -15,6 +15,8 @@ gem "hanami-controller", github: "hanami/controller", branch: "main"
|
|
|
15
15
|
gem "hanami-router", github: "hanami/router", branch: "main"
|
|
16
16
|
gem "hanami-utils", github: "hanami/utils", branch: "main"
|
|
17
17
|
|
|
18
|
+
gem "dry-files", github: "dry-rb/dry-files", branch: "main"
|
|
19
|
+
|
|
18
20
|
gem "rack"
|
|
19
21
|
|
|
20
22
|
group :test do
|
|
@@ -23,7 +23,7 @@ module Hanami
|
|
|
23
23
|
def call(**)
|
|
24
24
|
cmd, *args = cmd_with_args
|
|
25
25
|
|
|
26
|
-
system_call.call(cmd, *args
|
|
26
|
+
system_call.call(cmd, *args)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
private
|
|
@@ -39,20 +39,7 @@ module Hanami
|
|
|
39
39
|
# @since 2.1.0
|
|
40
40
|
# @api private
|
|
41
41
|
def cmd_with_args
|
|
42
|
-
[
|
|
43
|
-
config.package_manager_executable,
|
|
44
|
-
config.package_manager_command,
|
|
45
|
-
config.executable
|
|
46
|
-
]
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# @since 2.1.0
|
|
50
|
-
# @api private
|
|
51
|
-
def env
|
|
52
|
-
ENV.to_h.merge(
|
|
53
|
-
"ESBUILD_ENTRY_POINTS" => entry_points,
|
|
54
|
-
"ESBUILD_OUTDIR" => destination
|
|
55
|
-
)
|
|
42
|
+
[config.package_manager_run_command, "assets"]
|
|
56
43
|
end
|
|
57
44
|
|
|
58
45
|
# @since 2.1.0
|
|
@@ -13,16 +13,6 @@ module Hanami
|
|
|
13
13
|
# @api private
|
|
14
14
|
desc "Start the application in development mode"
|
|
15
15
|
|
|
16
|
-
# @since 2.1.0
|
|
17
|
-
# @api private
|
|
18
|
-
option :procfile, type: :string, desc: "Path to Procfile", aliases: ["-f"]
|
|
19
|
-
|
|
20
|
-
# @since 2.1.0
|
|
21
|
-
# @api private
|
|
22
|
-
example [
|
|
23
|
-
"-f /path/to/Procfile",
|
|
24
|
-
]
|
|
25
|
-
|
|
26
16
|
# @since 2.1.0
|
|
27
17
|
# @api private
|
|
28
18
|
def initialize(interactive_system_call: InteractiveSystemCall.new, **)
|
|
@@ -32,8 +22,8 @@ module Hanami
|
|
|
32
22
|
|
|
33
23
|
# @since 2.1.0
|
|
34
24
|
# @api private
|
|
35
|
-
def call(
|
|
36
|
-
bin, args = executable
|
|
25
|
+
def call(**)
|
|
26
|
+
bin, args = executable
|
|
37
27
|
interactive_system_call.call(bin, *args)
|
|
38
28
|
end
|
|
39
29
|
|
|
@@ -45,10 +35,8 @@ module Hanami
|
|
|
45
35
|
|
|
46
36
|
# @since 2.1.0
|
|
47
37
|
# @api private
|
|
48
|
-
def executable
|
|
49
|
-
|
|
50
|
-
# See: https://github.com/ddollar/foreman#ports
|
|
51
|
-
["foreman", ["start", "-f", procfile || "Procfile.dev"]]
|
|
38
|
+
def executable
|
|
39
|
+
[::File.join("bin", "dev")]
|
|
52
40
|
end
|
|
53
41
|
end
|
|
54
42
|
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/inflector"
|
|
4
|
+
require "dry/files"
|
|
5
|
+
require "shellwords"
|
|
6
|
+
|
|
7
|
+
module Hanami
|
|
8
|
+
module CLI
|
|
9
|
+
module Commands
|
|
10
|
+
module App
|
|
11
|
+
module Generate
|
|
12
|
+
# @since 2.1.0
|
|
13
|
+
# @api private
|
|
14
|
+
class Part < App::Command
|
|
15
|
+
argument :name, required: true, desc: "Part name"
|
|
16
|
+
option :slice, required: false, desc: "Slice name"
|
|
17
|
+
|
|
18
|
+
example [
|
|
19
|
+
%(book (MyApp::Views::Parts::Book)),
|
|
20
|
+
%(book --slice=admin (Admin::Views::Parts::Book)),
|
|
21
|
+
]
|
|
22
|
+
attr_reader :generator
|
|
23
|
+
private :generator
|
|
24
|
+
|
|
25
|
+
# @since 2.0.0
|
|
26
|
+
# @api private
|
|
27
|
+
def initialize(
|
|
28
|
+
fs: Hanami::CLI::Files.new,
|
|
29
|
+
inflector: Dry::Inflector.new,
|
|
30
|
+
generator: Generators::App::Part.new(fs: fs, inflector: inflector),
|
|
31
|
+
**
|
|
32
|
+
)
|
|
33
|
+
@generator = generator
|
|
34
|
+
super(fs: fs)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @since 2.0.0
|
|
38
|
+
# @api private
|
|
39
|
+
def call(name:, slice: nil, **)
|
|
40
|
+
slice = inflector.underscore(Shellwords.shellescape(slice)) if slice
|
|
41
|
+
|
|
42
|
+
generator.call(app.namespace, name, slice)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -33,37 +33,9 @@ module Hanami
|
|
|
33
33
|
# @api private
|
|
34
34
|
option :head, type: :boolean, desc: "Install head deps", default: DEFAULT_HEAD
|
|
35
35
|
|
|
36
|
-
# @since 2.1.0
|
|
37
|
-
# @api private
|
|
38
|
-
def initialize(system_call: SystemCall.new, **)
|
|
39
|
-
@system_call = system_call
|
|
40
|
-
super()
|
|
41
|
-
end
|
|
42
|
-
|
|
43
36
|
# @since 2.0.0
|
|
44
37
|
# @api private
|
|
45
38
|
def call(head: DEFAULT_HEAD, **)
|
|
46
|
-
install_hanami_assets!(head: head)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
private
|
|
50
|
-
|
|
51
|
-
# @since 2.1.0
|
|
52
|
-
# @api private
|
|
53
|
-
attr_reader :system_call
|
|
54
|
-
|
|
55
|
-
# @since 2.1.0
|
|
56
|
-
# @api private
|
|
57
|
-
def install_hanami_assets!(head:)
|
|
58
|
-
return unless Hanami.bundled?("hanami-assets")
|
|
59
|
-
|
|
60
|
-
system_call.call("npm", ["init", "-y"])
|
|
61
|
-
|
|
62
|
-
if head
|
|
63
|
-
system_call.call("npm", %w[install https://github.com/hanami/assets-js])
|
|
64
|
-
else
|
|
65
|
-
system_call.call("npm", %w[install hanami-assets])
|
|
66
|
-
end
|
|
67
39
|
end
|
|
68
40
|
end
|
|
69
41
|
end
|
|
@@ -58,6 +58,8 @@ module Hanami
|
|
|
58
58
|
]
|
|
59
59
|
# rubocop:enable Layout/LineLength
|
|
60
60
|
|
|
61
|
+
# rubocop:disable Metrics/ParameterLists
|
|
62
|
+
|
|
61
63
|
# @since 2.0.0
|
|
62
64
|
# @api private
|
|
63
65
|
def initialize(
|
|
@@ -65,13 +67,19 @@ module Hanami
|
|
|
65
67
|
inflector: Dry::Inflector.new,
|
|
66
68
|
bundler: CLI::Bundler.new(fs: fs),
|
|
67
69
|
generator: Generators::Gem::App.new(fs: fs, inflector: inflector),
|
|
70
|
+
system_call: SystemCall.new,
|
|
68
71
|
**other
|
|
69
72
|
)
|
|
70
73
|
@bundler = bundler
|
|
71
74
|
@generator = generator
|
|
75
|
+
@system_call = system_call
|
|
72
76
|
super(fs: fs, inflector: inflector, **other)
|
|
73
77
|
end
|
|
74
78
|
|
|
79
|
+
# rubocop:enable Metrics/ParameterLists
|
|
80
|
+
|
|
81
|
+
# rubocop:disable Metrics/AbcSize
|
|
82
|
+
|
|
75
83
|
# @since 2.0.0
|
|
76
84
|
# @api private
|
|
77
85
|
def call(app:, head: HEAD_DEFAULT, skip_install: SKIP_INSTALL_DEFAULT, skip_assets: SKIP_ASSETS_DEFAULT, **)
|
|
@@ -88,17 +96,25 @@ module Hanami
|
|
|
88
96
|
else
|
|
89
97
|
out.puts "Running Bundler install..."
|
|
90
98
|
bundler.install!
|
|
99
|
+
|
|
100
|
+
unless skip_assets
|
|
101
|
+
out.puts "Running npm install..."
|
|
102
|
+
system_call.call("npm", ["install"])
|
|
103
|
+
end
|
|
104
|
+
|
|
91
105
|
out.puts "Running Hanami install..."
|
|
92
106
|
run_install_commmand!(head: head)
|
|
93
107
|
end
|
|
94
108
|
end
|
|
95
109
|
end
|
|
96
110
|
end
|
|
111
|
+
# rubocop:enable Metrics/AbcSize
|
|
97
112
|
|
|
98
113
|
private
|
|
99
114
|
|
|
100
115
|
attr_reader :bundler
|
|
101
116
|
attr_reader :generator
|
|
117
|
+
attr_reader :system_call
|
|
102
118
|
|
|
103
119
|
def run_install_commmand!(head:)
|
|
104
120
|
head_flag = head ? " --head" : ""
|
|
@@ -56,6 +56,14 @@ module Hanami
|
|
|
56
56
|
}.freeze
|
|
57
57
|
private_constant :ROUTE_RESTFUL_URL_SUFFIXES
|
|
58
58
|
|
|
59
|
+
# @api private
|
|
60
|
+
# @since 2.1.0
|
|
61
|
+
RESTFUL_COUNTERPART_VIEWS = {
|
|
62
|
+
"create" => "new",
|
|
63
|
+
"update" => "edit"
|
|
64
|
+
}.freeze
|
|
65
|
+
private_constant :RESTFUL_COUNTERPART_VIEWS
|
|
66
|
+
|
|
59
67
|
PATH_SEPARATOR = "/"
|
|
60
68
|
private_constant :PATH_SEPARATOR
|
|
61
69
|
|
|
@@ -77,7 +85,7 @@ module Hanami
|
|
|
77
85
|
fs.mkdir(directory = fs.join(slice_directory, "actions", controller))
|
|
78
86
|
fs.write(fs.join(directory, "#{action}.rb"), t("slice_action.erb", context))
|
|
79
87
|
|
|
80
|
-
|
|
88
|
+
if generate_view?(skip_view, action, directory)
|
|
81
89
|
fs.mkdir(directory = fs.join(slice_directory, "views", controller))
|
|
82
90
|
fs.write(fs.join(directory, "#{action}.rb"), t("slice_view.erb", context))
|
|
83
91
|
|
|
@@ -97,12 +105,15 @@ module Hanami
|
|
|
97
105
|
fs.mkdir(directory = fs.join("app", "actions", controller))
|
|
98
106
|
fs.write(fs.join(directory, "#{action}.rb"), t("action.erb", context))
|
|
99
107
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
fs.write(fs.join(directory, "#{action}.rb"), t("view.erb", context))
|
|
108
|
+
view = action
|
|
109
|
+
view_directory = fs.join("app", "views", controller)
|
|
103
110
|
|
|
104
|
-
|
|
105
|
-
fs.
|
|
111
|
+
if generate_view?(skip_view, view, view_directory)
|
|
112
|
+
fs.mkdir(view_directory)
|
|
113
|
+
fs.write(fs.join(view_directory, "#{view}.rb"), t("view.erb", context))
|
|
114
|
+
|
|
115
|
+
fs.mkdir(template_directory = fs.join("app", "templates", controller))
|
|
116
|
+
fs.write(fs.join(template_directory, "#{view}.#{format}.erb"),
|
|
106
117
|
t(template_with_format_ext("template", format), context))
|
|
107
118
|
end
|
|
108
119
|
end
|
|
@@ -117,6 +128,35 @@ module Hanami
|
|
|
117
128
|
http)} "#{route_url(controller, action, url)}", to: "#{controller.join('.')}.#{action}")
|
|
118
129
|
end
|
|
119
130
|
|
|
131
|
+
# @api private
|
|
132
|
+
# @since 2.1.0
|
|
133
|
+
def generate_view?(skip_view, view, directory)
|
|
134
|
+
return false if skip_view
|
|
135
|
+
return generate_restful_view?(view, directory) if rest_view?(view)
|
|
136
|
+
|
|
137
|
+
true
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# @api private
|
|
141
|
+
# @since 2.1.0
|
|
142
|
+
def generate_restful_view?(view, directory)
|
|
143
|
+
corresponding_action = corresponding_restful_view(view)
|
|
144
|
+
|
|
145
|
+
!fs.exist?(fs.join(directory, "#{corresponding_action}.rb"))
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# @api private
|
|
149
|
+
# @since 2.1.0
|
|
150
|
+
def rest_view?(view)
|
|
151
|
+
RESTFUL_COUNTERPART_VIEWS.keys.include?(view)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# @api private
|
|
155
|
+
# @since 2.1.0
|
|
156
|
+
def corresponding_restful_view(view)
|
|
157
|
+
RESTFUL_COUNTERPART_VIEWS.fetch(view, nil)
|
|
158
|
+
end
|
|
159
|
+
|
|
120
160
|
def template_with_format_ext(name, format)
|
|
121
161
|
ext =
|
|
122
162
|
case format.to_sym
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# auto_register: false
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module <%= camelized_app_name %>
|
|
5
|
+
module Views
|
|
6
|
+
module Parts
|
|
7
|
+
<%= module_namespace_declaration -%>
|
|
8
|
+
<%= module_namespace_offset %>class <%= camelized_name %> < <%= camelized_app_name %>::Views::Part
|
|
9
|
+
<%= module_namespace_offset %>end
|
|
10
|
+
<%= module_namespace_end -%>
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# auto_register: false
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module <%= camelized_slice_name %>
|
|
5
|
+
module Views
|
|
6
|
+
module Parts
|
|
7
|
+
<%= module_namespace_declaration -%>
|
|
8
|
+
<%= module_namespace_offset %>class <%= camelized_name %> < <%= camelized_slice_name %>::Views::Part
|
|
9
|
+
<%= module_namespace_offset %>end
|
|
10
|
+
<%= module_namespace_end -%>
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
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.1.0
|
|
12
|
+
# @api private
|
|
13
|
+
class Part
|
|
14
|
+
# @since 2.1.0
|
|
15
|
+
# @api private
|
|
16
|
+
def initialize(fs:, inflector:)
|
|
17
|
+
@fs = fs
|
|
18
|
+
@inflector = inflector
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @since 2.1.0
|
|
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
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
# @since 2.1.0
|
|
36
|
+
# @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))
|
|
72
|
+
end
|
|
73
|
+
|
|
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))
|
|
81
|
+
end
|
|
82
|
+
|
|
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)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# @since 2.1.0
|
|
95
|
+
# @api private
|
|
96
|
+
alias_method :t, :template
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
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 PartContext < SliceContext
|
|
15
|
+
# TODO: move these constants somewhere that will let us reuse them
|
|
16
|
+
|
|
17
|
+
# @since 2.1.0
|
|
18
|
+
# @api private
|
|
19
|
+
KEY_SEPARATOR = "."
|
|
20
|
+
private_constant :KEY_SEPARATOR
|
|
21
|
+
|
|
22
|
+
# @since 2.1.0
|
|
23
|
+
# @api private
|
|
24
|
+
INDENTATION = " "
|
|
25
|
+
private_constant :INDENTATION
|
|
26
|
+
|
|
27
|
+
# @since 2.1.0
|
|
28
|
+
# @api private
|
|
29
|
+
OFFSET = INDENTATION * 2
|
|
30
|
+
private_constant :OFFSET
|
|
31
|
+
|
|
32
|
+
# @since 2.1.0
|
|
33
|
+
# @api private
|
|
34
|
+
attr_reader :key
|
|
35
|
+
|
|
36
|
+
# @since 2.1.0
|
|
37
|
+
# @api private
|
|
38
|
+
def initialize(inflector, app, slice, key)
|
|
39
|
+
@key = key
|
|
40
|
+
super(inflector, app, slice, nil)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @since 2.1.0
|
|
44
|
+
# @api private
|
|
45
|
+
def namespaces
|
|
46
|
+
@namespaces ||= key.split(KEY_SEPARATOR)[..-2]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @since 2.1.0
|
|
50
|
+
# @api private
|
|
51
|
+
def name
|
|
52
|
+
@name ||= key.split(KEY_SEPARATOR)[-1]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# @since 2.1.0
|
|
56
|
+
# @api private
|
|
57
|
+
def camelized_name
|
|
58
|
+
inflector.camelize(name)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @since 2.1.0
|
|
62
|
+
# @api private
|
|
63
|
+
def underscored_namespace
|
|
64
|
+
namespaces.map { inflector.underscore(_1) }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @since 2.1.0
|
|
68
|
+
# @api private
|
|
69
|
+
def underscored_name
|
|
70
|
+
inflector.underscore(name)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# @since 2.1.0
|
|
74
|
+
# @api private
|
|
75
|
+
def module_namespace_declaration
|
|
76
|
+
namespaces.each_with_index.map { |token, i|
|
|
77
|
+
"#{OFFSET}#{INDENTATION * i}module #{inflector.camelize(token)}"
|
|
78
|
+
}.join($/)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# @since 2.1.0
|
|
82
|
+
# @api private
|
|
83
|
+
def module_namespace_end
|
|
84
|
+
namespaces.each_with_index.map { |_, i|
|
|
85
|
+
"#{OFFSET}#{INDENTATION * i}end"
|
|
86
|
+
}.reverse.join($/)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# @since 2.1.0
|
|
90
|
+
# @api private
|
|
91
|
+
def module_namespace_offset
|
|
92
|
+
"#{OFFSET}#{INDENTATION * namespaces.count}"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -27,12 +27,12 @@ module Hanami
|
|
|
27
27
|
def hanami_gem(name)
|
|
28
28
|
gem_name = name == "hanami" ? "hanami" : "hanami-#{name}"
|
|
29
29
|
|
|
30
|
-
%(gem "#{gem_name}", #{
|
|
30
|
+
%(gem "#{gem_name}", #{hanami_gem_version(name)})
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
# @since 2.0.0
|
|
34
34
|
# @api private
|
|
35
|
-
def
|
|
35
|
+
def hanami_gem_version(gem_name)
|
|
36
36
|
if hanami_head?
|
|
37
37
|
%(github: "hanami/#{gem_name}", branch: "main")
|
|
38
38
|
else
|
|
@@ -40,6 +40,16 @@ module Hanami
|
|
|
40
40
|
end
|
|
41
41
|
end
|
|
42
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
|
|
51
|
+
end
|
|
52
|
+
|
|
43
53
|
# @since 2.0.0
|
|
44
54
|
# @api private
|
|
45
55
|
def camelized_app_name
|
|
@@ -82,6 +92,14 @@ module Hanami
|
|
|
82
92
|
Hanami.bundled?("hanami-assets")
|
|
83
93
|
end
|
|
84
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
|
+
|
|
85
103
|
private
|
|
86
104
|
|
|
87
105
|
# @since 2.0.0
|
|
@@ -1,11 +1,82 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
3
|
-
|
|
4
|
-
|
|
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>
|
|
5
72
|
</head>
|
|
6
73
|
<body>
|
|
7
74
|
<!-- This file lives in public/404.html -->
|
|
8
|
-
<
|
|
9
|
-
|
|
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>
|
|
10
81
|
</body>
|
|
11
82
|
</html>
|
|
@@ -1,11 +1,82 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
3
|
-
|
|
4
|
-
|
|
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>
|
|
5
72
|
</head>
|
|
6
73
|
<body>
|
|
7
74
|
<!-- This file lives in public/500.html -->
|
|
8
|
-
<
|
|
9
|
-
|
|
75
|
+
<main>
|
|
76
|
+
<div class="message">
|
|
77
|
+
<h1>500</h1>
|
|
78
|
+
<p>We’re sorry, but something went wrong.</p>
|
|
79
|
+
</div>
|
|
80
|
+
</main>
|
|
10
81
|
</body>
|
|
11
82
|
</html>
|
|
@@ -0,0 +1,14 @@
|
|
|
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
|
+
// await assets.run({
|
|
8
|
+
// esbuildOptionsFn: (args, esbuildOptions) => {
|
|
9
|
+
// // Add to esbuildOptions here. Use `args.watch` as a condition for different options for
|
|
10
|
+
// // compile vs watch.
|
|
11
|
+
//
|
|
12
|
+
// return esbuildOptions;
|
|
13
|
+
// }
|
|
14
|
+
// });
|
|
@@ -1,17 +1,47 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
#
|
|
4
|
+
# Environment and port
|
|
5
|
+
#
|
|
6
|
+
port ENV.fetch("<%= Hanami::Port::ENV_VAR %>", <%= Hanami::Port::DEFAULT %>)
|
|
7
|
+
environment ENV.fetch("HANAMI_ENV", "development")
|
|
8
|
+
|
|
9
|
+
#
|
|
10
|
+
# Threads within each Puma/Ruby process (aka worker)
|
|
11
|
+
#
|
|
12
|
+
|
|
13
|
+
# Configure the minimum and maximum number of threads to use to answer requests.
|
|
3
14
|
max_threads_count = ENV.fetch("HANAMI_MAX_THREADS", 5)
|
|
4
15
|
min_threads_count = ENV.fetch("HANAMI_MIN_THREADS") { max_threads_count }
|
|
16
|
+
|
|
5
17
|
threads min_threads_count, max_threads_count
|
|
6
18
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
19
|
+
#
|
|
20
|
+
# Workers (aka Puma/Ruby processes)
|
|
21
|
+
#
|
|
10
22
|
|
|
11
|
-
|
|
12
|
-
|
|
23
|
+
puma_concurrency = Integer(ENV.fetch("HANAMI_WEB_CONCURRENCY", 0))
|
|
24
|
+
puma_cluster_mode = puma_concurrency > 1
|
|
25
|
+
|
|
26
|
+
# How many worker (Puma/Ruby) processes to run.
|
|
27
|
+
# Typically this is set to the number of available cores.
|
|
28
|
+
workers puma_concurrency
|
|
29
|
+
|
|
30
|
+
#
|
|
31
|
+
# Cluster mode (aka multiple workers)
|
|
32
|
+
#
|
|
33
|
+
|
|
34
|
+
if puma_cluster_mode
|
|
35
|
+
# Preload the application before starting the workers. Only in cluster mode.
|
|
36
|
+
preload_app!
|
|
37
|
+
|
|
38
|
+
# Code to run immediately before master process forks workers (once on boot).
|
|
39
|
+
#
|
|
40
|
+
# These hooks can block if necessary to wait for background operations unknown
|
|
41
|
+
# to puma to finish before the process terminates. This can be used to close
|
|
42
|
+
# any connections to remote servers (database, redis, …) that were opened when
|
|
43
|
+
# preloading the code.
|
|
44
|
+
before_fork do
|
|
13
45
|
Hanami.shutdown
|
|
14
46
|
end
|
|
15
47
|
end
|
|
16
|
-
|
|
17
|
-
preload_app!
|
|
@@ -43,6 +43,9 @@ module Hanami
|
|
|
43
43
|
fs.write("Procfile.dev", t("procfile.erb", context))
|
|
44
44
|
fs.write("config.ru", t("config_ru.erb", context))
|
|
45
45
|
|
|
46
|
+
fs.write("bin/dev", file("dev"))
|
|
47
|
+
fs.chmod("bin/dev", 0o755)
|
|
48
|
+
|
|
46
49
|
fs.write("config/app.rb", t("app.erb", context))
|
|
47
50
|
fs.write("config/settings.rb", t("settings.erb", context))
|
|
48
51
|
fs.write("config/routes.rb", t("routes.erb", context))
|
|
@@ -58,13 +61,15 @@ module Hanami
|
|
|
58
61
|
fs.write("app/templates/layouts/app.html.erb", t("app_layout.erb", context))
|
|
59
62
|
|
|
60
63
|
if context.generate_assets?
|
|
64
|
+
fs.write("package.json", t("package.json.erb", context))
|
|
65
|
+
fs.write("config/assets.mjs", file("assets.mjs"))
|
|
61
66
|
fs.write("app/assets/js/app.js", t("app_js.erb", context))
|
|
62
67
|
fs.write("app/assets/css/app.css", t("app_css.erb", context))
|
|
63
|
-
fs.write("app/assets/images/favicon.ico",
|
|
68
|
+
fs.write("app/assets/images/favicon.ico", file("favicon.ico"))
|
|
64
69
|
end
|
|
65
70
|
|
|
66
|
-
fs.write("public/404.html",
|
|
67
|
-
fs.write("public/500.html",
|
|
71
|
+
fs.write("public/404.html", file("404.html"))
|
|
72
|
+
fs.write("public/500.html", file("500.html"))
|
|
68
73
|
end
|
|
69
74
|
|
|
70
75
|
def template(path, context)
|
|
@@ -77,6 +82,10 @@ module Hanami
|
|
|
77
82
|
end
|
|
78
83
|
|
|
79
84
|
alias_method :t, :template
|
|
85
|
+
|
|
86
|
+
def file(path)
|
|
87
|
+
File.read(File.join(__dir__, "app", path))
|
|
88
|
+
end
|
|
80
89
|
end
|
|
81
90
|
end
|
|
82
91
|
end
|
|
@@ -26,6 +26,18 @@ module Hanami
|
|
|
26
26
|
"~> #{result}"
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
def self.npm_package_requirement
|
|
30
|
+
result = version
|
|
31
|
+
# Change "2.1.0.beta2.1" to "2.1.0-beta.2" (the only format tolerable by `npm install`)
|
|
32
|
+
if prerelease?
|
|
33
|
+
result = result
|
|
34
|
+
.sub(/\.(alpha|beta|rc)/, '-\1')
|
|
35
|
+
.sub(/(alpha|beta|rc)(.+)\.(.+)$/, '\1.\2')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
"^#{result}"
|
|
39
|
+
end
|
|
40
|
+
|
|
29
41
|
# @since 2.0.0
|
|
30
42
|
# @api private
|
|
31
43
|
def self.prerelease?
|
data/lib/hanami/cli/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hanami-cli
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.1.0.
|
|
4
|
+
version: 2.1.0.rc1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Luca Guidi
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2023-
|
|
11
|
+
date: 2023-11-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -216,6 +216,7 @@ files:
|
|
|
216
216
|
- lib/hanami/cli/commands/app/dev.rb
|
|
217
217
|
- lib/hanami/cli/commands/app/generate.rb
|
|
218
218
|
- lib/hanami/cli/commands/app/generate/action.rb
|
|
219
|
+
- lib/hanami/cli/commands/app/generate/part.rb
|
|
219
220
|
- lib/hanami/cli/commands/app/generate/slice.rb
|
|
220
221
|
- lib/hanami/cli/commands/app/generate/view.rb
|
|
221
222
|
- lib/hanami/cli/commands/app/install.rb
|
|
@@ -237,6 +238,12 @@ files:
|
|
|
237
238
|
- lib/hanami/cli/generators/app/action/template.html.erb
|
|
238
239
|
- lib/hanami/cli/generators/app/action/view.erb
|
|
239
240
|
- lib/hanami/cli/generators/app/action_context.rb
|
|
241
|
+
- lib/hanami/cli/generators/app/part.rb
|
|
242
|
+
- lib/hanami/cli/generators/app/part/app_base_part.erb
|
|
243
|
+
- lib/hanami/cli/generators/app/part/app_part.erb
|
|
244
|
+
- lib/hanami/cli/generators/app/part/slice_base_part.erb
|
|
245
|
+
- lib/hanami/cli/generators/app/part/slice_part.erb
|
|
246
|
+
- lib/hanami/cli/generators/app/part_context.rb
|
|
240
247
|
- lib/hanami/cli/generators/app/slice.rb
|
|
241
248
|
- lib/hanami/cli/generators/app/slice/action.erb
|
|
242
249
|
- lib/hanami/cli/generators/app/slice/app_css.erb
|
|
@@ -265,13 +272,16 @@ files:
|
|
|
265
272
|
- lib/hanami/cli/generators/gem/app/app_css.erb
|
|
266
273
|
- lib/hanami/cli/generators/gem/app/app_js.erb
|
|
267
274
|
- lib/hanami/cli/generators/gem/app/app_layout.erb
|
|
275
|
+
- lib/hanami/cli/generators/gem/app/assets.mjs
|
|
268
276
|
- lib/hanami/cli/generators/gem/app/config_ru.erb
|
|
277
|
+
- lib/hanami/cli/generators/gem/app/dev
|
|
269
278
|
- lib/hanami/cli/generators/gem/app/env.erb
|
|
270
279
|
- lib/hanami/cli/generators/gem/app/favicon.ico
|
|
271
280
|
- lib/hanami/cli/generators/gem/app/gemfile.erb
|
|
272
281
|
- lib/hanami/cli/generators/gem/app/gitignore.erb
|
|
273
282
|
- lib/hanami/cli/generators/gem/app/helpers.erb
|
|
274
283
|
- lib/hanami/cli/generators/gem/app/keep.erb
|
|
284
|
+
- lib/hanami/cli/generators/gem/app/package.json.erb
|
|
275
285
|
- lib/hanami/cli/generators/gem/app/procfile.erb
|
|
276
286
|
- lib/hanami/cli/generators/gem/app/puma.erb
|
|
277
287
|
- lib/hanami/cli/generators/gem/app/rakefile.erb
|