From a6e3706d19597dbb58ab302f74d6bb29894aa7c0 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 | 49 ++++++++++ lib/install/web.rb | 9 ++ lib/suspenders/generators.rb | 3 + .../suspenders/install/web_generator_test.rb | 96 +++++++++++++++++++ 6 files changed, 169 insertions(+), 1 deletion(-) 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 7831295c6..64101d8f9 100644 --- a/NEWS.md +++ b/NEWS.md @@ -16,6 +16,7 @@ Unreleased * Introduce `suspenders:email` generator * Introduce `suspenders:testing` generator * Introduce `suspenders:prerequisites` generator +* Introduce `suspenders:install:web` generator 20230113.0 (January, 13, 2023) diff --git a/README.md b/README.md index 4876d7085..6d71793d6 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..381525549 --- /dev/null +++ b/lib/generators/suspenders/install/web_generator.rb @@ -0,0 +1,49 @@ +require "generators/suspenders/prerequisites_generator" +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" +require "generators/suspenders/views_generator" +require "generators/suspenders/setup_generator" +require "generators/suspenders/tasks_generator" +require "generators/suspenders/email_generator" +require "generators/suspenders/testing_generator" + +module Suspenders + module Generators + module Install + class WebGenerator < Rails::Generators::Base + include Suspenders::Generators::APIAppUnsupported + + def invoke_generators + # This needs to go first, since it configures `.node-version` + Suspenders::Generators::PrerequisitesGenerator.new.invoke_all + + Suspenders::Generators::AccessibilityGenerator.new.invoke_all + Suspenders::Generators::StylesGenerator.new.invoke_all + Suspenders::Generators::AdvisoriesGenerator.new.invoke_all + Suspenders::Generators::InlineSvgGenerator.new.invoke_all + Suspenders::Generators::FactoriesGenerator.new.invoke_all + + # Needs to be invoked before StylesGenerator, since that generator + # creates Procfile.dev + Suspenders::Generators::JobsGenerator.new.invoke_all + Suspenders::Generators::RakeGenerator.new.invoke_all + Suspenders::Generators::ViewsGenerator.new.invoke_all + Suspenders::Generators::SetupGenerator.new.invoke_all + Suspenders::Generators::TasksGenerator.new.invoke_all + Suspenders::Generators::EmailGenerator.new.invoke_all + Suspenders::Generators::TestingGenerator.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 + end + end + end +end diff --git a/lib/install/web.rb b/lib/install/web.rb new file mode 100644 index 000000000..07d014e2d --- /dev/null +++ b/lib/install/web.rb @@ -0,0 +1,9 @@ +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" +end diff --git a/lib/suspenders/generators.rb b/lib/suspenders/generators.rb index cbefec2bf..d8580572a 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..8e812dfbd --- /dev/null +++ b/test/generators/suspenders/install/web_generator_test.rb @@ -0,0 +1,96 @@ +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 + prerequisites_generator_mock = mock("prerequisites_generator") + Suspenders::Generators::PrerequisitesGenerator.stubs(:new).returns(prerequisites_generator_mock) + + accessibility_generator_mock = mock("accessibility_generator") + Suspenders::Generators::AccessibilityGenerator.stubs(:new).returns(accessibility_generator_mock) + + styles_generator_mock = mock("styles_generator") + Suspenders::Generators::StylesGenerator.stubs(:new).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) + + views_generator_mock = mock("views_generator") + Suspenders::Generators::ViewsGenerator.stubs(:new).returns(views_generator_mock) + + setup_generator_mock = mock("setup_generator") + Suspenders::Generators::SetupGenerator.stubs(:new).returns(setup_generator_mock) + + tasks_generator_mock = mock("tasks_generator") + Suspenders::Generators::TasksGenerator.stubs(:new).returns(tasks_generator_mock) + + email_generator_mock = mock("email_generator") + Suspenders::Generators::EmailGenerator.stubs(:new).returns(email_generator_mock) + + testing_generator_mock = mock("testing_generator") + Suspenders::Generators::TestingGenerator.stubs(:new).returns(testing_generator_mock) + + prerequisites_generator_mock.expects(:invoke_all).once + 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 + views_generator_mock.expects(:invoke_all).once + setup_generator_mock.expects(:invoke_all).once + tasks_generator_mock.expects(:invoke_all).once + email_generator_mock.expects(:invoke_all).once + testing_generator_mock.expects(:invoke_all).once + + generator_class.new([], css: "tailwind").invoke_generators + end + + private + + def prepare_destination + touch "Gemfile" + end + + def restore_destination + remove_file_if_exists "Gemfile" + end + end + end + end +end