Class: DeprecationTracker
- Inherits:
-
Object
- Object
- DeprecationTracker
show all
- Includes:
- KernelWarnTracker
- Defined in:
- lib/deprecation_tracker.rb,
lib/deprecation_tracker/valid_modes.rb,
lib/deprecation_tracker/shard_merger.rb
Defined Under Namespace
Modules: KernelWarnTracker, MinitestExtension
Classes: ShardMerger
Constant Summary
collapse
- UnexpectedDeprecations =
Class.new(StandardError)
- VALID_MODES =
%i[save compare].freeze
- DEFAULT_PATH =
"spec/support/deprecation_warning.shitlist.json"
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
callbacks, #warn
Constructor Details
#initialize(shitlist_path, transform_message = nil, mode = :save, node_index: nil) ⇒ DeprecationTracker
Returns a new instance of DeprecationTracker.
141
142
143
144
145
146
147
148
149
150
151
152
153
|
# File 'lib/deprecation_tracker.rb', line 141
def initialize(shitlist_path, transform_message = nil, mode = :save, node_index: nil)
@shitlist_path = shitlist_path
@transform_message = transform_message || -> (message) { message }
@deprecation_messages = {}
@mode = mode ? mode.to_sym : :save
unless self.class.valid_mode?(@mode)
raise ArgumentError, "mode must be one of: #{self.class.valid_modes_display}. Got: #{mode.inspect}"
end
if @mode == :compare && node_index
raise ArgumentError, "node_index cannot be used with compare mode"
end
@node_index = node_index
end
|
Instance Attribute Details
#bucket ⇒ Object
Returns the value of attribute bucket.
139
140
141
|
# File 'lib/deprecation_tracker.rb', line 139
def bucket
@bucket
end
|
#deprecation_messages ⇒ Object
Returns the value of attribute deprecation_messages.
139
140
141
|
# File 'lib/deprecation_tracker.rb', line 139
def deprecation_messages
@deprecation_messages
end
|
#mode ⇒ Object
Returns the value of attribute mode.
139
140
141
|
# File 'lib/deprecation_tracker.rb', line 139
def mode
@mode
end
|
#node_index ⇒ Object
Returns the value of attribute node_index.
139
140
141
|
# File 'lib/deprecation_tracker.rb', line 139
def node_index
@node_index
end
|
#shitlist_path ⇒ Object
Returns the value of attribute shitlist_path.
139
140
141
|
# File 'lib/deprecation_tracker.rb', line 139
def shitlist_path
@shitlist_path
end
|
Returns the value of attribute transform_message.
139
140
141
|
# File 'lib/deprecation_tracker.rb', line 139
def transform_message
@transform_message
end
|
Class Method Details
.init_tracker(opts = {}) ⇒ Object
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
|
# File 'lib/deprecation_tracker.rb', line 80
def self.init_tracker(opts = {})
shitlist_path = opts[:shitlist_path] || DEFAULT_PATH
mode = sanitize_mode(opts[:mode] || ENV["DEPRECATION_TRACKER"]) || :save
transform_message = opts[:transform_message]
node_index = opts[:node_index]
deprecation_tracker = DeprecationTracker.new(shitlist_path, transform_message, mode, node_index: node_index)
if defined?(Rails) && defined?(Rails.application) && defined?(Rails.application.deprecators)
Rails.application.deprecators.each do |deprecator|
deprecator.behavior << -> (message, _callstack = nil, _deprecation_horizon = nil, _gem_name = nil) {
deprecation_tracker.add(message)
}
end
elsif defined?(ActiveSupport)
ActiveSupport::Deprecation.behavior << -> (message, _callstack = nil, _deprecation_horizon = nil, _gem_name = nil) {
deprecation_tracker.add(message)
}
end
KernelWarnTracker.callbacks << -> (message) { deprecation_tracker.add(message) }
deprecation_tracker
end
|
.merge_shards(base_path, delete_shards: false) ⇒ Object
134
135
136
137
|
# File 'lib/deprecation_tracker.rb', line 134
def self.merge_shards(base_path, delete_shards: false)
require_relative "deprecation_tracker/shard_merger"
ShardMerger.new(base_path, delete_shards: delete_shards).merge[:result]
end
|
.sanitize_mode(mode) ⇒ Object
Returns the mode as-is, or nil when it is blank. A blank DEPRECATION_TRACKER
(e.g. DEPRECATION_TRACKER= rspec) is truthy in Ruby, so callers can use this
to treat an empty value as unset and fall back to the default mode.
16
17
18
19
20
21
|
# File 'lib/deprecation_tracker/valid_modes.rb', line 16
def self.sanitize_mode(mode)
return if mode.nil?
stripped = mode.to_s.strip
stripped.empty? ? nil : stripped
end
|
.track_minitest(opts = {}) ⇒ Object
124
125
126
127
128
129
130
131
132
|
# File 'lib/deprecation_tracker.rb', line 124
def self.track_minitest(opts = {})
tracker = init_tracker(opts)
Minitest.after_run do
tracker.after_run
end
ActiveSupport::TestCase.include(MinitestExtension.new(tracker))
end
|
.track_rspec(rspec_config, opts = {}) ⇒ Object
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
# File 'lib/deprecation_tracker.rb', line 106
def self.track_rspec(rspec_config, opts = {})
deprecation_tracker = init_tracker(opts)
rspec_config.around do |example|
deprecation_tracker.bucket = example.metadata.fetch(:rerun_file_path)
begin
example.run
ensure
deprecation_tracker.bucket = nil
end
end
rspec_config.after(:suite) do
deprecation_tracker.after_run
end
end
|
.valid_mode?(mode) ⇒ Boolean
9
10
11
|
# File 'lib/deprecation_tracker/valid_modes.rb', line 9
def self.valid_mode?(mode)
mode && VALID_MODES.include?(mode.to_sym)
end
|
.valid_modes_display ⇒ Object
Human-readable list of valid modes for error messages and CLI help text.
5
6
7
|
# File 'lib/deprecation_tracker/valid_modes.rb', line 5
def self.valid_modes_display
@valid_modes_display ||= VALID_MODES.map(&:to_s).join(", ")
end
|
Instance Method Details
#add(message) ⇒ Object
168
169
170
171
172
|
# File 'lib/deprecation_tracker.rb', line 168
def add(message)
return if bucket.nil?
@deprecation_messages[bucket] << transform_message.(message)
end
|
#after_run ⇒ Object
179
180
181
182
183
184
185
|
# File 'lib/deprecation_tracker.rb', line 179
def after_run
if mode == :save
save
elsif mode == :compare
compare
end
end
|
#compare ⇒ Object
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
|
# File 'lib/deprecation_tracker.rb', line 187
def compare
stored = read_json(shitlist_path)
changed_buckets = []
normalized_deprecation_messages.each do |bucket, messages|
if stored[bucket] != messages
changed_buckets << bucket
end
end
if changed_buckets.any?
message = <<-MESSAGE
⚠️ Deprecation warnings have changed!
Code called by the following spec files is now generating different deprecation warnings:
#{changed_buckets.join("\n")}
To check your failures locally, you can run:
DEPRECATION_TRACKER=compare bundle exec rspec #{changed_buckets.join(" ")}
Here is a diff between what is expected and what was generated by this process:
#{diff}
See \e[4;37mdev-docs/testing/deprecation_tracker.md\e[0;31m for more information.
MESSAGE
raise UnexpectedDeprecations, NextRails::Tint(message).red
end
end
|
#create_if_path_does_not_exist(path) ⇒ Object
236
237
238
239
240
241
|
# File 'lib/deprecation_tracker.rb', line 236
def create_if_path_does_not_exist(path)
dirname = File.dirname(path)
unless File.directory?(dirname)
FileUtils.mkdir_p(dirname)
end
end
|
#create_temp_file ⇒ Object
243
244
245
246
247
248
249
|
# File 'lib/deprecation_tracker.rb', line 243
def create_temp_file
temp_file = Tempfile.new("temp-deprecation-tracker-shitlist")
temp_file.write(JSON.pretty_generate(normalized_deprecation_messages))
temp_file.flush
temp_file
end
|
#diff ⇒ Object
221
222
223
224
225
226
|
# File 'lib/deprecation_tracker.rb', line 221
def diff
temp_file = create_temp_file
`git diff --no-index #{shitlist_path} #{temp_file.path}`
ensure
temp_file.delete
end
|
#normalized_deprecation_messages ⇒ Object
Normalize deprecation messages to reduce noise from file output and test files to be tracked with separate test runs
252
253
254
255
256
257
258
259
260
261
262
263
264
265
|
# File 'lib/deprecation_tracker.rb', line 252
def normalized_deprecation_messages
@normalized_deprecation_messages ||= begin
normalized = read_json(target_path).merge(deprecation_messages).each_with_object({}) do |(bucket, messages), hash|
hash[bucket] = messages.sort
end
{}.tap do |h|
normalized.reject {|_key, value| value.empty? }.sort_by {|key, _value| key }.each do |k ,v|
h[k] = v
end
end
end
end
|
#parallel? ⇒ Boolean
155
156
157
|
# File 'lib/deprecation_tracker.rb', line 155
def parallel?
!@node_index.nil?
end
|
#read_json(path) ⇒ Object
267
268
269
270
271
272
|
# File 'lib/deprecation_tracker.rb', line 267
def read_json(path)
return {} unless File.exist?(path)
JSON.parse(File.read(path))
rescue JSON::ParserError => e
raise "#{path} is not valid JSON: #{e.message}"
end
|
#save ⇒ Object
228
229
230
231
232
233
234
|
# File 'lib/deprecation_tracker.rb', line 228
def save
temp_file = create_temp_file
create_if_path_does_not_exist(target_path)
FileUtils.cp(temp_file.path, target_path)
ensure
temp_file.delete if temp_file
end
|
#shard_path ⇒ Object
159
160
161
162
|
# File 'lib/deprecation_tracker.rb', line 159
def shard_path
ext = File.extname(shitlist_path)
"#{shitlist_path.chomp(ext)}.node-#{node_index}#{ext}"
end
|
#target_path ⇒ Object
164
165
166
|
# File 'lib/deprecation_tracker.rb', line 164
def target_path
parallel? ? shard_path : shitlist_path
end
|