From 0665a8f730e19e180f2b4d240ca7e93408d61b12 Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Thu, 31 May 2018 20:32:36 +0200 Subject: Enable mapping to nil in enums Enum in Rails 5 does not map nil values - IOW nil value remains nil, even if there is a key with nil value in the enum definition. This commit overrides the underlying Enum methods so nil value is still mapped. This solution is far from being ideal: it uses dynamic definition of methods which introduces more magic/confusion into the codebase. It would be better to get rid of the nil value in enums. --- app/models/ci/pipeline.rb | 16 +++------------- app/models/commit_status.rb | 10 ++-------- app/models/concerns/enum_with_nil.rb | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 21 deletions(-) create mode 100644 app/models/concerns/enum_with_nil.rb (limited to 'app/models') diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index eecd86349e4..0878356e87a 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -8,6 +8,7 @@ module Ci include Gitlab::OptimisticLocking include Gitlab::Utils::StrongMemoize include AtomicInternalId + include EnumWithNil belongs_to :project, inverse_of: :pipelines belongs_to :user @@ -54,7 +55,7 @@ module Ci after_create :keep_around_commits, unless: :importing? - enum source: { + enum_with_nil source: { unknown: nil, push: 1, web: 2, @@ -64,7 +65,7 @@ module Ci external: 6 } - enum config_source: { + enum_with_nil config_source: { unknown_source: nil, repository_source: 1, auto_devops_source: 2 @@ -599,17 +600,6 @@ module Ci @latest_builds_with_artifacts ||= builds.latest.with_artifacts_archive.to_a end - # Rails 5.0 autogenerated question mark enum methods return wrong result if enum value is nil. - # They always return `false`. - # These methods overwrite autogenerated ones to return correct results. - def unknown? - Gitlab.rails5? ? source.nil? : super - end - - def unknown_source? - Gitlab.rails5? ? config_source.nil? : super - end - private def ci_yaml_from_repo diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index a7d05722287..97516079b66 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -3,6 +3,7 @@ class CommitStatus < ActiveRecord::Base include Importable include AfterCommitQueue include Presentable + include EnumWithNil self.table_name = 'ci_builds' @@ -39,7 +40,7 @@ class CommitStatus < ActiveRecord::Base scope :retried_ordered, -> { retried.ordered.includes(project: :namespace) } scope :after_stage, -> (index) { where('stage_idx > ?', index) } - enum failure_reason: { + enum_with_nil failure_reason: { unknown_failure: nil, script_failure: 1, api_failure: 2, @@ -190,11 +191,4 @@ class CommitStatus < ActiveRecord::Base v =~ /\d+/ ? v.to_i : v end end - - # Rails 5.0 autogenerated question mark enum methods return wrong result if enum value is nil. - # They always return `false`. - # This method overwrites the autogenerated one to return correct result. - def unknown_failure? - Gitlab.rails5? ? failure_reason.nil? : super - end end diff --git a/app/models/concerns/enum_with_nil.rb b/app/models/concerns/enum_with_nil.rb new file mode 100644 index 00000000000..6b37903da20 --- /dev/null +++ b/app/models/concerns/enum_with_nil.rb @@ -0,0 +1,33 @@ +module EnumWithNil + extend ActiveSupport::Concern + + included do + def self.enum_with_nil(definitions) + # use original `enum` to auto-define all methods + enum(definitions) + + # override auto-defined methods only for the + # key which uses nil value + definitions.each do |name, values| + next unless key_with_nil = values.key(nil) + + # E.g. for enum_with_nil failure_reason: { unknown_failure: nil } + # this overrides auto-generated method `unknown_failure?` + define_method("#{key_with_nil}?") do + Gitlab.rails5? ? self[name].nil? : super() + end + + # E.g. for enum_with_nil failure_reason: { unknown_failure: nil } + # this overrides auto-generated method `failure_reason` + define_method(name) do + orig = super() + + return orig unless Gitlab.rails5? + return orig unless orig.nil? + + self.class.public_send(name.to_s.pluralize).key(nil) # rubocop:disable GitlabSecurity/PublicSend + end + end + end + end +end -- cgit v1.2.3