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`.
...
@@ -87,9 +87,9 @@ still having access the class's implementation with `super`.
There are a few gotchas with it:
There are a few gotchas with it:
-
you should always
add a
`raise NotImplementedError unless defined?(super)`
-
you should always
`extend ::Gitlab::Utils::Override`
and use
`override`
to
guard
clause in the "overrider" method to ensure that if the method gets
guard
the "overrider" method to ensure that if the method gets renamed in
renamed in
CE, the EE override won't be silently forgotten.
CE, the EE override won't be silently forgotten.
-
when the "overrider" would add a line in the middle of the CE
-
when the "overrider" would add a line in the middle of the CE
implementation, you should refactor the CE method and split it in
implementation, you should refactor the CE method and split it in
smaller methods. Or create a "hook" method that is empty in CE,
smaller methods. Or create a "hook" method that is empty in CE,
...
@@ -134,6 +134,9 @@ There are a few gotchas with it:
...
@@ -134,6 +134,9 @@ There are a few gotchas with it:
guards:
guards:
```
ruby
```
ruby
module
EE::Base
module
EE::Base
extend
::
Gitlab
::
Utils
::
Override
override
:do_something
def
do_something
def
do_something
# Follow the above pattern to call super and extend it
# Follow the above pattern to call super and extend it
end
end
...
@@ -174,10 +177,11 @@ implementation:
...
@@ -174,10 +177,11 @@ implementation:
```
ruby
```
ruby
module
EE
module
EE
class
ApplicationController
module
ApplicationController
def
after_sign_out_path_for
(
resource
)
extend
::
Gitlab
::
Utils
::
Override
raise
NotImplementedError
unless
defined?
(
super
)
override
:after_sign_out_path_for
def
after_sign_out_path_for
(
resource
)
if
Gitlab
::
Geo
.
secondary?
if
Gitlab
::
Geo
.
secondary?
Gitlab
::
Geo
.
primary_node
.
oauth_logout_url
(
@geo_logout_state
)
Gitlab
::
Geo
.
primary_node
.
oauth_logout_url
(
@geo_logout_state
)
else
else
...
@@ -209,7 +213,6 @@ In EE, the implementation `ee/app/models/ee/users.rb` would be:
...
@@ -209,7 +213,6 @@ In EE, the implementation `ee/app/models/ee/users.rb` would be:
```
ruby
```
ruby
def
full_private_access?
def
full_private_access?
raise
NotImplementedError
unless
defined?
(
super
)
super
||
auditor?
super
||
auditor?
end
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
...
@@ -7,4 +7,9 @@ namespace :dev do
Rake
::
Task
[
"gitlab:setup"
].
invoke
Rake
::
Task
[
"gitlab:setup"
].
invoke
Rake
::
Task
[
"gitlab:shell:setup"
].
invoke
Rake
::
Task
[
"gitlab:shell:setup"
].
invoke
end
end
desc
"GitLab | Eager load application"
task
load: :environment
do
Rails
.
application
.
eager_load!
end
end
end
lib/tasks/lint.rake
View file @
8139895b
unless
Rails
.
env
.
production?
unless
Rails
.
env
.
production?
namespace
:lint
do
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"
desc
"GitLab | lint | Lint JavaScript files using ESLint"
task
:javascript
do
task
:javascript
do
Rake
::
Task
[
'eslint'
].
invoke
Rake
::
Task
[
'eslint'
].
invoke
...
...
scripts/static-analysis
View file @
8139895b
...
@@ -10,9 +10,10 @@ tasks = [
...
@@ -10,9 +10,10 @@ tasks = [
%w[bundle exec license_finder]
,
%w[bundle exec license_finder]
,
%w[yarn run eslint]
,
%w[yarn run eslint]
,
%w[bundle exec rubocop --parallel]
,
%w[bundle exec rubocop --parallel]
,
%w[scripts/lint-conflicts.sh]
,
%w[bundle exec rake gettext:lint]
,
%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
|
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