Commit f71fc932 authored by Lin Jen-Shin's avatar Lin Jen-Shin

Also verify if extending would override a class method

Since extending a class means including on the singleton class of the
class, this should now complain this:

``` ruby
module M
  extend Gitlab::Utils::Override

  override :f
  def f
    super.succ
  end
end

class C
  extend M

  def self.f
    0
  end
end
```

It should complain because `C.f` wasn't calling `M#f`.
This should pass verification:

``` ruby
module M
  extend Gitlab::Utils::Override

  override :f
  def f
    super.succ
  end
end

class B
  def self.f
    0
  end
end

class C < B
  extend M
end
```

Because `C.f` would now call `M#f`, and `M#f` does override something.
parent 9c296194
...@@ -87,18 +87,28 @@ module Gitlab ...@@ -87,18 +87,28 @@ module Gitlab
end end
def included(base = nil) def included(base = nil)
return super if base.nil? # Rails concern, ignoring it super
queue_verification(base)
end
alias_method :prepended, :included
def extended(mod)
super super
queue_verification(mod.singleton_class)
end
def queue_verification(base)
return unless ENV['STATIC_VERIFICATION']
if base.is_a?(Class) # We could check for Class in `override` if base.is_a?(Class) # We could check for Class in `override`
# This could be `nil` if `override` was never called # This could be `nil` if `override` was never called
Override.extensions[self]&.add_class(base) Override.extensions[self]&.add_class(base)
end end
end end
alias_method :prepended, :included
def self.extensions def self.extensions
@extensions ||= {} @extensions ||= {}
end end
......
require 'spec_helper' require 'fast_spec_helper'
describe Gitlab::Utils::Override do describe Gitlab::Utils::Override do
let(:base) { Struct.new(:good) } let(:base) do
Struct.new(:good) do
def self.good
0
end
end
end
let(:derived) { Class.new(base).tap { |m| m.extend described_class } } let(:derived) { Class.new(base).tap { |m| m.extend described_class } }
let(:extension) { Module.new.tap { |m| m.extend described_class } } let(:extension) { Module.new.tap { |m| m.extend described_class } }
...@@ -9,6 +15,14 @@ describe Gitlab::Utils::Override do ...@@ -9,6 +15,14 @@ describe Gitlab::Utils::Override do
let(:prepending_class) { base.tap { |m| m.prepend extension } } let(:prepending_class) { base.tap { |m| m.prepend extension } }
let(:including_class) { base.tap { |m| m.include extension } } let(:including_class) { base.tap { |m| m.include extension } }
let(:prepending_class_methods) do
base.tap { |m| m.singleton_class.prepend extension }
end
let(:extending_class_methods) do
base.tap { |m| m.extend extension }
end
let(:klass) { subject } let(:klass) { subject }
def good(mod) def good(mod)
...@@ -36,7 +50,7 @@ describe Gitlab::Utils::Override do ...@@ -36,7 +50,7 @@ describe Gitlab::Utils::Override do
shared_examples 'checking as intended' do shared_examples 'checking as intended' do
it 'checks ok for overriding method' do it 'checks ok for overriding method' do
good(subject) good(subject)
result = klass.new(0).good result = instance.good
expect(result).to eq(1) expect(result).to eq(1)
described_class.verify! described_class.verify!
...@@ -45,7 +59,25 @@ describe Gitlab::Utils::Override do ...@@ -45,7 +59,25 @@ describe Gitlab::Utils::Override do
it 'raises NotImplementedError when it is not overriding anything' do it 'raises NotImplementedError when it is not overriding anything' do
expect do expect do
bad(subject) bad(subject)
klass.new(0).bad instance.bad
described_class.verify!
end.to raise_error(NotImplementedError)
end
end
shared_examples 'checking as intended, nothing was overridden' do
it 'raises NotImplementedError because it is not overriding it' do
expect do
good(subject)
instance.good
described_class.verify!
end.to raise_error(NotImplementedError)
end
it 'raises NotImplementedError when it is not overriding anything' do
expect do
bad(subject)
instance.bad
described_class.verify! described_class.verify!
end.to raise_error(NotImplementedError) end.to raise_error(NotImplementedError)
end end
...@@ -54,7 +86,7 @@ describe Gitlab::Utils::Override do ...@@ -54,7 +86,7 @@ describe Gitlab::Utils::Override do
shared_examples 'nothing happened' do shared_examples 'nothing happened' do
it 'does not complain when it is overriding something' do it 'does not complain when it is overriding something' do
good(subject) good(subject)
result = klass.new(0).good result = instance.good
expect(result).to eq(1) expect(result).to eq(1)
described_class.verify! described_class.verify!
...@@ -62,7 +94,7 @@ describe Gitlab::Utils::Override do ...@@ -62,7 +94,7 @@ describe Gitlab::Utils::Override do
it 'does not complain when it is not overriding anything' do it 'does not complain when it is not overriding anything' do
bad(subject) bad(subject)
result = klass.new(0).bad result = instance.bad
expect(result).to eq(true) expect(result).to eq(true)
described_class.verify! described_class.verify!
...@@ -75,6 +107,9 @@ describe Gitlab::Utils::Override do ...@@ -75,6 +107,9 @@ describe Gitlab::Utils::Override do
end end
describe '#override' do describe '#override' do
context 'when instance is klass.new(0)' do
let(:instance) { klass.new(0) }
context 'when STATIC_VERIFICATION is set' do context 'when STATIC_VERIFICATION is set' do
before do before do
stub_env('STATIC_VERIFICATION', 'true') stub_env('STATIC_VERIFICATION', 'true')
...@@ -97,22 +132,7 @@ describe Gitlab::Utils::Override do ...@@ -97,22 +132,7 @@ describe Gitlab::Utils::Override do
subject { extension } subject { extension }
let(:klass) { including_class } let(:klass) { including_class }
it 'raises NotImplementedError because it is not overriding it' do it_behaves_like 'checking as intended, nothing was overridden'
expect do
good(subject)
klass.new(0).good
described_class.verify!
end.to raise_error(NotImplementedError)
end
it 'raises NotImplementedError when it is not overriding anything' do
expect do
bad(subject)
klass.new(0).bad
described_class.verify!
end.to raise_error(NotImplementedError)
end
end
end end
end end
...@@ -140,7 +160,7 @@ describe Gitlab::Utils::Override do ...@@ -140,7 +160,7 @@ describe Gitlab::Utils::Override do
it 'does not complain when it is overriding something' do it 'does not complain when it is overriding something' do
good(subject) good(subject)
result = klass.new(0).good result = instance.good
expect(result).to eq(0) expect(result).to eq(0)
described_class.verify! described_class.verify!
...@@ -148,11 +168,37 @@ describe Gitlab::Utils::Override do ...@@ -148,11 +168,37 @@ describe Gitlab::Utils::Override do
it 'does not complain when it is not overriding anything' do it 'does not complain when it is not overriding anything' do
bad(subject) bad(subject)
result = klass.new(0).bad result = instance.bad
expect(result).to eq(true) expect(result).to eq(true)
described_class.verify! described_class.verify!
end end
end end
end end
end
context 'when instance is klass' do
let(:instance) { klass }
context 'when STATIC_VERIFICATION is set' do
before do
stub_env('STATIC_VERIFICATION', 'true')
end
context 'when subject is a module, and class is prepending it' do
subject { extension }
let(:klass) { prepending_class_methods }
it_behaves_like 'checking as intended'
end
context 'when subject is a module, and class is extending it' do
subject { extension }
let(:klass) { extending_class_methods }
it_behaves_like 'checking as intended, nothing was overridden'
end
end
end
end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment