Skip to content

Commit b406b7e

Browse files
hahmeddhh
andcommitted
rails new --minimal gives you a minimal rails stack.
Your app will not include any of the following: - action_cable - active_storage - action_text - action_mailer - action_mailbox - bootsnap - javascript - jbuilder - rack mini profiler - spring - system tests - turbolinks - webconsole - webpack Co-authored-by: dhh <david@loudthinking.com>
1 parent 3b953da commit b406b7e

File tree

7 files changed

+139
-10
lines changed

7 files changed

+139
-10
lines changed

railties/CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
1+
* Create a new rails app using a minimal stack.
2+
3+
`rails new cool_app --minimal`
4+
5+
All the following are excluded from your minimal stack:
6+
7+
- action_cable
8+
- action_mailbox
9+
- action_mailer
10+
- action_text
11+
- active_job
12+
- active_storage
13+
- bootsnap
14+
- jbuilder
15+
- spring
16+
- system_tests
17+
- turbolinks
18+
- webpack
19+
20+
*Haroon Ahmed*, *DHH*
21+
122
* Add default ENV variable option with BACKTRACE to turn off backtrace cleaning when debugging framework code in the
223
generated config/initializers/backtrace_silencers.rb.
324

railties/lib/rails/generators/app_base.rb

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ def self.add_shared_options_for(name)
5252
class_option :skip_active_record, type: :boolean, aliases: "-O", default: false,
5353
desc: "Skip Active Record files"
5454

55+
class_option :skip_active_job, type: :boolean, default: false,
56+
desc: "Skip Active Job"
57+
5558
class_option :skip_active_storage, type: :boolean, default: false,
5659
desc: "Skip Active Storage files"
5760

@@ -76,6 +79,9 @@ def self.add_shared_options_for(name)
7679
class_option :skip_turbolinks, type: :boolean, default: false,
7780
desc: "Skip turbolinks gem"
7881

82+
class_option :skip_jbuilder, type: :boolean, default: false,
83+
desc: "Skip jbuilder gem"
84+
7985
class_option :skip_test, type: :boolean, aliases: "-T", default: false,
8086
desc: "Skip test files"
8187

@@ -204,7 +210,8 @@ def include_all_railties? # :doc:
204210
:skip_action_mailer,
205211
:skip_test,
206212
:skip_sprockets,
207-
:skip_action_cable
213+
:skip_action_cable,
214+
:skip_active_job
208215
),
209216
skip_active_storage?,
210217
skip_action_mailbox?,
@@ -245,6 +252,10 @@ def skip_action_text? # :doc:
245252
options[:skip_action_text] || skip_active_storage?
246253
end
247254

255+
def skip_dev_gems? # :doc:
256+
options[:skip_dev_gems]
257+
end
258+
248259
class GemfileEntry < Struct.new(:name, :version, :comment, :options, :commented_out)
249260
def initialize(name, version, comment, options = {}, commented_out = false)
250261
super
@@ -327,6 +338,7 @@ def webpacker_gemfile_entry
327338
end
328339

329340
def jbuilder_gemfile_entry
341+
return [] if options[:skip_jbuilder]
330342
comment = "Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder"
331343
GemfileEntry.new "jbuilder", "~> 2.7", comment, {}, options[:api]
332344
end

railties/lib/rails/generators/rails/app/app_generator.rb

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,9 @@ class AppGenerator < AppBase
272272
class_option :api, type: :boolean,
273273
desc: "Preconfigure smaller stack for API only apps"
274274

275+
class_option :minimal, type: :boolean,
276+
desc: "Preconfigure a minimal rails app"
277+
275278
class_option :skip_bundle, type: :boolean, aliases: "-B", default: false,
276279
desc: "Don't run bundle install"
277280

@@ -294,6 +297,29 @@ def initialize(*args)
294297
self.options = options.merge(skip_sprockets: true, skip_javascript: true).freeze
295298
end
296299

