From 0099f25106a877936e3ceb6183f446a546fe4abf Mon Sep 17 00:00:00 2001 From: Alexander Senko Date: Sat, 31 Aug 2024 10:02:37 +0700 Subject: [PATCH] Generators: allow decorators not to match controller names Decorator will respect `--model-name` passed to `scaffold_controller` or `controller` generator. That could be of a particular use to solve namespace issues (e.g. when controllers are namespaced and models are not). The specs have been refactored to reduce and DRY the code. Resolves drapergem/draper#919. --- lib/generators/controller_override.rb | 11 +- .../controller/controller_generator_spec.rb | 62 ++++++++-- .../decorator/decorator_generator_spec.rb | 108 ++++++++++-------- 3 files changed, 123 insertions(+), 58 deletions(-) diff --git a/lib/generators/controller_override.rb b/lib/generators/controller_override.rb index 5e90ded5..46c21cb2 100644 --- a/lib/generators/controller_override.rb +++ b/lib/generators/controller_override.rb @@ -1,17 +1,24 @@ require "rails/generators" require "rails/generators/rails/controller/controller_generator" require "rails/generators/rails/scaffold_controller/scaffold_controller_generator" +require "rails/generators/model_helpers" module Rails module Generators class ControllerGenerator + include Rails::Generators::ModelHelpers + + class_option :model_name, type: :string, desc: "ModelName to be used" + hook_for :decorator, type: :boolean, default: true do |generator| - invoke generator, [name.singularize] + invoke generator, [options[:model_name] || name] end end class ScaffoldControllerGenerator - hook_for :decorator, type: :boolean, default: true + hook_for :decorator, type: :boolean, default: true do |generator| + invoke generator, [options[:model_name] || name] + end end end end diff --git a/spec/generators/controller/controller_generator_spec.rb b/spec/generators/controller/controller_generator_spec.rb index 07be58f2..ce99f549 100644 --- a/spec/generators/controller/controller_generator_spec.rb +++ b/spec/generators/controller/controller_generator_spec.rb @@ -5,19 +5,65 @@ require 'generators/rails/decorator_generator' SimpleCov.command_name 'test:generator' -describe Rails::Generators::ControllerGenerator do - destination File.expand_path("../tmp", __FILE__) +describe Rails::Generators do + destination Rails.root / 'tmp/generated' + + subject { file path } + + let(:controller_name) { 'YourModels' } + let(:model_name) { controller_name.singularize } + let(:decorator_name) { "#{model_name}Decorator" } + let(:options) { {} } + let(:args) { options.map { |k, v| "--#{k.to_s.dasherize}=#{v}" } } before { prepare_destination } - after(:all) { FileUtils.rm_rf destination_root } + before { run_generator [controller_name, *args] } + + shared_context :namespaceable do + let(:controller_name) { 'Namespace::YourModels' } + let(:model_name) { options[:model_name] or super() } + + include_examples :naming + + context "with the same namespace" do + let(:options) { super().merge model_name: 'Namespace::OtherModel' } + + include_examples :naming + end + + context "with another namespace" do + let(:options) { super().merge model_name: 'OtherNamespace::YourModel' } + + include_examples :naming + end + + context "without namespace" do + let(:options) { super().merge model_name: 'YourModel' } + + include_examples :naming + end + end - describe "the generated decorator" do - subject { file("app/decorators/your_model_decorator.rb") } + describe "decorator class" do + let(:path) { "app/decorators/#{decorator_name.underscore}.rb" } + + shared_examples :naming do + it 'is properly named' do + is_expected.to exist + is_expected.to contain "class #{decorator_name}" + end + end + + describe Rails::Generators::ControllerGenerator do + include_examples :naming + it_behaves_like :namespaceable + end - describe "naming" do - before { run_generator %w(YourModels) } + describe Rails::Generators::ScaffoldControllerGenerator do + let(:options) { { skip_routes: true } } - it { is_expected.to contain "class YourModelDecorator" } + include_examples :naming + it_behaves_like :namespaceable end end end diff --git a/spec/generators/decorator/decorator_generator_spec.rb b/spec/generators/decorator/decorator_generator_spec.rb index 3846dc87..1a0b78f6 100644 --- a/spec/generators/decorator/decorator_generator_spec.rb +++ b/spec/generators/decorator/decorator_generator_spec.rb @@ -4,90 +4,102 @@ require 'generators/rails/decorator_generator' describe Rails::Generators::DecoratorGenerator do - destination File.expand_path("../tmp", __FILE__) + destination Rails.root / 'tmp/generated' + + subject { file path } + + let(:model_name) { 'YourModel' } + let(:decorator_name) { "#{model_name}Decorator" } + let(:options) { {} } + let(:args) { options.map { |k, v| "--#{k.to_s.dasherize}=#{v}" } } before { prepare_destination } - after(:all) { FileUtils.rm_rf destination_root } + before { run_generator [model_name, *args] } - describe "the generated decorator" do - subject { file("app/decorators/your_model_decorator.rb") } + shared_context :namespaceable do + let(:model_name) { 'Namespace::YourModel' } - describe "naming" do - before { run_generator %w(YourModel) } + include_examples :naming + end - it { is_expected.to contain "class YourModelDecorator" } - end + describe "decorator class" do + let(:path) { "app/decorators/#{decorator_name.underscore}.rb" } - describe "namespacing" do - subject { file("app/decorators/namespace/your_model_decorator.rb") } - before { run_generator %w(Namespace::YourModel) } + it { is_expected.to have_correct_syntax } - it { is_expected.to contain "class Namespace::YourModelDecorator" } + shared_examples :naming do + it 'is properly named' do + is_expected.to exist + is_expected.to contain "class #{decorator_name}" + end end + include_examples :naming + it_behaves_like :namespaceable + describe "inheritance" do - context "by default" do - before { run_generator %w(YourModel) } + let(:parent) { 'Draper::Decorator' } - it { is_expected.to contain "class YourModelDecorator < Draper::Decorator" } + shared_examples :inheritance do + it { is_expected.to contain "class #{decorator_name} < #{parent}" } end - context "with the --parent option" do - before { run_generator %w(YourModel --parent=FooDecorator) } + include_examples :inheritance + + context "with --parent" do + let(:options) { { parent: 'FooDecorator' } } + let(:parent) { options[:parent] } - it { is_expected.to contain "class YourModelDecorator < FooDecorator" } + include_examples :inheritance end context "with an ApplicationDecorator" do - before do + let(:parent) { 'ApplicationDecorator' } + + let :options do # HACK: run before the generator allow_any_instance_of(Object).to receive(:require).and_call_original allow_any_instance_of(Object).to receive(:require).with("application_decorator").and_return( stub_const "ApplicationDecorator", Class.new ) + super() end - before { run_generator %w(YourModel) } - - it { is_expected.to contain "class YourModelDecorator < ApplicationDecorator" } + include_examples :inheritance end end end - context "with -t=rspec" do - describe "the generated spec" do - subject { file("spec/decorators/your_model_decorator_spec.rb") } + describe "spec" do + let(:options) { { test_framework: :rspec } } + let(:path) { "spec/decorators/#{decorator_name.underscore}_spec.rb" } - describe "naming" do - before { run_generator %w(YourModel -t=rspec) } + it { is_expected.to have_correct_syntax } - it { is_expected.to contain "describe YourModelDecorator" } - end - - describe "namespacing" do - subject { file("spec/decorators/namespace/your_model_decorator_spec.rb") } - before { run_generator %w(Namespace::YourModel -t=rspec) } - - it { is_expected.to contain "describe Namespace::YourModelDecorator" } + shared_examples :naming do + it 'is properly named' do + is_expected.to exist + is_expected.to contain "describe #{decorator_name}" end end - end - context "with -t=test_unit" do - describe "the generated test" do - subject { file("test/decorators/your_model_decorator_test.rb") } - - describe "naming" do - before { run_generator %w(YourModel -t=test_unit) } + include_examples :naming + it_behaves_like :namespaceable + end - it { is_expected.to contain "class YourModelDecoratorTest < Draper::TestCase" } - end + describe "test" do + let(:options) { { test_framework: :test_unit } } + let(:path) { "test/decorators/#{decorator_name.underscore}_test.rb" } - describe "namespacing" do - subject { file("test/decorators/namespace/your_model_decorator_test.rb") } - before { run_generator %w(Namespace::YourModel -t=test_unit) } + it { is_expected.to have_correct_syntax } - it { is_expected.to contain "class Namespace::YourModelDecoratorTest < Draper::TestCase" } + shared_examples :naming do + it 'is properly named' do + is_expected.to exist + is_expected.to contain "class #{decorator_name}Test < Draper::TestCase" end end + + include_examples :naming + it_behaves_like :namespaceable end end