From 81a5cee25c37a1ffe251a95e4b63ac7e84c41155 Mon Sep 17 00:00:00 2001 From: Steve Polito Date: Tue, 12 Dec 2023 13:05:12 -0500 Subject: [PATCH] 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 --- NEWS.md | 1 + README.md | 12 ++- .../suspenders/install/web_generator.rb | 40 +++++++++ lib/generators/suspenders/styles_generator.rb | 4 +- lib/install/web.rb | 13 +++ lib/suspenders/generators.rb | 3 + .../suspenders/install/web_generator_test.rb | 87 +++++++++++++++++++ 7 files changed, 156 insertions(+), 4 deletions(-) create mode 100644 lib/generators/suspenders/install/web_generator.rb create mode 100644 lib/install/web.rb create mode 100644 test/generators/suspenders/install/web_generator_test.rb diff --git a/NEWS.md b/NEWS.md index 31563045d..08391d018 100644 --- a/NEWS.md +++ b/NEWS.md @@ -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) diff --git a/README.md b/README.md index f3cffb81c..d6c5dfc23 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ if you like missing deadlines. ## Usage +### Existing Rails Applications + ``` group :development, :test do gem "suspenders" @@ -16,7 +18,15 @@ 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 diff --git a/lib/generators/suspenders/install/web_generator.rb b/lib/generators/suspenders/install/web_generator.rb new file mode 100644 index 000000000..66d4a93c9 --- /dev/null +++ b/lib/generators/suspenders/install/web_generator.rb @@ -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 diff --git a/lib/generators/suspenders/styles_generator.rb b/lib/generators/suspenders/styles_generator.rb index a83b686ff..2a978fa18 100644 --- a/lib/generators/suspenders/styles_generator.rb +++ b/lib/generators/suspenders/styles_generator.rb @@ -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 diff --git a/lib/install/web.rb b/lib/install/web.rb new file mode 100644 index 000000000..428410826 --- /dev/null +++ b/lib/install/web.rb @@ -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 diff --git a/lib/suspenders/generators.rb b/lib/suspenders/generators.rb index 553231c5a..7e96f48c7 100644 --- a/lib/suspenders/generators.rb +++ b/lib/suspenders/generators.rb @@ -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") diff --git a/test/generators/suspenders/install/web_generator_test.rb b/test/generators/suspenders/install/web_generator_test.rb new file mode 100644 index 000000000..f8ea75b10 --- /dev/null +++ b/test/generators/suspenders/install/web_generator_test.rb @@ -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