roda-project 0.1.17 → 0.1.18
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/lib/roda/project/bin/generators/routes.rb +161 -72
- data/lib/roda/project/bin/generators.rb +1 -0
- data/lib/roda/project/cli.rb +19 -12
- data/lib/roda/project/helpers/ids.rb +9 -0
- data/lib/roda/project/main_context.rb +22 -1
- data/lib/roda/project/templates/base/app/app.rb.erb +11 -2
- data/lib/roda/project/templates/{base/scaffold → base-scaffold}/AGENTS.md.erb +12 -6
- data/lib/roda/project/templates/{base/scaffold → base-scaffold}/Gemfile.erb +3 -2
- data/lib/roda/project/templates/front-end-inertia/app/assets/css/app.css +20 -0
- data/lib/roda/project/templates/front-end-inertia/app/assets/js/app.js +11 -0
- data/lib/roda/project/templates/front-end-inertia/app/assets/js/inertia/components/Layout.jsx +11 -0
- data/lib/roda/project/templates/front-end-inertia/app/assets/js/inertia/views/Index.jsx +13 -0
- data/lib/roda/project/templates/front-end-inertia/app/config/locales/en.yml +273 -0
- data/lib/roda/project/templates/front-end-inertia/app/config/locales/foo/en.yml +2 -0
- data/lib/roda/project/templates/front-end-inertia/app/config/locales/foo/pt-br.yml +2 -0
- data/lib/roda/project/templates/front-end-inertia/app/config/locales/pt-br.yml +244 -0
- data/lib/roda/project/templates/front-end-inertia/app/views/foo/bar.erb +1 -0
- data/lib/roda/project/templates/front-end-inertia/app/views/index.erb +1 -0
- data/lib/roda/project/templates/front-end-inertia/app/views/layout.erb +23 -0
- data/lib/roda/project/templates/front-end-inertia/esbuild.js +53 -0
- data/lib/roda/project/templates/front-end-inertia/package.json +23 -0
- data/lib/roda/project/templates/front-end-inertia/public/assets/app.css +1 -0
- data/lib/roda/project/templates/front-end-inertia/public/images/roda-project.png +0 -0
- data/lib/roda/project/templates/tests-rspec/spec/app/views/html_spec.rb +0 -0
- data/lib/roda/project/version.rb +1 -1
- data/lib/roda/project.rb +3 -0
- metadata +53 -38
- data/lib/roda/project/templates/front-end/app/assets/js/some/foo.js +0 -5
- /data/lib/roda/project/templates/{base/minimal → base-minimal}/AGENTS.md +0 -0
- /data/lib/roda/project/templates/{base/minimal → base-minimal}/Gemfile.erb +0 -0
- /data/lib/roda/project/templates/{base/minimal → base-minimal}/Gemfile.lock +0 -0
- /data/lib/roda/project/templates/{base/minimal → base-minimal}/Guardfile +0 -0
- /data/lib/roda/project/templates/{base/minimal → base-minimal}/README.md +0 -0
- /data/lib/roda/project/templates/{base/minimal → base-minimal}/app.rb +0 -0
- /data/lib/roda/project/templates/{base/minimal → base-minimal}/bin/roda.erb +0 -0
- /data/lib/roda/project/templates/{base/minimal → base-minimal}/boot.rb +0 -0
- /data/lib/roda/project/templates/{base/minimal → base-minimal}/config.ru +0 -0
- /data/lib/roda/project/templates/{base/minimal → base-minimal}/public/exception_page.css +0 -0
- /data/lib/roda/project/templates/{base/scaffold → base-scaffold}/Guardfile +0 -0
- /data/lib/roda/project/templates/{base/scaffold → base-scaffold}/README.md +0 -0
- /data/lib/roda/project/templates/{base/scaffold → base-scaffold}/app/config/config.rb.erb +0 -0
- /data/lib/roda/project/templates/{base/scaffold → base-scaffold}/app/config/providers/logger.rb +0 -0
- /data/lib/roda/project/templates/{base/scaffold → base-scaffold}/app/config/providers/mailer.rb +0 -0
- /data/lib/roda/project/templates/{base/scaffold → base-scaffold}/app/routes/foo.rb.erb +0 -0
- /data/lib/roda/project/templates/{base/scaffold → base-scaffold}/bin/roda.erb +0 -0
- /data/lib/roda/project/templates/{base/scaffold → base-scaffold}/boot.rb.erb +0 -0
- /data/lib/roda/project/templates/{base/scaffold → base-scaffold}/config.ru.erb +0 -0
- /data/lib/roda/project/templates/{base/scaffold → base-scaffold}/public/exception_page.css +0 -0
- /data/lib/roda/project/templates/{tests/minitest/spec/app/views/html_spec.rb → front-end-inertia/public/assets/app.js} +0 -0
- /data/lib/roda/project/templates/{tests/minimal/minitest → tests-minimal-minitest}/spec/app_spec.rb +0 -0
- /data/lib/roda/project/templates/{tests/minimal/minitest → tests-minimal-minitest}/spec/spec_helper.rb.erb +0 -0
- /data/lib/roda/project/templates/{tests/minimal/rspec → tests-minimal-rspec}/spec/app_spec.rb +0 -0
- /data/lib/roda/project/templates/{tests/minimal/rspec → tests-minimal-rspec}/spec/spec_helper.rb +0 -0
- /data/lib/roda/project/templates/{tests/minitest → tests-minitest}/spec/app/app_spec.rb.erb +0 -0
- /data/lib/roda/project/templates/{tests/minitest → tests-minitest}/spec/app/routes/foo_spec.rb.erb +0 -0
- /data/lib/roda/project/templates/{tests/minitest → tests-minitest}/spec/app/routes/test_branch_spec.rb +0 -0
- /data/lib/roda/project/templates/{tests/rspec → tests-minitest}/spec/app/views/html_spec.rb +0 -0
- /data/lib/roda/project/templates/{tests/minitest → tests-minitest}/spec/spec_helper.rb.erb +0 -0
- /data/lib/roda/project/templates/{tests/rspec → tests-rspec}/spec/app/app_spec.rb.erb +0 -0
- /data/lib/roda/project/templates/{tests/rspec → tests-rspec}/spec/app/routes/foo_spec.rb.erb +0 -0
- /data/lib/roda/project/templates/{tests/rspec → tests-rspec}/spec/app/routes/test_branch_spec.rb +0 -0
- /data/lib/roda/project/templates/{tests/rspec → tests-rspec}/spec/spec_helper.rb.erb +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f0b645d449bd52931506a9e9ee0ef203d14f8b6606cee831d1570bae7e8b8e66
|
|
4
|
+
data.tar.gz: ca8c1717d95f3bb0aebe8f698df9dee7306eb687b2c7d763a2f6d3d4ea46a746
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f60f531f4683da41ba01d19e67ded642ffb4cf8e828044287354d11362bb31a07cdb6ff4affd75483ebc384d11140b4c9014173af24116b95c4efaaba93173c5
|
|
7
|
+
data.tar.gz: 0d87b9e35efedb3f34dd250edfbdf12bae629d1c92562d9a0652aff2c6dd741a67515119ec08ac79855a7626f5f65766c5a59c13a2399cdeef126b3a58a42717
|
|
@@ -4,9 +4,12 @@ class Roda
|
|
|
4
4
|
module Bin
|
|
5
5
|
class Generators < ::Thor
|
|
6
6
|
class Routes < Roda::Project::Bin::Generator
|
|
7
|
+
DEFAULT_VIEW_NAME = "index"
|
|
8
|
+
VALID_METHODS = %w[get post delete put patch].freeze
|
|
9
|
+
|
|
7
10
|
def call
|
|
8
11
|
if branch_name.nil? || branch_name.empty? || routes_list.nil? || routes_list.empty?
|
|
9
|
-
puts "Usage: bin/roda g routes branch_name route1 route2
|
|
12
|
+
puts "Usage: bin/roda g routes branch_name route1 method:route2"
|
|
10
13
|
exit 1
|
|
11
14
|
end
|
|
12
15
|
|
|
@@ -21,52 +24,40 @@ class Roda
|
|
|
21
24
|
|
|
22
25
|
def generate_routes
|
|
23
26
|
filename = File.join(ensure_and_get_path("app/routes", branch_name), "#{branch_name}.rb")
|
|
24
|
-
route_definitions = routes_list.map
|
|
27
|
+
route_definitions = routes_list.map { |route_str| build_route_definition(route_str) }.join("\n\n")
|
|
28
|
+
|
|
29
|
+
write_file(filename, routes_file_content(route_definitions))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def generate_views
|
|
33
|
+
return unless must_generate_views?
|
|
34
|
+
|
|
35
|
+
@context.inertia? ? generate_inertia_views : generate_erb_views
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def generate_erb_views
|
|
39
|
+
branch_views_dir = File.join("app/views", branch_name)
|
|
40
|
+
FileUtils.mkdir_p(branch_views_dir)
|
|
41
|
+
routes_list.each do |route_str|
|
|
25
42
|
method, name = parse_route_string(route_str)
|
|
26
|
-
|
|
27
|
-
view_name = "index" if view_name == nil
|
|
28
|
-
view_line = (method == "get" && must_generate_views?) ? "\n view('#{view_name}')" : ""
|
|
29
|
-
|
|
30
|
-
if name != nil
|
|
31
|
-
" r.#{method} \"#{name}\" do#{view_line}\n end"
|
|
32
|
-
else
|
|
33
|
-
" r.#{method} do#{view_line}\n end"
|
|
34
|
-
end
|
|
35
|
-
end.join("\n\n")
|
|
43
|
+
next unless method == "get"
|
|
36
44
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
sub_path = parts[0..-2].join("/")
|
|
40
|
-
branch_segment = parts.last
|
|
41
|
-
"hash_branch #{namespace_for(sub_path)}, \"#{branch_segment}\" do |r|"
|
|
42
|
-
else
|
|
43
|
-
"hash_branch \"#{branch_name}\" do |r|"
|
|
45
|
+
name = DEFAULT_VIEW_NAME if name.nil?
|
|
46
|
+
write_file(File.join(branch_views_dir, "#{name}.erb"), "")
|
|
44
47
|
end
|
|
45
|
-
|
|
46
|
-
content = <<~RUBY
|
|
47
|
-
class #{@context.const_project_name}
|
|
48
|
-
#{hash_branch_header}
|
|
49
|
-
#{route_definitions}
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
RUBY
|
|
53
|
-
File.write(filename, content)
|
|
54
|
-
action_success_message(filename)
|
|
55
48
|
end
|
|
56
49
|
|
|
57
|
-
def
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
end
|
|
69
|
-
end
|
|
50
|
+
def generate_inertia_views
|
|
51
|
+
views_dir = "app/assets/js/inertia/views"
|
|
52
|
+
FileUtils.mkdir_p(views_dir)
|
|
53
|
+
routes_list.each do |route_str|
|
|
54
|
+
method, name = parse_route_string(route_str)
|
|
55
|
+
next unless method == "get"
|
|
56
|
+
|
|
57
|
+
path = InertiaPath.new(branch_name, name)
|
|
58
|
+
view_filename = File.join(views_dir, "#{path.view_path}.jsx")
|
|
59
|
+
FileUtils.mkdir_p(File.dirname(view_filename))
|
|
60
|
+
write_file(view_filename, inertia_component_content(path))
|
|
70
61
|
end
|
|
71
62
|
end
|
|
72
63
|
|
|
@@ -75,31 +66,16 @@ class Roda
|
|
|
75
66
|
nesting_level = branch_name.count("/")
|
|
76
67
|
relative_spec_helper_path = "../" * (2 + nesting_level) + "spec_helper"
|
|
77
68
|
|
|
78
|
-
test_route_definitions = routes_list.map
|
|
79
|
-
method, name = parse_route_string(route_str)
|
|
80
|
-
|
|
81
|
-
if name != nil
|
|
82
|
-
" it \"responds to #{method.upcase} /#{branch_name}/#{name}\" do\n" \
|
|
83
|
-
" #{method} \"/#{branch_name}/#{name}\"\n" \
|
|
84
|
-
" expect(last_response.status).to eq(200)\n" \
|
|
85
|
-
" end"
|
|
86
|
-
else
|
|
87
|
-
" it \"responds to #{method.upcase} /#{branch_name}\" do\n" \
|
|
88
|
-
" #{method} \"/#{branch_name}\"\n" \
|
|
89
|
-
" expect(last_response.status).to eq(200)\n" \
|
|
90
|
-
" end"
|
|
91
|
-
end
|
|
92
|
-
end.join("\n")
|
|
69
|
+
test_route_definitions = routes_list.map { |route_str| build_test_definition(route_str) }.join("\n")
|
|
93
70
|
|
|
94
71
|
test_content = <<~RUBY
|
|
95
|
-
|
|
72
|
+
require_relative "#{relative_spec_helper_path}"
|
|
96
73
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
74
|
+
describe "Routes for #{branch_name}" do
|
|
75
|
+
#{test_route_definitions}
|
|
76
|
+
end
|
|
100
77
|
RUBY
|
|
101
|
-
|
|
102
|
-
action_success_message(test_filename)
|
|
78
|
+
write_file(test_filename, test_content)
|
|
103
79
|
end
|
|
104
80
|
|
|
105
81
|
def generate_nested_branch_files
|
|
@@ -117,11 +93,61 @@ class Roda
|
|
|
117
93
|
|
|
118
94
|
dir = File.dirname(filename)
|
|
119
95
|
FileUtils.mkdir_p(dir) unless File.directory?(dir)
|
|
120
|
-
|
|
121
|
-
|
|
96
|
+
write_file(filename, nested_branch_file_content(prefix, index))
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def build_route_definition(route_str)
|
|
101
|
+
method, name = parse_route_string(route_str)
|
|
102
|
+
view_line = build_view_line(method, name)
|
|
103
|
+
route_block(method, name, view_line)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def build_view_line(method, name)
|
|
107
|
+
return "" unless method == "get" && must_generate_views?
|
|
108
|
+
|
|
109
|
+
if @context.inertia?
|
|
110
|
+
"\n inertia('#{InertiaPath.new(branch_name, name).view_path}')"
|
|
111
|
+
else
|
|
112
|
+
"\n view('#{name || DEFAULT_VIEW_NAME}')"
|
|
122
113
|
end
|
|
123
114
|
end
|
|
124
115
|
|
|
116
|
+
def route_block(method, name, view_line)
|
|
117
|
+
route_name = name ? " \"#{name}\"" : ""
|
|
118
|
+
" r.#{method}#{route_name} do#{view_line}\n end"
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def build_test_definition(route_str)
|
|
122
|
+
method, name = parse_route_string(route_str)
|
|
123
|
+
test_name = name ? " /#{branch_name}/#{name}" : " /#{branch_name}"
|
|
124
|
+
test_path = name ? " \"/#{branch_name}/#{name}\"" : " \"/#{branch_name}\""
|
|
125
|
+
|
|
126
|
+
" it \"responds to #{method.upcase}#{test_name}\" do\n" \
|
|
127
|
+
" #{method}#{test_path}\n" \
|
|
128
|
+
" expect(last_response.status).to eq(200)\n" \
|
|
129
|
+
" end"
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def routes_file_content(route_definitions)
|
|
133
|
+
<<~RUBY
|
|
134
|
+
class #{@context.const_project_name}
|
|
135
|
+
#{build_hash_branch_header}
|
|
136
|
+
#{route_definitions}
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
RUBY
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def build_hash_branch_header
|
|
143
|
+
return "hash_branch \"#{branch_name}\" do |r|" unless branch_name.include?("/")
|
|
144
|
+
|
|
145
|
+
parts = branch_name.split("/")
|
|
146
|
+
sub_path = parts[0..-2].join("/")
|
|
147
|
+
branch_segment = parts.last
|
|
148
|
+
"hash_branch #{namespace_for(sub_path)}, \"#{branch_segment}\" do |r|"
|
|
149
|
+
end
|
|
150
|
+
|
|
125
151
|
def nested_branch_file_content(prefix, index)
|
|
126
152
|
parts = prefix.split("/")
|
|
127
153
|
segment = parts.last
|
|
@@ -133,11 +159,11 @@ class Roda
|
|
|
133
159
|
end
|
|
134
160
|
|
|
135
161
|
<<~RUBY
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
162
|
+
class #{@context.const_project_name}
|
|
163
|
+
#{header} # #{prefix} branch
|
|
164
|
+
r.hash_branches(#{namespace_for(prefix)}) # #{prefix}/ +1 routes
|
|
165
|
+
end
|
|
166
|
+
end
|
|
141
167
|
RUBY
|
|
142
168
|
end
|
|
143
169
|
|
|
@@ -154,12 +180,47 @@ class Roda
|
|
|
154
180
|
end
|
|
155
181
|
|
|
156
182
|
def must_generate_views?
|
|
157
|
-
@options[:views]
|
|
183
|
+
return false unless @options[:views]
|
|
184
|
+
|
|
185
|
+
if @context.inertia?
|
|
186
|
+
Dir.exist?("app/assets/js/inertia/views")
|
|
187
|
+
else
|
|
188
|
+
Dir.exist?("app/views")
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def inertia_component_content(path)
|
|
193
|
+
<<~JSX
|
|
194
|
+
import React from 'react'
|
|
195
|
+
import { Link } from '@inertiajs/react'
|
|
196
|
+
import Layout from '../components/Layout'
|
|
197
|
+
|
|
198
|
+
const #{path.component_name} = props => {
|
|
199
|
+
return (<Layout>
|
|
200
|
+
<div>
|
|
201
|
+
#{path.display_path}
|
|
202
|
+
</div>
|
|
203
|
+
</Layout>)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export default #{path.component_name}
|
|
207
|
+
JSX
|
|
158
208
|
end
|
|
159
209
|
|
|
160
210
|
def parse_route_string(route_str)
|
|
161
211
|
method, name = route_str.split(":", 2)
|
|
162
|
-
|
|
212
|
+
if name && VALID_METHODS.include?(method)
|
|
213
|
+
[method, name]
|
|
214
|
+
elsif name.nil? && VALID_METHODS.include?(route_str)
|
|
215
|
+
[route_str, nil]
|
|
216
|
+
else
|
|
217
|
+
["get", route_str]
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def write_file(filename, content)
|
|
222
|
+
File.write(filename, content)
|
|
223
|
+
action_success_message(filename)
|
|
163
224
|
end
|
|
164
225
|
|
|
165
226
|
def routes_list
|
|
@@ -169,6 +230,34 @@ class Roda
|
|
|
169
230
|
def branch_name
|
|
170
231
|
@branch_name ||= @args[0]
|
|
171
232
|
end
|
|
233
|
+
|
|
234
|
+
class InertiaPath
|
|
235
|
+
attr_reader :view_path, :component_name, :display_path
|
|
236
|
+
|
|
237
|
+
def initialize(branch_name, name)
|
|
238
|
+
@view_path = build_view_path(branch_name, name)
|
|
239
|
+
@component_name = build_component_name(name)
|
|
240
|
+
@display_path = build_display_path(branch_name, name)
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
private
|
|
244
|
+
|
|
245
|
+
def build_view_path(branch_name, name)
|
|
246
|
+
segments = branch_name.split("/")
|
|
247
|
+
segments << (name || "index")
|
|
248
|
+
(segments[0..-2] + [segments.last.capitalize]).join("/")
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def build_component_name(name)
|
|
252
|
+
(name || "index").capitalize
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def build_display_path(branch_name, name)
|
|
256
|
+
segments = branch_name.split("/")
|
|
257
|
+
segments << (name || "index")
|
|
258
|
+
segments.join("/")
|
|
259
|
+
end
|
|
260
|
+
end
|
|
172
261
|
end
|
|
173
262
|
end
|
|
174
263
|
end
|
|
@@ -37,6 +37,7 @@ class Roda
|
|
|
37
37
|
@context = Roda::Project::MainContext.new
|
|
38
38
|
@context.project_name = options[:main_context][:project_name]
|
|
39
39
|
@context.base = options[:main_context][:base]
|
|
40
|
+
@context.frontend = options[:main_context][:frontend]
|
|
40
41
|
@context.database = options[:main_context][:database]
|
|
41
42
|
@context.database_type = options[:main_context][:database_type]
|
|
42
43
|
@context.rodauth = options[:main_context][:rodauth]
|
data/lib/roda/project/cli.rb
CHANGED
|
@@ -49,6 +49,10 @@ class Roda
|
|
|
49
49
|
return if jeremy?
|
|
50
50
|
|
|
51
51
|
retry_on_error { @context.base = read_line("(#{fullstack_id}) Fullstack (#{api_id}) API (#{minimal_id}) Minimal › ", fullstack_id).to_i }
|
|
52
|
+
if @context.fullstack?
|
|
53
|
+
retry_on_error { @context.frontend = read_line("(#{erb_id}) only ERB (#{inertia_id}) Inertia.js › ", erb_id).to_i }
|
|
54
|
+
end
|
|
55
|
+
|
|
52
56
|
retry_on_error { @context.tests = read_line("(#{rspec_id}) RSpec (#{minitest_id}) Minitest › ", rspec_id).to_i }
|
|
53
57
|
|
|
54
58
|
unless @context.minimal?
|
|
@@ -75,7 +79,7 @@ class Roda
|
|
|
75
79
|
puts "* creating base project"
|
|
76
80
|
if @context.minimal?
|
|
77
81
|
TTY::File.copy_directory(
|
|
78
|
-
File.expand_path("templates/base
|
|
82
|
+
File.expand_path("templates/base-minimal", __dir__),
|
|
79
83
|
"#{@dir}#{@context.project_name}",
|
|
80
84
|
context: @context
|
|
81
85
|
)
|
|
@@ -84,7 +88,7 @@ class Roda
|
|
|
84
88
|
end
|
|
85
89
|
|
|
86
90
|
TTY::File.copy_directory(
|
|
87
|
-
File.expand_path("templates/base
|
|
91
|
+
File.expand_path("templates/base-scaffold", __dir__),
|
|
88
92
|
"#{@dir}#{@context.project_name}",
|
|
89
93
|
context: @context
|
|
90
94
|
)
|
|
@@ -101,16 +105,19 @@ class Roda
|
|
|
101
105
|
def add_front_end
|
|
102
106
|
if @context.fullstack?
|
|
103
107
|
puts "* adding front-end"
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
+
from_template = "front-end"
|
|
109
|
+
from_template = "front-end-inertia" if @context.inertia?
|
|
110
|
+
|
|
111
|
+
erb_cp_dir(from_template, "app/assets")
|
|
112
|
+
erb_cp_file(from_template, "esbuild.js")
|
|
113
|
+
erb_cp_file(from_template, "package.json")
|
|
114
|
+
cp_dir(from_template, "app/config/locales")
|
|
108
115
|
action_success_message("app/config/locales")
|
|
109
|
-
cp_dir(
|
|
116
|
+
cp_dir(from_template, "app/views")
|
|
110
117
|
action_success_message("app/views")
|
|
111
|
-
cp_dir(
|
|
118
|
+
cp_dir(from_template, "public/assets")
|
|
112
119
|
action_success_message("public/assets")
|
|
113
|
-
cp_dir(
|
|
120
|
+
cp_dir(from_template, "public/images")
|
|
114
121
|
action_success_message("public/images")
|
|
115
122
|
end
|
|
116
123
|
end
|
|
@@ -138,12 +145,12 @@ class Roda
|
|
|
138
145
|
|
|
139
146
|
def add_test_files
|
|
140
147
|
puts "* adding test files"
|
|
141
|
-
minimal_dir = @context.minimal? ? "minimal
|
|
148
|
+
minimal_dir = @context.minimal? ? "minimal-" : ""
|
|
142
149
|
|
|
143
150
|
if @context.rspec?
|
|
144
|
-
erb_cp_dir("tests
|
|
151
|
+
erb_cp_dir("tests-#{minimal_dir}rspec", "spec")
|
|
145
152
|
else
|
|
146
|
-
erb_cp_dir("tests
|
|
153
|
+
erb_cp_dir("tests-#{minimal_dir}minitest", "spec")
|
|
147
154
|
end
|
|
148
155
|
end
|
|
149
156
|
end
|
|
@@ -3,6 +3,8 @@ class Roda
|
|
|
3
3
|
module Helpers
|
|
4
4
|
module Ids
|
|
5
5
|
def fullstack_id = Roda::Project::FULLSTACK
|
|
6
|
+
def erb_id = Roda::Project::ERB
|
|
7
|
+
def inertia_id = Roda::Project::INERTIA
|
|
6
8
|
def api_id = Roda::Project::API
|
|
7
9
|
def minimal_id = Roda::Project::MINIMAL
|
|
8
10
|
def mysql_id = Roda::Project::MYSQL
|
|
@@ -23,6 +25,13 @@ class Roda
|
|
|
23
25
|
when minimal_id
|
|
24
26
|
"MINIMAL"
|
|
25
27
|
end
|
|
28
|
+
when :frontend
|
|
29
|
+
case id
|
|
30
|
+
when erb_id
|
|
31
|
+
"ERB"
|
|
32
|
+
when inertia_id
|
|
33
|
+
"INERTIA"
|
|
34
|
+
end
|
|
26
35
|
when :database
|
|
27
36
|
case id
|
|
28
37
|
when mysql_id
|
|
@@ -15,7 +15,8 @@ class Roda
|
|
|
15
15
|
:default_db_url,
|
|
16
16
|
:db_gem,
|
|
17
17
|
:project_name,
|
|
18
|
-
:base
|
|
18
|
+
:base,
|
|
19
|
+
:frontend
|
|
19
20
|
)
|
|
20
21
|
|
|
21
22
|
def to_thor_option
|
|
@@ -30,6 +31,9 @@ class Roda
|
|
|
30
31
|
if database?
|
|
31
32
|
option << " database_type: #{id_to_string(database_type, :database)},"
|
|
32
33
|
end
|
|
34
|
+
if fullstack?
|
|
35
|
+
option << "\n frontend: #{id_to_string(frontend, :frontend)},"
|
|
36
|
+
end
|
|
33
37
|
option << "\n }"
|
|
34
38
|
end
|
|
35
39
|
|
|
@@ -49,6 +53,14 @@ class Roda
|
|
|
49
53
|
@base = val
|
|
50
54
|
end
|
|
51
55
|
|
|
56
|
+
def frontend=(val)
|
|
57
|
+
if ![erb_id, inertia_id].include?(val)
|
|
58
|
+
raise InvalidValue, "Invalid frontend option: #{val}"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
@frontend = val
|
|
62
|
+
end
|
|
63
|
+
|
|
52
64
|
def rodauth=(val)
|
|
53
65
|
val = false if ![true, false].include?(val)
|
|
54
66
|
|
|
@@ -106,6 +118,7 @@ class Roda
|
|
|
106
118
|
end
|
|
107
119
|
|
|
108
120
|
def root_example
|
|
121
|
+
return 'inertia("Index")' if inertia?
|
|
109
122
|
return 'view("index")' if fullstack?
|
|
110
123
|
|
|
111
124
|
'{ message: "hello world" }'
|
|
@@ -124,6 +137,14 @@ class Roda
|
|
|
124
137
|
base == minimal_id
|
|
125
138
|
end
|
|
126
139
|
|
|
140
|
+
def inertia?
|
|
141
|
+
frontend == inertia_id
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def erb?
|
|
145
|
+
frontend == erb_id
|
|
146
|
+
end
|
|
147
|
+
|
|
127
148
|
def rodauth?
|
|
128
149
|
rodauth == true
|
|
129
150
|
end
|
|
@@ -12,8 +12,17 @@ class <%= context.const_project_name %> < Roda
|
|
|
12
12
|
plugin :render, **Config.get[:render]
|
|
13
13
|
plugin :content_for, append: false
|
|
14
14
|
plugin :flash
|
|
15
|
-
plugin :assets_manifest
|
|
16
|
-
|
|
15
|
+
plugin :assets_manifest<% if context.inertia? %>
|
|
16
|
+
plugin :inertia,
|
|
17
|
+
version: assets_version,
|
|
18
|
+
layout: {
|
|
19
|
+
head: [
|
|
20
|
+
'<meta http-equiv="x-ua-compatible" content="ie=edge">',
|
|
21
|
+
css_entrypoint_tag(:app)
|
|
22
|
+
].join,
|
|
23
|
+
body: js_entrypoint_tag(:app)
|
|
24
|
+
}
|
|
25
|
+
<% end %><% end %>
|
|
17
26
|
# Request / Response
|
|
18
27
|
plugin :halt
|
|
19
28
|
plugin :json
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
- **Web Framework**: Roda (STRICT: Do NOT use Rails, ActionController, or Sinatra conventions).
|
|
6
6
|
- **No Rails Magic:** Do not use `ActiveSupport` methods (like `.present?` or `.blank?`) unless the gem is explicitly in the Gemfile.<% if context.database %>
|
|
7
7
|
- **ORM**: Sequel (STRICT: Do NOT use ActiveRecord. Use Sequel Models, Datasets, and Migrations).<% end %>
|
|
8
|
-
- **Autoloader**: Zeitwerk (Do NOT manually `require` files located in the `app/` directory. Only require standard libraries or gems).<% if context.fullstack? %>
|
|
9
|
-
- **Frontend**: esbuild + ERB (Fullstack mode).<%
|
|
8
|
+
- **Autoloader**: Zeitwerk (Do NOT manually `require` files located in the `app/` directory. Only require standard libraries or gems).<% if context.fullstack? && context.erb? %>
|
|
9
|
+
- **Frontend**: esbuild + ERB (Fullstack mode).<% else %>
|
|
10
|
+
- **Frontend**: esbuild + Inertia.js + React.js (Fullstack mode).<% end %>
|
|
10
11
|
|
|
11
12
|
## Code Style & Conventions<%= i = 0 %>
|
|
12
13
|
|
|
@@ -15,13 +16,18 @@
|
|
|
15
16
|
- Rely on standard Ruby 3.x patterns. Use explicit YARD docs for method signatures.
|
|
16
17
|
|
|
17
18
|
<% if context.fullstack? %>### <%= i=i+1 %>. Views & Presentation
|
|
18
|
-
|
|
19
|
+
<% if context.erb? %>
|
|
19
20
|
- ERB templates go in `app/views/`.
|
|
20
21
|
- The `view` method inside a route block will match the hash branch to find the view path location:
|
|
21
22
|
- For `hash_branch "foo/bar", "zas" do |r|`, calling `view('something')` will search an erb file in "views/foo/bar/zas/something.erb".
|
|
22
|
-
- For `hash_branch "zas" do |r|`, calling `view('something')` will search an erb file in "views/zas/something.erb"
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
- For `hash_branch "zas" do |r|`, calling `view('something')` will search an erb file in "views/zas/something.erb".<% else %>
|
|
24
|
+
- Inertia.js views go in `app/assets/js/inertia/views/`.
|
|
25
|
+
- The `inertia` method inside a route block will find the inertia view location and pass the props to the component:
|
|
26
|
+
- Calling `inertia('Something')` will search an inertia view file in "app/assets/js/inertia/views/Something.jsx".
|
|
27
|
+
- Calling `inertia('Zoo', { name: "foo", age: 3 })` will search an inertia view file in "app/assets/js/inertia/views/Zoo.jsx" and set the props data in the React function component.
|
|
28
|
+
- Calling `inertia('Foo/Bar')` will search an inertia view file in "app/assets/js/inertia/views/Foo/Bar.jsx".<% end %>
|
|
29
|
+
<% end %>
|
|
30
|
+
### <%= i=i+1 %>. Routing
|
|
25
31
|
|
|
26
32
|
- The main app resides in `app/[project_name].rb`.
|
|
27
33
|
- Sub-routes MUST be implemented using Roda **Hash Branches** (`hash_branches` plugin) and placed in `app/routes/`.
|
|
@@ -22,8 +22,9 @@ gem "puma"
|
|
|
22
22
|
# gem 'thin'<% if context.fullstack? %>
|
|
23
23
|
|
|
24
24
|
# Views
|
|
25
|
-
gem 'roda-assets_manifest'
|
|
26
|
-
|
|
25
|
+
gem 'roda-assets_manifest'<% if context.inertia? %>
|
|
26
|
+
gem 'roda-inertia-lite'
|
|
27
|
+
<% end %><% end %>
|
|
27
28
|
# Performance
|
|
28
29
|
gem "oj"
|
|
29
30
|
# gem 'async'
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
body {
|
|
2
|
+
background: black;
|
|
3
|
+
color: white;
|
|
4
|
+
margin: 1em 2em;
|
|
5
|
+
font-family: monospace;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
a {
|
|
9
|
+
color: #ef4348;
|
|
10
|
+
margin-right: 1em;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
img {
|
|
14
|
+
width: 25%;
|
|
15
|
+
margin-bottom: 1em;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.navbar {
|
|
19
|
+
margin-bottom: 1em;
|
|
20
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { createInertiaApp } from '@inertiajs/react'
|
|
3
|
+
import { createRoot } from 'react-dom/client'
|
|
4
|
+
|
|
5
|
+
createInertiaApp({
|
|
6
|
+
resolve: name => import(`./inertia/views/${name}.jsx`),
|
|
7
|
+
page: JSON.parse(document.getElementById('app').dataset.page),
|
|
8
|
+
setup({ el, App, props }) {
|
|
9
|
+
createRoot(el).render(<App {...props} />)
|
|
10
|
+
},
|
|
11
|
+
})
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import Layout from '../components/Layout'
|
|
3
|
+
//import { Link } from '@inertiajs/react'
|
|
4
|
+
|
|
5
|
+
const Index = props => {
|
|
6
|
+
return (<Layout>
|
|
7
|
+
<div>
|
|
8
|
+
Hello from Roda + Inertia.js!
|
|
9
|
+
</div>
|
|
10
|
+
</Layout>)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default Index
|