Skip to content

Commit

Permalink
WIP: Introduce suspenders:install:web generator and application tem…
Browse files Browse the repository at this point in the history
…plate

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
  • Loading branch information
stevepolitodesign committed Mar 8, 2024
1 parent 0da07ad commit 81a5cee
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 4 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Unreleased
* Introduce `suspenders:db:migrate` task
* Introduce `suspenders:email` generator
* Introduce `suspenders:testing` generator
* Introduce `suspenders:install:web` generator

20230113.0 (January, 13, 2023)

Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,24 @@ if you like missing deadlines.

## Usage

### Existing Rails Applications

```
group :development, :test do
gem "suspenders"
end
```

```
bin/rails g suspenders:all
bin/rails g suspenders:install:web
```

### New Rails Applications

```
rails new my_app \
-d=postgresql \
-m=https://raw.githubusercontent.com/thoughtbot/suspenders/lib/install/web.rb
```

## Generators
Expand Down
40 changes: 40 additions & 0 deletions lib/generators/suspenders/install/web_generator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
require "generators/suspenders/accessibility_generator"
require "generators/suspenders/styles_generator"
require "generators/suspenders/advisories_generator"
require "generators/suspenders/inline_svg_generator"
require "generators/suspenders/factories_generator"
require "generators/suspenders/jobs_generator"
require "generators/suspenders/lint_generator"
require "generators/suspenders/rake_generator"

module Suspenders
module Generators
module Install
class WebGenerator < Rails::Generators::Base
include Suspenders::Generators::APIAppUnsupported

class_option :css, enum: Generators::CSS_OPTIONS

def invoke_generators
Suspenders::Generators::AccessibilityGenerator.new.invoke_all
Suspenders::Generators::StylesGenerator.new([], css: css).invoke_all
Suspenders::Generators::AdvisoriesGenerator.new.invoke_all
Suspenders::Generators::InlineSvgGenerator.new.invoke_all
Suspenders::Generators::FactoriesGenerator.new.invoke_all
Suspenders::Generators::JobsGenerator.new.invoke_all
Suspenders::Generators::RakeGenerator.new.invoke_all

# Needs to be invoked last, since it fixes any liting violations
# caused by the previous generators.
Suspenders::Generators::LintGenerator.new.invoke_all
end

private

def css
@css ||= options["css"]
end
end
end
end
end
4 changes: 1 addition & 3 deletions lib/generators/suspenders/styles_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ module Generators
class StylesGenerator < Rails::Generators::Base
include Suspenders::Generators::APIAppUnsupported

CSS_OPTIONS = %w[tailwind postcss].freeze

class_option :css, enum: CSS_OPTIONS, default: "postcss"
class_option :css, enum: Generators::CSS_OPTIONS, default: "postcss"
desc <<~TEXT
Configures applications to use PostCSS or Tailwind via cssbundling-rails.
Defaults to PostCSS with modern-normalize, with the option to override via
Expand Down
13 changes: 13 additions & 0 deletions lib/install/web.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
css = ask("What CSS framwork do you want to use? [postcss, tailwind]")

raise ArgumentError if ["postcss","tailwind"].exclude? css

after_bundle do
gem_group :development, :test do
gem "suspenders", github: "thoughtbot/suspenders", branch: "suspenders-3-0-0-web-generator"
end

run "bundle install"

generate "suspenders:install:web --css=#{css}"
end
3 changes: 3 additions & 0 deletions lib/suspenders/generators.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

module Suspenders
module Generators

CSS_OPTIONS = %w[tailwind postcss].freeze

module Helpers
def default_test_suite?
File.exist? Rails.root.join("test")
Expand Down
87 changes: 87 additions & 0 deletions test/generators/suspenders/install/web_generator_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
require "test_helper"
require "generators/suspenders/install/web_generator"

module Suspenders
module Generators
module Install
class WebGeneratorTest < Rails::Generators::TestCase
include Suspenders::TestHelpers

tests Suspenders::Generators::Install::WebGenerator
destination Rails.root
setup :prepare_destination
teardown :restore_destination

test "raises if API only application" do
within_api_only_app do
assert_raises Suspenders::Generators::APIAppUnsupported::Error do
run_generator
end
end
end

test "invokes generators" do
accessibility_generator_mock = mock("accessibility_generator")
Suspenders::Generators::AccessibilityGenerator.stubs(:new).returns(accessibility_generator_mock)

styles_generator_mock = mock("styles_generator")
Suspenders::Generators::StylesGenerator.expects(:new).with([], css: "tailwind").returns(styles_generator_mock)

advisories_generator_mock = mock("advisories_generator")
Suspenders::Generators::AdvisoriesGenerator.stubs(:new).returns(advisories_generator_mock)

inline_svg_generator_mock = mock("inline_svg_generator")
Suspenders::Generators::InlineSvgGenerator.stubs(:new).returns(inline_svg_generator_mock)

facories_generator_mock = mock("facories_generator")
Suspenders::Generators::FactoriesGenerator.stubs(:new).returns(facories_generator_mock)

jobs_generator_mock = mock("jobs_generator")
Suspenders::Generators::JobsGenerator.stubs(:new).returns(jobs_generator_mock)

lint_generator_mock = mock("lint_generator")
Suspenders::Generators::LintGenerator.stubs(:new).returns(lint_generator_mock)

rake_generator_mock = mock("rake_generator")
Suspenders::Generators::RakeGenerator.stubs(:new).returns(rake_generator_mock)

accessibility_generator_mock.expects(:invoke_all).once
styles_generator_mock.expects(:invoke_all).once
advisories_generator_mock.expects(:invoke_all).once
inline_svg_generator_mock.expects(:invoke_all).once
facories_generator_mock.expects(:invoke_all).once
jobs_generator_mock.expects(:invoke_all).once
lint_generator_mock.expects(:invoke_all).once
rake_generator_mock.expects(:invoke_all).once

generator_class.new([], css: "tailwind").invoke_generators
end

test "requires a css option" do
option = generator_class.class_options[:css]

assert_equal :string, option.type
assert_not option.required
assert_equal %w[tailwind postcss], option.enum
assert_nil option.default
end

test "raises if css option is unsupported" do
output = capture(:stderr) { run_generator %w[--css=unknown] }

assert_match(/Expected '--css' to be one of/, output)
end

private

def prepare_destination
touch "Gemfile"
end

def restore_destination
remove_file_if_exists "Gemfile"
end
end
end
end
end

0 comments on commit 81a5cee

Please sign in to comment.