nextgen 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +143 -0
- data/config/generators.yml +146 -0
- data/exe/nextgen +13 -0
- data/lib/nextgen/actions/bundler.rb +62 -0
- data/lib/nextgen/actions/git.rb +56 -0
- data/lib/nextgen/actions/yarn.rb +32 -0
- data/lib/nextgen/actions.rb +155 -0
- data/lib/nextgen/cli.rb +19 -0
- data/lib/nextgen/commands/create.rb +312 -0
- data/lib/nextgen/ext/prompt/list.rb +13 -0
- data/lib/nextgen/ext/prompt/multilist.rb +16 -0
- data/lib/nextgen/generators/action_mailer.rb +26 -0
- data/lib/nextgen/generators/annotate.rb +2 -0
- data/lib/nextgen/generators/base.rb +60 -0
- data/lib/nextgen/generators/basic_auth.rb +5 -0
- data/lib/nextgen/generators/brakeman.rb +2 -0
- data/lib/nextgen/generators/bundler_audit.rb +2 -0
- data/lib/nextgen/generators/capybara_lockstep.rb +4 -0
- data/lib/nextgen/generators/clean_gemfile.rb +1 -0
- data/lib/nextgen/generators/dotenv.rb +3 -0
- data/lib/nextgen/generators/erb_lint.rb +11 -0
- data/lib/nextgen/generators/eslint.rb +24 -0
- data/lib/nextgen/generators/factory_bot_rails.rb +16 -0
- data/lib/nextgen/generators/git_safe.rb +4 -0
- data/lib/nextgen/generators/github_actions.rb +2 -0
- data/lib/nextgen/generators/good_migrations.rb +1 -0
- data/lib/nextgen/generators/home_controller.rb +3 -0
- data/lib/nextgen/generators/initial_git_commit.rb +5 -0
- data/lib/nextgen/generators/initial_migrations.rb +11 -0
- data/lib/nextgen/generators/letter_opener.rb +8 -0
- data/lib/nextgen/generators/node.rb +5 -0
- data/lib/nextgen/generators/open_browser_on_start.rb +12 -0
- data/lib/nextgen/generators/overcommit.rb +1 -0
- data/lib/nextgen/generators/pgcli_rails.rb +1 -0
- data/lib/nextgen/generators/rack_canonical_host.rb +8 -0
- data/lib/nextgen/generators/rack_mini_profiler.rb +2 -0
- data/lib/nextgen/generators/rspec_rails.rb +19 -0
- data/lib/nextgen/generators/rspec_system_testing.rb +5 -0
- data/lib/nextgen/generators/rubocop.rb +32 -0
- data/lib/nextgen/generators/shoulda.rb +6 -0
- data/lib/nextgen/generators/sidekiq.rb +30 -0
- data/lib/nextgen/generators/stylelint.rb +24 -0
- data/lib/nextgen/generators/thor.rb +2 -0
- data/lib/nextgen/generators/tomo.rb +10 -0
- data/lib/nextgen/generators/vite.rb +103 -0
- data/lib/nextgen/generators.rb +87 -0
- data/lib/nextgen/rails.rb +39 -0
- data/lib/nextgen/rails_options.rb +191 -0
- data/lib/nextgen/thor_extensions.rb +48 -0
- data/lib/nextgen/tidy_gemfile.rb +71 -0
- data/lib/nextgen/version.rb +3 -0
- data/lib/nextgen.rb +17 -0
- data/template/.editorconfig +14 -0
- data/template/.env.sample +2 -0
- data/template/.erb-lint.yml.tt +25 -0
- data/template/.eslintrc.cjs +26 -0
- data/template/.github/workflows/ci.yml.tt +142 -0
- data/template/.overcommit.yml.tt +86 -0
- data/template/.prettierrc.cjs +6 -0
- data/template/.rubocop.yml.tt +269 -0
- data/template/.stylelintrc.cjs +52 -0
- data/template/DEPLOYMENT.md +10 -0
- data/template/Procfile.tt +4 -0
- data/template/README.md.tt +52 -0
- data/template/Thorfile +7 -0
- data/template/app/controllers/concerns/basic_auth.rb +20 -0
- data/template/app/controllers/home_controller.rb +4 -0
- data/template/app/frontend/controllers/index.js +5 -0
- data/template/app/frontend/images/example.svg +3 -0
- data/template/app/frontend/stylesheets/base.css +8 -0
- data/template/app/frontend/stylesheets/index.css +3 -0
- data/template/app/frontend/stylesheets/reset.css +36 -0
- data/template/app/helpers/inline_svg_helper.rb +9 -0
- data/template/app/views/home/index.html.erb.tt +5 -0
- data/template/bin/setup +107 -0
- data/template/config/initializers/generators.rb +5 -0
- data/template/config/initializers/rack_mini_profiler.rb +4 -0
- data/template/config/initializers/sidekiq.rb +32 -0
- data/template/config/sidekiq.yml +5 -0
- data/template/lib/puma/plugin/open.rb +14 -0
- data/template/lib/tasks/auto_annotate_models.rake +52 -0
- data/template/lib/tasks/erblint.rake +11 -0
- data/template/lib/tasks/eslint.rake +11 -0
- data/template/lib/tasks/rubocop.rake +4 -0
- data/template/lib/tasks/stylelint.rake +11 -0
- data/template/lib/templates/rspec/system/system_spec.rb +10 -0
- data/template/lib/vite_inline_svg_file_loader.rb +25 -0
- data/template/package.json +6 -0
- data/template/postcss.config.js +3 -0
- data/template/spec/support/factory_bot.rb +3 -0
- data/template/spec/support/mailer.rb +5 -0
- data/template/spec/support/shoulda.rb +6 -0
- data/template/spec/support/system.rb +17 -0
- data/template/test/application_system_test_case.rb +18 -0
- data/template/test/helpers/inline_svg_helper_test.rb +23 -0
- data/template/test/support/factory_bot.rb +3 -0
- data/template/test/support/mailer.rb +3 -0
- data/template/test/support/shoulda.rb +6 -0
- data/template/test/support/vite.rb +5 -0
- metadata +220 -0
@@ -0,0 +1,8 @@
|
|
1
|
+
/*
|
2
|
+
This file is for base element styles, like:
|
3
|
+
|
4
|
+
- Any @font-face declarations needed custom web fonts.
|
5
|
+
- Default font-family on the body element.
|
6
|
+
- Default foreground, background, and link colors.
|
7
|
+
- Global CSS variables (declared on :root), such as color palette.
|
8
|
+
*/
|
@@ -0,0 +1,36 @@
|
|
1
|
+
:root {
|
2
|
+
line-height: 1.5;
|
3
|
+
-webkit-font-smoothing: antialiased;
|
4
|
+
}
|
5
|
+
|
6
|
+
h1,
|
7
|
+
h2,
|
8
|
+
h3,
|
9
|
+
h4,
|
10
|
+
h5,
|
11
|
+
figure,
|
12
|
+
p,
|
13
|
+
ol,
|
14
|
+
ul {
|
15
|
+
margin: 0;
|
16
|
+
}
|
17
|
+
|
18
|
+
ol,
|
19
|
+
ul {
|
20
|
+
list-style: none;
|
21
|
+
padding-inline: 0;
|
22
|
+
}
|
23
|
+
|
24
|
+
h1,
|
25
|
+
h2,
|
26
|
+
h3,
|
27
|
+
h4,
|
28
|
+
h5 {
|
29
|
+
font-size: inherit;
|
30
|
+
font-weight: inherit;
|
31
|
+
}
|
32
|
+
|
33
|
+
img {
|
34
|
+
display: block;
|
35
|
+
max-inline-size: 100%;
|
36
|
+
}
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module InlineSvgHelper
|
2
|
+
def inline_svg_tag(filename, title: nil)
|
3
|
+
svg = ViteInlineSvgFileLoader.named(filename)
|
4
|
+
svg = svg.sub(/\A<svg/, '<svg role="img"')
|
5
|
+
svg = svg.sub(/\A<svg.*?>/, safe_join(['\0', "\n", tag.title(title)])) if title.present?
|
6
|
+
|
7
|
+
svg.strip.html_safe # rubocop:disable Rails/OutputSafety
|
8
|
+
end
|
9
|
+
end
|
data/template/bin/setup
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# This script is a way to set up or update your development environment automatically.
|
4
|
+
# This script is idempotent, so that you can run it at any time and get an expectable outcome.
|
5
|
+
# Add necessary setup steps to this method.
|
6
|
+
def setup!
|
7
|
+
run "bundle install" if bundle_needed?
|
8
|
+
run "overcommit --install" if overcommit_installable?
|
9
|
+
run "bin/rails db:prepare" if database_present?
|
10
|
+
run "yarn install" if yarn_needed?
|
11
|
+
run "bin/rails tmp:create" if tmp_missing?
|
12
|
+
env ".env", from: ".env.sample"
|
13
|
+
run "bin/rails restart"
|
14
|
+
|
15
|
+
if git_safe_needed?
|
16
|
+
say_status :notice,
|
17
|
+
"Remember to run #{colorize("mkdir -p .git/safe", :yellow)} to trust the binstubs in this project",
|
18
|
+
:magenta
|
19
|
+
end
|
20
|
+
|
21
|
+
say_status :Ready!,
|
22
|
+
"Use #{colorize("bin/rails s", :yellow)} to start the app, " \
|
23
|
+
"or #{colorize("bin/rake", :yellow)} to run tests"
|
24
|
+
end
|
25
|
+
|
26
|
+
def run(command, echo: true, silent: false, exception: true)
|
27
|
+
say_status(:run, command, :blue) if echo
|
28
|
+
with_original_bundler_env do
|
29
|
+
options = silent ? {out: File::NULL, err: File::NULL} : {}
|
30
|
+
system(command, exception: exception, **options)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def run?(command)
|
35
|
+
run command, silent: true, echo: false, exception: false
|
36
|
+
end
|
37
|
+
|
38
|
+
def bundle_needed?
|
39
|
+
!run("bundle check", silent: true, exception: false)
|
40
|
+
end
|
41
|
+
|
42
|
+
def overcommit_installable?
|
43
|
+
File.exist?(".overcommit.yml") && !File.exist?(".git/hooks/overcommit-hook") && run?("overcommit -v")
|
44
|
+
end
|
45
|
+
|
46
|
+
def database_present?
|
47
|
+
File.exist?("config/database.yml")
|
48
|
+
end
|
49
|
+
|
50
|
+
def yarn_needed?
|
51
|
+
File.exist?("yarn.lock") && !run("yarn check --check-files", silent: true, exception: false)
|
52
|
+
end
|
53
|
+
|
54
|
+
def tmp_missing?
|
55
|
+
!Dir.exist?("tmp/pids")
|
56
|
+
end
|
57
|
+
|
58
|
+
def git_safe_needed?
|
59
|
+
ENV["PATH"].include?(".git/safe/../../bin") && !Dir.exist?(".git/safe")
|
60
|
+
end
|
61
|
+
|
62
|
+
def with_original_bundler_env(&block)
|
63
|
+
return yield unless defined?(Bundler)
|
64
|
+
|
65
|
+
Bundler.with_original_env(&block)
|
66
|
+
end
|
67
|
+
|
68
|
+
def env(env_file, from:)
|
69
|
+
return unless File.exist?(from)
|
70
|
+
|
71
|
+
unless File.exist?(env_file)
|
72
|
+
say_status(:copy, "#{from} → #{env_file}", :magenta)
|
73
|
+
require "fileutils"
|
74
|
+
FileUtils.cp(from, env_file)
|
75
|
+
end
|
76
|
+
|
77
|
+
keys = ->(f) { File.readlines(f).filter_map { |l| l[/^([^#\s][^=\s]*)/, 1] } }
|
78
|
+
|
79
|
+
missing = keys[from] - keys[env_file]
|
80
|
+
return if missing.empty?
|
81
|
+
|
82
|
+
say_status(:WARNING, "Your #{env_file} file is missing #{missing.join(", ")}. Refer to #{from} for details.", :red)
|
83
|
+
end
|
84
|
+
|
85
|
+
def say_status(label, message, color = :green)
|
86
|
+
label = label.to_s.rjust(12)
|
87
|
+
puts [colorize(label, color), message].join(" ")
|
88
|
+
end
|
89
|
+
|
90
|
+
def colorize(str, color)
|
91
|
+
return str unless color_supported?
|
92
|
+
|
93
|
+
code = {red: 31, green: 32, yellow: 33, blue: 34, magenta: 35}.fetch(color)
|
94
|
+
"\e[0;#{code};49m#{str}\e[0m"
|
95
|
+
end
|
96
|
+
|
97
|
+
def color_supported?
|
98
|
+
if ENV["TERM"] == "dumb" || !ENV["NO_COLOR"].to_s.empty?
|
99
|
+
false
|
100
|
+
else
|
101
|
+
[$stdout, $stderr].all? { |io| io.respond_to?(:tty?) && io.tty? }
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
Dir.chdir(File.expand_path("..", __dir__)) do
|
106
|
+
setup!
|
107
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
return unless defined?(Sidekiq)
|
2
|
+
|
3
|
+
# Disable SSL certificate verification if using Heroku Redis
|
4
|
+
# redis_opts = {
|
5
|
+
# ssl_params: {
|
6
|
+
# verify_mode: OpenSSL::SSL::VERIFY_NONE
|
7
|
+
# }
|
8
|
+
# }
|
9
|
+
# Sidekiq.configure_server do |config|
|
10
|
+
# config.redis = redis_opts
|
11
|
+
# end
|
12
|
+
# Sidekiq.configure_client do |config|
|
13
|
+
# config.redis = redis_opts
|
14
|
+
# end
|
15
|
+
|
16
|
+
# Enable Rails CurrentAttributes to flow transparently through to Sidekiq jobs
|
17
|
+
# require "sidekiq/middleware/current_attributes"
|
18
|
+
# Sidekiq::CurrentAttributes.persist(Myapp::Current)
|
19
|
+
|
20
|
+
require "sidekiq/web"
|
21
|
+
|
22
|
+
Sidekiq::Web.app_url = "/"
|
23
|
+
|
24
|
+
sidekiq_username = ENV.fetch("SIDEKIQ_WEB_USERNAME", nil)
|
25
|
+
sidekiq_password = ENV.fetch("SIDEKIQ_WEB_PASSWORD", nil)
|
26
|
+
|
27
|
+
Sidekiq::Web.use(Rack::Auth::Basic, "Sidekiq") do |username, password|
|
28
|
+
if sidekiq_username.present? && sidekiq_password.present?
|
29
|
+
ActiveSupport::SecurityUtils.secure_compare(username, sidekiq_username) &
|
30
|
+
ActiveSupport::SecurityUtils.secure_compare(password, sidekiq_password)
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "puma/plugin"
|
2
|
+
|
3
|
+
Puma::Plugin.create do
|
4
|
+
def start(launcher)
|
5
|
+
return unless defined?(Rails) && defined?(Launchy)
|
6
|
+
return unless Rails.env.development?
|
7
|
+
|
8
|
+
binding = launcher.options[:binds].grep(/^tcp|ssl/).first
|
9
|
+
return if binding.nil?
|
10
|
+
|
11
|
+
url = binding.sub(/^tcp/, "http").sub(/^ssl/, "https").sub("0.0.0.0", "localhost")
|
12
|
+
Launchy.open(url)
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
return unless Rails.env.development?
|
2
|
+
|
3
|
+
require "annotate"
|
4
|
+
task :set_annotation_options do # rubocop:disable Rake/Desc, Metrics/BlockLength
|
5
|
+
# You can override any of these by setting an environment variable of the same name.
|
6
|
+
Annotate.set_defaults(
|
7
|
+
"routes" => "false",
|
8
|
+
"models" => "true",
|
9
|
+
"position_in_routes" => "before",
|
10
|
+
"position_in_class" => "before",
|
11
|
+
"position_in_test" => "before",
|
12
|
+
"position_in_fixture" => "before",
|
13
|
+
"position_in_factory" => "before",
|
14
|
+
"position_in_serializer" => "before",
|
15
|
+
"show_foreign_keys" => "true",
|
16
|
+
"show_complete_foreign_keys" => "false",
|
17
|
+
"show_indexes" => "true",
|
18
|
+
"simple_indexes" => "false",
|
19
|
+
"model_dir" => "app/models",
|
20
|
+
"root_dir" => "",
|
21
|
+
"include_version" => "false",
|
22
|
+
"require" => "",
|
23
|
+
"exclude_tests" => "true",
|
24
|
+
"exclude_fixtures" => "false",
|
25
|
+
"exclude_factories" => "false",
|
26
|
+
"exclude_serializers" => "false",
|
27
|
+
"exclude_scaffolds" => "true",
|
28
|
+
"exclude_controllers" => "true",
|
29
|
+
"exclude_helpers" => "true",
|
30
|
+
"exclude_sti_subclasses" => "false",
|
31
|
+
"ignore_model_sub_dir" => "false",
|
32
|
+
"ignore_columns" => nil,
|
33
|
+
"ignore_routes" => nil,
|
34
|
+
"ignore_unknown_models" => "false",
|
35
|
+
"hide_limit_column_types" => "integer,bigint,boolean",
|
36
|
+
"hide_default_column_types" => "json,jsonb,hstore",
|
37
|
+
"skip_on_db_migrate" => "false",
|
38
|
+
"format_bare" => "true",
|
39
|
+
"format_rdoc" => "false",
|
40
|
+
"format_markdown" => "false",
|
41
|
+
"sort" => "false",
|
42
|
+
"force" => "false",
|
43
|
+
"frozen" => "false",
|
44
|
+
"classified_sort" => "true",
|
45
|
+
"trace" => "false",
|
46
|
+
"wrapper_open" => nil,
|
47
|
+
"wrapper_close" => nil,
|
48
|
+
"with_comment" => "true"
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
Annotate.load_tasks
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module ViteInlineSvgFileLoader
|
2
|
+
class << self
|
3
|
+
def named(filename)
|
4
|
+
vite = ViteRuby.instance
|
5
|
+
vite_asset_path = vite.manifest.path_for(filename)
|
6
|
+
|
7
|
+
if vite.dev_server_running?
|
8
|
+
fetch_from_dev_server(vite_asset_path)
|
9
|
+
else
|
10
|
+
Rails.public_path.join(vite_asset_path.sub(%r{^/}, "")).read
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def fetch_from_dev_server(path)
|
17
|
+
config = ViteRuby.config
|
18
|
+
dev_server_uri = URI("#{config.protocol}://#{config.host_with_port}#{path}")
|
19
|
+
response = Net::HTTP.get_response(dev_server_uri)
|
20
|
+
raise "Failed to load inline SVG from #{dev_server_uri}" unless response.is_a?(Net::HTTPSuccess)
|
21
|
+
|
22
|
+
response.body
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "capybara/rails"
|
2
|
+
require "capybara/rspec"
|
3
|
+
|
4
|
+
Capybara.default_max_wait_time = 2
|
5
|
+
Capybara.disable_animation = true
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
config.before(:each, type: :system) do
|
9
|
+
driven_by :selenium,
|
10
|
+
using: (ENV["SHOW_BROWSER"] ? :chrome : :headless_chrome),
|
11
|
+
screen_size: [1400, 1400] do |options|
|
12
|
+
# Allows running in Docker
|
13
|
+
options.add_argument("--disable-dev-shm-usage")
|
14
|
+
options.add_argument("--no-sandbox")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "capybara/rails"
|
3
|
+
|
4
|
+
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
|
5
|
+
driven_by :selenium,
|
6
|
+
using: (ENV["SHOW_BROWSER"] ? :chrome : :headless_chrome),
|
7
|
+
screen_size: [1400, 1400] do |options|
|
8
|
+
# Allows running in Docker
|
9
|
+
options.add_argument("--disable-dev-shm-usage")
|
10
|
+
options.add_argument("--no-sandbox")
|
11
|
+
end
|
12
|
+
|
13
|
+
setup do
|
14
|
+
Capybara.default_max_wait_time = 2
|
15
|
+
Capybara.disable_animation = true
|
16
|
+
Capybara.server = :puma, {Silent: true}
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class InlineSvgHelperTest < ActionView::TestCase
|
4
|
+
test "inline_svg_tag returns contents of svg file as html_safe string" do
|
5
|
+
svg_result = inline_svg_tag("images/example.svg")
|
6
|
+
assert_predicate(svg_result, :html_safe?)
|
7
|
+
assert_equal(<<~EXPECTED_SVG.strip, svg_result)
|
8
|
+
<svg role="img" width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
|
9
|
+
<rect x="2" y="2" width="176" height="176" rx="6" fill="white" stroke="#CCCCCC" stroke-width="4" stroke-miterlimit="0" stroke-linecap="round"/>
|
10
|
+
</svg>
|
11
|
+
EXPECTED_SVG
|
12
|
+
end
|
13
|
+
|
14
|
+
test "inline_svg_tag adds a <title> to the svg if specified" do
|
15
|
+
svg_result = inline_svg_tag("images/example.svg", title: "rounded box")
|
16
|
+
assert_equal(<<~EXPECTED_SVG.strip, svg_result)
|
17
|
+
<svg role="img" width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
|
18
|
+
<title>rounded box</title>
|
19
|
+
<rect x="2" y="2" width="176" height="176" rx="6" fill="white" stroke="#CCCCCC" stroke-width="4" stroke-miterlimit="0" stroke-linecap="round"/>
|
20
|
+
</svg>
|
21
|
+
EXPECTED_SVG
|
22
|
+
end
|
23
|
+
end
|