Skip to content

Commit f823f17

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 8628dcb commit f823f17

File tree

7 files changed

+156
-4
lines changed

7 files changed

+156
-4
lines changed

NEWS.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Unreleased
99
* Introduce `suspenders:jobs` generator
1010
* Introduce `suspenders:lint` generator
1111
* Introduce `suspenders:rake` generator
12+
* Introduce `suspenders:install:web` generator
1213

1314
20230113.0 (January, 13, 2023)
1415

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,40 @@
1+
require "generators/suspenders/accessibility_generator"
2+
require "generators/suspenders/styles_generator"
3+
require "generators/suspenders/advisories_generator"
4+
require "generators/suspenders/inline_svg_generator"
5+
require "generators/suspenders/factories_generator"
6+
require "generators/suspenders/jobs_generator"
7+
require "generators/suspenders/lint_generator"
8+
require "generators/suspenders/rake_generator"
9+
10+
module Suspenders
11+
module Generators
12+
module Install
13+
class WebGenerator < Rails::Generators::Base
14+
include Suspenders::Generators::APIAppUnsupported
15+
16+
class_option :css, enum: Generators::CSS_OPTIONS
17+
18+
def invoke_generators
19+
Suspenders::Generators::AccessibilityGenerator.new.invoke_all
20+
Suspenders::Generators::StylesGenerator.new([], css: css).invoke_all
21+
Suspenders::Generators::AdvisoriesGenerator.new.invoke_all
22+
Suspenders::Generators::InlineSvgGenerator.new.invoke_all
23+
Suspenders::Generators::FactoriesGenerator.new.invoke_all
24+
Suspenders::Generators::JobsGenerator.new.invoke_all
25+
Suspenders::Generators::RakeGenerator.new.invoke_all
26+
27+
# Needs to be invoked last, since it fixes any liting violations
28+
# caused by the previous generators.
29+
Suspenders::Generators::LintGenerator.new.invoke_all
30+
end
31+
32+
private
33+
34+
def css
35+
@css ||= options["css"]
36+
end
37+
end
38+
end
39+
end
40+
end

lib/generators/suspenders/styles_generator.rb

+1-3
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ module Generators
33
class StylesGenerator < Rails::Generators::Base
44
include Suspenders::Generators::APIAppUnsupported
55

6-
CSS_OPTIONS = %w[tailwind postcss].freeze
7-
8-
class_option :css, enum: CSS_OPTIONS, default: "postcss"
6+
class_option :css, enum: Generators::CSS_OPTIONS, default: "postcss"
97
desc <<~TEXT
108
Configures applications to use PostCSS or Tailwind via cssbundling-rails.
119
Defaults to PostCSS with modern-normalize, with the option to override via

lib/install/web.rb

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
css = ask("What CSS framwork do you want to use? [postcss, tailwind]")
2+
3+
raise ArgumentError if ["postcss","tailwind"].exclude? css
4+
5+
after_bundle do
6+
gem_group :development, :test do
7+
gem "suspenders", github: "thoughtbot/suspenders", branch: "suspenders-3-0-0-web-generator"
8+
end
9+
10+
run "bundle install"
11+
12+
generate "suspenders:install:web --css=#{css}"
13+
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,87 @@
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+
accessibility_generator_mock = mock("accessibility_generator")
25+
Suspenders::Generators::AccessibilityGenerator.stubs(:new).returns(accessibility_generator_mock)
26+
27+
styles_generator_mock = mock("styles_generator")
28+
Suspenders::Generators::StylesGenerator.expects(:new).with([], css: "tailwind").returns(styles_generator_mock)
29+
30+
advisories_generator_mock = mock("advisories_generator")
31+
Suspenders::Generators::AdvisoriesGenerator.stubs(:new).returns(advisories_generator_mock)
32+
33+
inline_svg_generator_mock = mock("inline_svg_generator")
34+
Suspenders::Generators::InlineSvgGenerator.stubs(:new).returns(inline_svg_generator_mock)
35+
36+
facories_generator_mock = mock("facories_generator")
37+
Suspenders::Generators::FactoriesGenerator.stubs(:new).returns(facories_generator_mock)
38+
39+
jobs_generator_mock = mock("jobs_generator")
40+
Suspenders::Generators::JobsGenerator.stubs(:new).returns(jobs_generator_mock)
41+
42+
lint_generator_mock = mock("lint_generator")
43+
Suspenders::Generators::LintGenerator.stubs(:new).returns(lint_generator_mock)
44+
45+
rake_generator_mock = mock("rake_generator")
46+
Suspenders::Generators::RakeGenerator.stubs(:new).returns(rake_generator_mock)
47+
48+
accessibility_generator_mock.expects(:invoke_all).once
49+
styles_generator_mock.expects(:invoke_all).once
50+
advisories_generator_mock.expects(:invoke_all).once
51+
inline_svg_generator_mock.expects(:invoke_all).once
52+
facories_generator_mock.expects(:invoke_all).once
53+
jobs_generator_mock.expects(:invoke_all).once
54+
lint_generator_mock.expects(:invoke_all).once
55+
rake_generator_mock.expects(:invoke_all).once
56+
57+
generator_class.new([], css: "tailwind").invoke_generators
58+
end
59+
60+
test "requires a css option" do
61+
option = generator_class.class_options[:css]
62+
63+
assert_equal :string, option.type
64+
assert_not option.required
65+
assert_equal %w[tailwind postcss], option.enum
66+
assert_nil option.default
67+
end
68+
69+
test "raises if css option is unsupported" do
70+
output = capture(:stderr) { run_generator %w[--css=unknown] }
71+
72+
assert_match(/Expected '--css' to be one of/, output)
73+
end
74+
75+
private
76+
77+
def prepare_destination
78+
touch "Gemfile"
79+
end
80+
81+
def restore_destination
82+
remove_file_if_exists "Gemfile"
83+
end
84+
end
85+
end
86+
end
87+
end

0 commit comments

Comments
 (0)