Skip to content

Commit 9212177

Browse files
WIP: Introduce suspenders:install:web generator and application template
Create generator to invoke all necessary generators. We add it to the `install` namespace to provide flexibility should we add other installation options, such as ones for API Only applications. We manually invoke generators rather than using `generate "suspenders:generator"`. This is because in those cases, the generator is actually run, which slows down the test suite dramatically. More importantly, this allows us to use [Mocha][] to mock generators in an effort to improve testing. By mocking the generators, we ensure they're not actually invoked, which would result in a slow test. Also, it would mean we would need to build up extensive `prepare_destination` and `restore_destination` method declarations. [Mocha]: https://github.com/freerange/mocha
1 parent 5f90074 commit 9212177

File tree

6 files changed

+169
-1
lines changed

6 files changed

+169
-1
lines changed

NEWS.md

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Unreleased
1717
* Introduce `suspenders:testing` generator
1818
* Introduce `suspenders:prerequisites` generator
1919
* Introduce `suspenders:ci` generator
20+
* Introduce `suspenders:install:web` generator
2021

2122
20230113.0 (January, 13, 2023)
2223

README.md

+11-1
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,24 @@ if you like missing deadlines.
99

1010
## Usage
1111

12+
### Existing Rails Applications
13+
1214
```
1315
group :development, :test do
1416
gem "suspenders"
1517
end
1618
```
1719

1820
```
19-
bin/rails g suspenders:all
21+
bin/rails g suspenders:install:web
22+
```
23+
24+
### New Rails Applications
25+
26+
```
27+
rails new my_app \
28+
-d=postgresql \
29+
-m=https://raw.githubusercontent.com/thoughtbot/suspenders/lib/install/web.rb
2030
```
2131

2232
## Generators
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
require "generators/suspenders/prerequisites_generator"
2+
require "generators/suspenders/accessibility_generator"
3+
require "generators/suspenders/styles_generator"
4+
require "generators/suspenders/advisories_generator"
5+
require "generators/suspenders/inline_svg_generator"
6+
require "generators/suspenders/factories_generator"
7+
require "generators/suspenders/jobs_generator"
8+
require "generators/suspenders/lint_generator"
9+
require "generators/suspenders/rake_generator"
10+
require "generators/suspenders/views_generator"
11+
require "generators/suspenders/setup_generator"
12+
require "generators/suspenders/tasks_generator"
13+
require "generators/suspenders/email_generator"
14+
require "generators/suspenders/testing_generator"
15+
16+
module Suspenders
17+
module Generators
18+
module Install
19+
class WebGenerator < Rails::Generators::Base
20+
include Suspenders::Generators::APIAppUnsupported
21+
22+
def invoke_generators
23+
# This needs to go first, since it configures `.node-version`
24+
Suspenders::Generators::PrerequisitesGenerator.new.invoke_all
25+
26+
Suspenders::Generators::AccessibilityGenerator.new.invoke_all
27+
Suspenders::Generators::StylesGenerator.new.invoke_all
28+
Suspenders::Generators::AdvisoriesGenerator.new.invoke_all
29+
Suspenders::Generators::InlineSvgGenerator.new.invoke_all
30+
Suspenders::Generators::FactoriesGenerator.new.invoke_all
31+
32+
# Needs to be invoked before StylesGenerator, since that generator
33+
# creates Procfile.dev
34+
Suspenders::Generators::JobsGenerator.new.invoke_all
35+
Suspenders::Generators::RakeGenerator.new.invoke_all
36+
Suspenders::Generators::ViewsGenerator.new.invoke_all
37+
Suspenders::Generators::SetupGenerator.new.invoke_all
38+
Suspenders::Generators::TasksGenerator.new.invoke_all
39+
Suspenders::Generators::EmailGenerator.new.invoke_all
40+
Suspenders::Generators::TestingGenerator.new.invoke_all
41+
42+
# Needs to be invoked last, since it fixes any liting violations
43+
# caused by the previous generators.
44+
Suspenders::Generators::LintGenerator.new.invoke_all
45+
end
46+
end
47+
end
48+
end
49+
end

lib/install/web.rb

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
after_bundle do
2+
gem_group :development, :test do
3+
gem "suspenders", github: "thoughtbot/suspenders", branch: "suspenders-3-0-0-web-generator"
4+
end
5+
6+
run "bundle install"
7+
8+
generate "suspenders:install:web"
9+
end

