Skip to content

Commit

Permalink
Generators: allow decorators not to match controller names
Browse files Browse the repository at this point in the history
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 #919.
  • Loading branch information
Alexander-Senko committed Aug 31, 2024
1 parent 2836ea7 commit 250eb7b
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 58 deletions.
11 changes: 9 additions & 2 deletions lib/generators/controller_override.rb
Original file line number Diff line number Diff line change
@@ -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
62 changes: 54 additions & 8 deletions spec/generators/controller/controller_generator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
108 changes: 60 additions & 48 deletions spec/generators/decorator/decorator_generator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 250eb7b

Please sign in to comment.