300+
if options[:minimal]
301+
self.options = options.merge(
302+
skip_action_cable: true,
303+
skip_action_mailer: true,
304+
skip_action_mailbox: true,
305+
skip_action_text: true,
306+
skip_active_job: true,
307+
skip_active_storage: true,
308+
skip_bootsnap: true,
309+
skip_dev_gems: true,
310+
skip_javascript: true,
311+
skip_jbuilder: true,
312+
skip_spring: true,
313+
skip_system_test: true,
314+
skip_webpack_install: true,
315+
skip_turbolinks: true).tap do |option|
316+
if option[:webpack]
317+
option[:skip_webpack_install] = false
318+
option[:skip_javascript] = false
319+
end
320+
end.freeze
321+
end
322+
297323
@after_bundle_callbacks = []
298324
end
299325

@@ -446,11 +472,18 @@ def delete_public_files_if_api_option
446472
end
447473

448474
def delete_js_folder_skipping_javascript
449-
if options[:skip_javascript]
475+
if options[:skip_javascript] && !options[:minimal]
450476
remove_dir "app/javascript"
451477
end
452478
end
453479

480+
def delete_js_packs_when_minimal_skipping_webpack
481+
if options[:minimal] && options[:skip_webpack_install]
482+
remove_dir "app/javascript/packs"
483+
keep_file "app/javascript"
484+
end
485+
end
486+
454487
def delete_assets_initializer_skipping_sprockets
455488
if options[:skip_sprockets]
456489
remove_file "config/initializers/assets.rb"
@@ -508,6 +541,12 @@ def finish_template
508541
build(:leftovers)
509542
end
510543

544+
def delete_active_job_folder_if_skipping_active_job
545+
if options[:skip_active_job]
546+
remove_dir "app/jobs"
547+
end
548+
end
549+
511550
public_task :apply_rails_template, :run_bundle
512551
public_task :generate_bundler_binstub, :generate_spring_binstub
513552
public_task :run_webpack

railties/lib/rails/generators/rails/app/templates/Gemfile.tt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ end
4444

4545
<% end -%>
4646
group :development do
47-
<%- unless options.api? -%>
47+
<%- unless options.api? || options.skip_dev_gems? -%>
4848
# Access an interactive console on exception pages or by calling 'console' anywhere in the code.
4949
<%- if options.dev? || options.edge? || options.master? -%>
5050
gem 'web-console', github: 'rails/web-console'

railties/lib/rails/generators/rails/app/templates/config/application.rb.tt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ require "rails/all"
66
require "rails"
77
# Pick the frameworks you want:
88
require "active_model/railtie"
9-
require "active_job/railtie"
9+
<%= comment_if :skip_active_job %>require "active_job/railtie"
1010
<%= comment_if :skip_active_record %>require "active_record/railtie"
1111
<%= comment_if :skip_active_storage %>require "active_storage/engine"
1212
require "action_controller/railtie"

railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,11 @@ Rails.application.configure do
6666
# Use a different cache store in production.
6767
# config.cache_store = :mem_cache_store
6868

69+
<%- unless options[:skip_active_job] -%>
6970
# Use a real queuing backend for Active Job (and separate queues per environment).
7071
# config.active_job.queue_adapter = :resque
7172
# config.active_job.queue_name_prefix = "<%= app_name %>_production"
73+
<%- end -%>
7274

7375
<%- unless options.skip_action_mailer? -%>
7476
config.action_mailer.perform_caching = false

railties/test/generators/app_generator_test.rb

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,22 @@ def test_generator_if_skip_test_is_given
618618
assert_no_directory("test")
619619
end
620620

621+
def test_generator_if_skip_jbuilder_is_given
622+
run_generator [destination_root, "--skip-jbuilder"]
623+
assert_no_gem "jbuilder"
624+
end
625+
626+
def test_generator_if_skip_active_job_is_given
627+
run_generator [destination_root, "--skip-active-job"]
628+
assert_no_file "app/jobs/application.rb"
629+
assert_file "config/environments/production.rb" do |content|
630+
assert_no_match(/config\.active_job/, content)
631+
end
632+
assert_file "config/application.rb" do |content|
633+
assert_match(/#\s+require\s+["']active_job\/railtie["']/, content)
634+
end
635+
end
636+
621637
def test_generator_if_skip_system_test_is_given
622638
run_generator [destination_root, "--skip-system-test"]
623639
assert_no_gem "capybara"
@@ -829,7 +845,6 @@ def test_edge_option
829845
assert_file "Gemfile", %r{^gem\s+["']rails["'],\s+github:\s+["']#{Regexp.escape("rails/rails")}["']$}
830846
end
831847

832-
833848
def test_master_option
834849
generator([destination_root], master: true, skip_webpack_install: true)
835850

@@ -1110,6 +1125,46 @@ def test_master_key_is_only_readable_by_the_owner
11101125
end
11111126
end
11121127

1128+
def test_minimal_rails_app
1129+
app_root = File.join(destination_root, "myapp")
1130+
run_generator [app_root, "--minimal"]
1131+
1132+
assert_no_file "#{app_root}/config/storage.yml"
1133+
assert_no_file "#{app_root}/config/webpacker.yml"
1134+
assert_no_file "#{app_root}/config/cable.yml"
1135+
assert_no_file "#{app_root}/bin/yarn"
1136+
assert_no_file "#{app_root}/views/layouts/mailer.html.erb"
1137+
assert_no_file "#{app_root}/config/spring.rb"
1138+
assert_no_file "#{app_root}/app/jobs/application.rb"
1139+
assert_file "#{app_root}/app/views/layouts/application.html.erb" do |content|
1140+
assert_no_match(/data-turbolinks-track/, content)
1141+
end
1142+
assert_file "#{app_root}/config/environments/development.rb" do |content|
1143+
assert_no_match(/config\.active_storage/, content)
1144+
end
1145+
assert_file "#{app_root}/config/environments/production.rb" do |content|
1146+
assert_no_match(/config\.active_job/, content)
1147+
end
1148+
assert_file "#{app_root}/config/boot.rb" do |content|
1149+
assert_no_match(/require "bootsnap\/setup"/, content)
1150+
end
1151+
assert_file "#{app_root}/config/application.rb" do |content|
1152+
assert_match(/#\s+require\s+["']active_job\/railtie["']/, content)
1153+
assert_match(/#\s+require\s+["']active_storage\/engine["']/, content)
1154+
assert_match(/#\s+require\s+["']action_mailer\/railtie["']/, content)
1155+
assert_match(/#\s+require\s+["']action_mailbox\/engine["']/, content)
1156+
assert_match(/#\s+require\s+["']action_text\/engine["']/, content)
1157+
assert_match(/#\s+require\s+["']action_cable\/engine["']/, content)
1158+
assert_match(/\s+require\s+["']sprockets\/railtie["']/, content)
1159+
end
1160+
1161+
assert_no_gem "webpacker", app_root
1162+
assert_no_gem "jbuilder", app_root
1163+
assert_no_gem "rack-mini-profiler", app_root
1164+
assert_no_gem "spring", app_root
1165+
assert_no_gem "web-console", app_root
1166+
end
1167+
11131168
private
11141169
def stub_rails_application(root)
11151170
Rails.application.config.root = root
@@ -1122,16 +1177,16 @@ def action(*args, &block)
11221177
capture(:stdout) { generator.send(*args, &block) }
11231178
end
11241179

1125-
def assert_gem(gem, constraint = nil)
1180+
def assert_gem(gem, constraint = nil, app_path = ".")
11261181
if constraint
1127-
assert_file "Gemfile", /^\s*gem\s+["']#{gem}["'], #{constraint}$*/
1182+
assert_file File.join(app_path, "Gemfile"), /^\s*gem\s+["']#{gem}["'], #{constraint}$*/
11281183
else
1129-
assert_file "Gemfile", /^\s*gem\s+["']#{gem}["']$*/
1184+
assert_file File.join(app_path, "Gemfile"), /^\s*gem\s+["']#{gem}["']$*/
11301185
end
11311186
end
11321187

1133-
def assert_no_gem(gem)
1134-
assert_file "Gemfile" do |content|
1188+
def assert_no_gem(gem, app_path = ".")
1189+
assert_file File.join(app_path, "Gemfile") do |content|
11351190
assert_no_match(gem, content)
11361191
end
11371192
end

0 commit comments

Comments
 (0)