Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Léo-Paul Géneau
gitlab-ce
Commits
8139895b
Commit
8139895b
authored
Dec 25, 2017
by
Lin Jen-Shin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Use `Gitlab::Utils::Override` over defined?(super)
parent
29749f92
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
299 additions
and
9 deletions
+299
-9
doc/development/ee_features.md
doc/development/ee_features.md
+10
-7
lib/gitlab/utils/override.rb
lib/gitlab/utils/override.rb
+111
-0
lib/tasks/dev.rake
lib/tasks/dev.rake
+5
-0
lib/tasks/lint.rake
lib/tasks/lint.rake
+12
-0
scripts/static-analysis
scripts/static-analysis
+3
-2
spec/lib/gitlab/utils/override_spec.rb
spec/lib/gitlab/utils/override_spec.rb
+158
-0
No files found.
doc/development/ee_features.md
View file @
8139895b
...
...
@@ -87,9 +87,9 @@ still having access the class's implementation with `super`.
There are a few gotchas with it:
-
you should always
add a
`raise NotImplementedError unless defined?(super)`
guard
clause in the "overrider" method to ensure that if the method gets
renamed in
CE, the EE override won't be silently forgotten.
-
you should always
`extend ::Gitlab::Utils::Override`
and use
`override`
to
guard
the "overrider" method to ensure that if the method gets renamed in
CE, the EE override won't be silently forgotten.
-
when the "overrider" would add a line in the middle of the CE
implementation, you should refactor the CE method and split it in
smaller methods. Or create a "hook" method that is empty in CE,
...
...
@@ -134,6 +134,9 @@ There are a few gotchas with it:
guards:
```
ruby
module
EE::Base
extend
::
Gitlab
::
Utils
::
Override
override
:do_something
def
do_something
# Follow the above pattern to call super and extend it
end
...
...
@@ -174,10 +177,11 @@ implementation:
```
ruby
module
EE
class
ApplicationController
def
after_sign_out_path_for
(
resource
)
raise
NotImplementedError
unless
defined?
(
super
)
module
ApplicationController
extend
::
Gitlab
::
Utils
::
Override
override
:after_sign_out_path_for
def
after_sign_out_path_for
(
resource
)
if
Gitlab
::
Geo
.
secondary?
Gitlab
::
Geo
.
primary_node
.
oauth_logout_url
(
@geo_logout_state
)
else
...
...
@@ -209,7 +213,6 @@ In EE, the implementation `ee/app/models/ee/users.rb` would be:
```
ruby
def
full_private_access?
raise
NotImplementedError
unless
defined?
(
super
)
super
||
auditor?
end
```
...
...
lib/gitlab/utils/override.rb
0 → 100644
View file @
8139895b
module
Gitlab
module
Utils
module
Override
class
Extension
def
self
.
verify_class!
(
klass
,
method_name
)
instance_method_defined?
(
klass
,
method_name
)
||
raise
(
NotImplementedError
.
new
(
"
#{
klass
}
\#
#{
method_name
}
doesn't exist!"
))
end
def
self
.
instance_method_defined?
(
klass
,
name
,
include_super:
true
)
klass
.
instance_methods
(
include_super
).
include?
(
name
)
||
klass
.
private_instance_methods
(
include_super
).
include?
(
name
)
end
attr_reader
:subject
def
initialize
(
subject
)
@subject
=
subject
end
def
add_method_name
(
method_name
)
method_names
<<
method_name
end
def
add_class
(
klass
)
classes
<<
klass
end
def
verify!
classes
.
each
do
|
klass
|
index
=
klass
.
ancestors
.
index
(
subject
)
parents
=
klass
.
ancestors
.
drop
(
index
+
1
)
method_names
.
each
do
|
method_name
|
parents
.
any?
do
|
parent
|
self
.
class
.
instance_method_defined?
(
parent
,
method_name
,
include_super:
false
)
end
||
raise
(
NotImplementedError
.
new
(
"
#{
klass
}
\#
#{
method_name
}
doesn't exist!"
))
end
end
end
private
def
method_names
@method_names
||=
[]
end
def
classes
@classes
||=
[]
end
end
# Instead of writing patterns like this:
#
# def f
# raise NotImplementedError unless defined?(super)
#
# true
# end
#
# We could write it like:
#
# extend ::Gitlab::Utils::Override
#
# override :f
# def f
# true
# end
#
# This would make sure we're overriding something. See:
# https://gitlab.com/gitlab-org/gitlab-ee/issues/1819
def
override
(
method_name
)
return
unless
ENV
[
'STATIC_VERIFICATION'
]
if
is_a?
(
Class
)
Extension
.
verify_class!
(
self
,
method_name
)
else
# We delay the check for modules
Override
.
extensions
[
self
]
||=
Extension
.
new
(
self
)
Override
.
extensions
[
self
].
add_method_name
(
method_name
)
end
end
def
included
(
base
=
nil
)
return
super
if
base
.
nil?
# Rails concern, ignoring it
super
if
base
.
is_a?
(
Class
)
# We could check for Class in `override`
# This could be `nil` if `override` was never called
Override
.
extensions
[
self
]
&
.
add_class
(
base
)
end
end
alias_method
:prepended
,
:included
def
self
.
extensions
@extensions
||=
{}
end
def
self
.
verify!
extensions
.
values
.
each
(
&
:verify!
)
end
end
end
end
lib/tasks/dev.rake
View file @
8139895b
...
...
@@ -7,4 +7,9 @@ namespace :dev do
Rake
::
Task
[
"gitlab:setup"
].
invoke
Rake
::
Task
[
"gitlab:shell:setup"
].
invoke
end
desc
"GitLab | Eager load application"
task
load: :environment
do
Rails
.
application
.
eager_load!
end
end
lib/tasks/lint.rake
View file @
8139895b
unless
Rails
.
env
.
production?
namespace
:lint
do
task
:static_verification_env
do
ENV
[
'STATIC_VERIFICATION'
]
=
'true'
end
desc
"GitLab | lint | Static verification"
task
static_verification:
%w[
lint:static_verification_env
dev:load
]
do
Gitlab
::
Utils
::
Override
.
verify!
end
desc
"GitLab | lint | Lint JavaScript files using ESLint"
task
:javascript
do
Rake
::
Task
[
'eslint'
].
invoke
...
...
scripts/static-analysis
View file @
8139895b
...
...
@@ -10,9 +10,10 @@ tasks = [
%w[bundle exec license_finder]
,
%w[yarn run eslint]
,
%w[bundle exec rubocop --parallel]
,
%w[scripts/lint-conflicts.sh]
,
%w[bundle exec rake gettext:lint]
,
%w[scripts/lint-changelog-yaml]
%w[bundle exec rake lint:static_verification]
,
%w[scripts/lint-changelog-yaml]
,
%w[scripts/lint-conflicts.sh]
]
failed_tasks
=
tasks
.
reduce
({})
do
|
failures
,
task
|
...
...
spec/lib/gitlab/utils/override_spec.rb
0 → 100644
View file @
8139895b
require
'spec_helper'
describe
Gitlab
::
Utils
::
Override
do
let
(
:base
)
{
Struct
.
new
(
:good
)
}
let
(
:derived
)
{
Class
.
new
(
base
).
tap
{
|
m
|
m
.
extend
described_class
}
}
let
(
:extension
)
{
Module
.
new
.
tap
{
|
m
|
m
.
extend
described_class
}
}
let
(
:prepending_class
)
{
base
.
tap
{
|
m
|
m
.
prepend
extension
}
}
let
(
:including_class
)
{
base
.
tap
{
|
m
|
m
.
include
extension
}
}
let
(
:klass
)
{
subject
}
def
good
(
mod
)
mod
.
module_eval
do
override
:good
def
good
super
.
succ
end
end
mod
end
def
bad
(
mod
)
mod
.
module_eval
do
override
:bad
def
bad
true
end
end
mod
end
shared_examples
'checking as intended'
do
it
'checks ok for overriding method'
do
good
(
subject
)
result
=
klass
.
new
(
0
).
good
expect
(
result
).
to
eq
(
1
)
described_class
.
verify!
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
shared_examples
'nothing happened'
do
it
'does not complain when it is overriding something'
do
good
(
subject
)
result
=
klass
.
new
(
0
).
good
expect
(
result
).
to
eq
(
1
)
described_class
.
verify!
end
it
'does not complain when it is not overriding anything'
do
bad
(
subject
)
result
=
klass
.
new
(
0
).
bad
expect
(
result
).
to
eq
(
true
)
described_class
.
verify!
end
end
before
do
# Make sure we're not touching the internal cache
allow
(
described_class
).
to
receive
(
:extensions
).
and_return
({})
end
describe
'#override'
do
context
'when STATIC_VERIFICATION is set'
do
before
do
stub_env
(
'STATIC_VERIFICATION'
,
'true'
)
end
context
'when subject is a class'
do
subject
{
derived
}
it_behaves_like
'checking as intended'
end
context
'when subject is a module, and class is prepending it'
do
subject
{
extension
}
let
(
:klass
)
{
prepending_class
}
it_behaves_like
'checking as intended'
end
context
'when subject is a module, and class is including it'
do
subject
{
extension
}
let
(
:klass
)
{
including_class
}
it
'raises NotImplementedError because it is not overriding it'
do
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
context
'when STATIC_VERIFICATION is not set'
do
before
do
stub_env
(
'STATIC_VERIFICATION'
,
nil
)
end
context
'when subject is a class'
do
subject
{
derived
}
it_behaves_like
'nothing happened'
end
context
'when subject is a module, and class is prepending it'
do
subject
{
extension
}
let
(
:klass
)
{
prepending_class
}
it_behaves_like
'nothing happened'
end
context
'when subject is a module, and class is including it'
do
subject
{
extension
}
let
(
:klass
)
{
including_class
}
it
'does not complain when it is overriding something'
do
good
(
subject
)
result
=
klass
.
new
(
0
).
good
expect
(
result
).
to
eq
(
0
)
described_class
.
verify!
end
it
'does not complain when it is not overriding anything'
do
bad
(
subject
)
result
=
klass
.
new
(
0
).
bad
expect
(
result
).
to
eq
(
true
)
described_class
.
verify!
end
end
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment