Welcome to mirror list, hosted at ThFree Co, Russian Federation.

deprecation.rb « deprecations « graphql « gitlab « lib - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 0cf555b0e3405d62e02c8fee547b35e03c8b45a1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# frozen_string_literal: true

module Gitlab
  module Graphql
    module Deprecations
      class Deprecation
        REASON_RENAMED = :renamed
        REASON_ALPHA = :alpha

        REASONS = {
          REASON_RENAMED => 'This was renamed.',
          REASON_ALPHA => 'This feature is an Experiment. It can be changed or removed at any time.'
        }.freeze

        include ActiveModel::Validations

        validates :milestone, presence: true, format: { with: /\A\d+\.\d+\z/, message: 'must be milestone-ish' }
        validates :reason, presence: true
        validates :reason,
                  format: { with: /.*[^.]\z/, message: 'must not end with a period' },
                  if: :reason_is_string?
        validate :milestone_is_string
        validate :reason_known_or_string

        def self.parse(alpha: nil, deprecated: nil)
          options = alpha || deprecated
          return unless options

          if alpha
            raise ArgumentError, '`alpha` and `deprecated` arguments cannot be passed at the same time' \
              if deprecated

            options[:reason] = :alpha
          end

          new(**options)
        end

        def initialize(reason: nil, milestone: nil, replacement: nil)
          @reason = reason.presence
          @milestone = milestone.presence
          @replacement = replacement.presence
        end

        def ==(other)
          return false unless other.is_a?(self.class)

          [reason_text, milestone, replacement] == [:reason_text, :milestone, :replacement].map do |attr|
            other.send(attr) # rubocop: disable GitlabSecurity/PublicSend
          end
        end
        alias_method :eql, :==

        def markdown(context: :inline)
          parts = [
            "#{changed_in_milestone(format: :markdown)}.",
            reason_text,
            replacement_markdown.then { |r| "Use: #{r}." if r }
          ].compact

          case context
          when :block
            ['WARNING:', *parts].join("\n")
          when :inline
            parts.join(' ')
          end
        end

        def replacement_markdown
          return unless replacement.present?
          return "`#{replacement}`" unless replacement.include?('.') # only fully qualified references can be linked

          "[`#{replacement}`](##{replacement.downcase.tr('.', '')})"
        end

        def edit_description(original_description)
          @original_description = original_description
          return unless original_description

          original_description + description_suffix
        end

        def original_description
          return unless @original_description
          return @original_description if @original_description.ends_with?('.')

          "#{@original_description}."
        end

        def deprecation_reason
          [
            reason_text,
            replacement && "Please use `#{replacement}`.",
            "#{changed_in_milestone}."
          ].compact.join(' ')
        end

        def alpha?
          reason == REASON_ALPHA
        end

        private

        attr_reader :reason, :milestone, :replacement

        def milestone_is_string
          return if milestone.is_a?(String)

          errors.add(:milestone, 'must be a string')
        end

        def reason_known_or_string
          return if REASONS.key?(reason)
          return if reason_is_string?

          errors.add(:reason, 'must be a known reason or a string')
        end

        def reason_is_string?
          reason.is_a?(String)
        end

        def reason_text
          @reason_text ||= REASONS[reason] || "#{reason.to_s.strip}."
        end

        def description_suffix
          " #{changed_in_milestone}: #{reason_text}"
        end

        # Returns 'Deprecated in <milestone>' for proper deprecations.
        # Retruns 'Introduced in <milestone>' for :alpha deprecations.
        # Formatted to markdown or plain format.
        def changed_in_milestone(format: :plain)
          verb = if alpha?
                   'Introduced'
                 else
                   'Deprecated'
                 end

          case format
          when :plain
            "#{verb} in #{milestone}"
          when :markdown
            "**#{verb}** in #{milestone}"
          end
        end
      end
    end
  end
end