lib/suspenders/generators.rb

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
module Suspenders
44
module Generators
5+
6+
CSS_OPTIONS = %w[tailwind postcss].freeze
7+
58
module Helpers
69
def default_test_suite?
710
File.exist? Rails.root.join("test")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
require "test_helper"
2+
require "generators/suspenders/install/web_generator"
3+
4+
module Suspenders
5+
module Generators
6+
module Install
7+
class WebGeneratorTest < Rails::Generators::TestCase
8+
include Suspenders::TestHelpers
9+
10+
tests Suspenders::Generators::Install::WebGenerator
11+
destination Rails.root
12+
setup :prepare_destination
13+
teardown :restore_destination
14+
15+
test "raises if API only application" do
16+
within_api_only_app do
17+
assert_raises Suspenders::Generators::APIAppUnsupported::Error do
18+
run_generator
19+
end
20+
end
21+
end
22+
23+
test "invokes generators" do
24+
prerequisites_generator_mock = mock("prerequisites_generator")
25+
Suspenders::Generators::PrerequisitesGenerator.stubs(:new).returns(prerequisites_generator_mock)
26+
27+
accessibility_generator_mock = mock("accessibility_generator")
28+
Suspenders::Generators::AccessibilityGenerator.stubs(:new).returns(accessibility_generator_mock)
29+
30+
styles_generator_mock = mock("styles_generator")
31+
Suspenders::Generators::StylesGenerator.stubs(:new).returns(styles_generator_mock)
32+
33+
advisories_generator_mock = mock("advisories_generator")
34+
Suspenders::Generators::AdvisoriesGenerator.stubs(:new).returns(advisories_generator_mock)
35+
36+
inline_svg_generator_mock = mock("inline_svg_generator")
37+
Suspenders::Generators::InlineSvgGenerator.stubs(:new).returns(inline_svg_generator_mock)
38+
39+
facories_generator_mock = mock("facories_generator")
40+
Suspenders::Generators::FactoriesGenerator.stubs(:new).returns(facories_generator_mock)
41+
42+
jobs_generator_mock = mock("jobs_generator")
43+
Suspenders::Generators::JobsGenerator.stubs(:new).returns(jobs_generator_mock)
44+
45+
lint_generator_mock = mock("lint_generator")
46+
Suspenders::Generators::LintGenerator.stubs(:new).returns(lint_generator_mock)
47+
48+
rake_generator_mock = mock("rake_generator")
49+
Suspenders::Generators::RakeGenerator.stubs(:new).returns(rake_generator_mock)
50+
51+
views_generator_mock = mock("views_generator")
52+
Suspenders::Generators::ViewsGenerator.stubs(:new).returns(views_generator_mock)
53+
54+
setup_generator_mock = mock("setup_generator")
55+
Suspenders::Generators::SetupGenerator.stubs(:new).returns(setup_generator_mock)
56+
57+
tasks_generator_mock = mock("tasks_generator")
58+
Suspenders::Generators::TasksGenerator.stubs(:new).returns(tasks_generator_mock)
59+
60+
email_generator_mock = mock("email_generator")
61+
Suspenders::Generators::EmailGenerator.stubs(:new).returns(email_generator_mock)
62+
63+
testing_generator_mock = mock("testing_generator")
64+
Suspenders::Generators::TestingGenerator.stubs(:new).returns(testing_generator_mock)
65+
66+
prerequisites_generator_mock.expects(:invoke_all).once
67+
accessibility_generator_mock.expects(:invoke_all).once
68+
styles_generator_mock.expects(:invoke_all).once
69+
advisories_generator_mock.expects(:invoke_all).once
70+
inline_svg_generator_mock.expects(:invoke_all).once
71+
facories_generator_mock.expects(:invoke_all).once
72+
jobs_generator_mock.expects(:invoke_all).once
73+
lint_generator_mock.expects(:invoke_all).once
74+
rake_generator_mock.expects(:invoke_all).once
75+
views_generator_mock.expects(:invoke_all).once
76+
setup_generator_mock.expects(:invoke_all).once
77+
tasks_generator_mock.expects(:invoke_all).once
78+
email_generator_mock.expects(:invoke_all).once
79+
testing_generator_mock.expects(:invoke_all).once
80+
81+
generator_class.new([], css: "tailwind").invoke_generators
82+
end
83+
84+
private
85+
86+
def prepare_destination
87+
touch "Gemfile"
88+
end
89+
90+
def restore_destination
91+
remove_file_if_exists "Gemfile"
92+
end
93+
end
94+
end
95+
end
96+
end

0 commit comments

Comments
 (0)