hanami-cli 2.1.0.beta2 → 2.1.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|