2018-04-19 12:53:07 -04:00
|
|
|
require 'flipper/adapters/active_record'
|
|
|
|
require 'flipper/adapters/active_support_cache_store'
|
|
|
|
|
2017-05-31 17:06:01 -04:00
|
|
|
class Feature
|
|
|
|
# Classes to override flipper table names
|
|
|
|
class FlipperFeature < Flipper::Adapters::ActiveRecord::Feature
|
|
|
|
# Using `self.table_name` won't work. ActiveRecord bug?
|
|
|
|
superclass.table_name = 'features'
|
2017-10-30 11:10:31 -04:00
|
|
|
|
|
|
|
def self.feature_names
|
|
|
|
pluck(:key)
|
|
|
|
end
|
2017-05-31 17:06:01 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
class FlipperGate < Flipper::Adapters::ActiveRecord::Gate
|
|
|
|
superclass.table_name = 'feature_gates'
|
|
|
|
end
|
|
|
|
|
|
|
|
class << self
|
2017-06-21 10:49:51 -04:00
|
|
|
delegate :group, to: :flipper
|
|
|
|
|
2017-05-31 17:06:01 -04:00
|
|
|
def all
|
|
|
|
flipper.features.to_a
|
|
|
|
end
|
|
|
|
|
|
|
|
def get(key)
|
|
|
|
flipper.feature(key)
|
|
|
|
end
|
|
|
|
|
2017-10-30 11:10:31 -04:00
|
|
|
def persisted_names
|
2018-09-20 15:32:54 -04:00
|
|
|
Gitlab::SafeRequestStore[:flipper_persisted_names] ||= FlipperFeature.feature_names
|
2017-10-30 11:10:31 -04:00
|
|
|
end
|
|
|
|
|
2017-05-31 17:06:01 -04:00
|
|
|
def persisted?(feature)
|
|
|
|
# Flipper creates on-memory features when asked for a not-yet-created one.
|
|
|
|
# If we want to check if a feature has been actually set, we look for it
|
|
|
|
# on the persisted features list.
|
2018-07-19 20:05:24 -04:00
|
|
|
persisted_names.include?(feature.name.to_s)
|
2017-05-31 17:06:01 -04:00
|
|
|
end
|
|
|
|
|
2018-09-05 09:14:16 -04:00
|
|
|
# use `default_enabled: true` to default the flag to being `enabled`
|
|
|
|
# unless set explicitly. The default is `disabled`
|
2018-09-04 14:34:37 -04:00
|
|
|
def enabled?(key, thing = nil, default_enabled: false)
|
|
|
|
feature = Feature.get(key)
|
|
|
|
|
2018-09-05 09:14:16 -04:00
|
|
|
# If we're not default enabling the flag or the feature has been set, always evaluate.
|
|
|
|
# `persisted?` can potentially generate DB queries and also checks for inclusion
|
|
|
|
# in an array of feature names (177 at last count), possibly reducing performance by half.
|
|
|
|
# So we only perform the `persisted` check if `default_enabled: true`
|
|
|
|
!default_enabled || Feature.persisted?(feature) ? feature.enabled?(thing) : true
|
2017-06-21 10:49:51 -04:00
|
|
|
end
|
|
|
|
|
2018-09-04 14:34:37 -04:00
|
|
|
def disabled?(key, thing = nil, default_enabled: false)
|
2018-08-20 14:52:56 -04:00
|
|
|
# we need to make different method calls to make it easy to mock / define expectations in test mode
|
2018-09-04 14:34:37 -04:00
|
|
|
thing.nil? ? !enabled?(key, default_enabled: default_enabled) : !enabled?(key, thing, default_enabled: default_enabled)
|
Add repository languages for projects
Our friends at GitHub show the programming languages for a long time,
and inspired by that this commit means to create about the same
functionality.
Language detection is done through Linguist, as before, where the
difference is that we cache the result in the database. Also, Gitaly can
incrementaly scan a repository. This is done through a shell out, which
creates overhead of about 3s each run. For now this won't be improved.
Scans are triggered by pushed to the default branch, usually `master`.
However, one exception to this rule the charts page. If we're requesting
this expensive data anyway, we just cache it in the database.
Edge cases where there is no repository, or its empty are caught in the
Repository model. This makes use of Redis caching, which is probably
already loaded.
The added model is called RepositoryLanguage, which will make it harder
if/when GitLab supports multiple repositories per project. However, for
now I think this shouldn't be a concern. Also, Language could be
confused with the i18n languages and felt like the current name was
suiteable too.
Design of the Project#Show page is done with help from @dimitrieh. This
change is not visible to the end user unless detections are done.
2018-06-06 07:10:59 -04:00
|
|
|
end
|
|
|
|
|
2017-06-21 10:49:51 -04:00
|
|
|
def enable(key, thing = true)
|
|
|
|
get(key).enable(thing)
|
|
|
|
end
|
|
|
|
|
|
|
|
def disable(key, thing = false)
|
|
|
|
get(key).disable(thing)
|
2017-05-12 12:44:03 -04:00
|
|
|
end
|
|
|
|
|
2017-06-21 10:49:51 -04:00
|
|
|
def enable_group(key, group)
|
|
|
|
get(key).enable_group(group)
|
2017-05-12 12:44:03 -04:00
|
|
|
end
|
|
|
|
|
2017-06-21 10:49:51 -04:00
|
|
|
def disable_group(key, group)
|
|
|
|
get(key).disable_group(group)
|
2017-05-12 12:44:03 -04:00
|
|
|
end
|
|
|
|
|
2017-05-31 17:06:01 -04:00
|
|
|
def flipper
|
2018-09-24 15:29:22 -04:00
|
|
|
@flipper ||= (Gitlab::SafeRequestStore[:flipper] ||= build_flipper_instance)
|
2018-05-31 12:12:48 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def build_flipper_instance
|
|
|
|
Flipper.new(flipper_adapter).tap { |flip| flip.memoize = true }
|
2017-05-31 17:06:01 -04:00
|
|
|
end
|
2017-07-10 09:54:49 -04:00
|
|
|
|
|
|
|
# This method is called from config/initializers/flipper.rb and can be used
|
|
|
|
# to register Flipper groups.
|
|
|
|
# See https://docs.gitlab.com/ee/development/feature_flags.html#feature-groups
|
|
|
|
def register_feature_groups
|
|
|
|
end
|
2018-04-19 12:53:07 -04:00
|
|
|
|
|
|
|
def flipper_adapter
|
|
|
|
active_record_adapter = Flipper::Adapters::ActiveRecord.new(
|
|
|
|
feature_class: FlipperFeature,
|
|
|
|
gate_class: FlipperGate)
|
|
|
|
|
|
|
|
Flipper::Adapters::ActiveSupportCacheStore.new(
|
|
|
|
active_record_adapter,
|
|
|
|
Rails.cache,
|
|
|
|
expires_in: 1.hour)
|
|
|
|
end
|
2017-05-31 17:06:01 -04:00
|
|
|
end
|
|
|
|
end
|