deprecation_collector 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 12a18a7dcdd264edfd2e2350b648a5f7ba9ade6c6623ee500ffff557277bc02e
4
- data.tar.gz: 4c39ea72577e7eca7092aa040a9b0095134f833c155b8a417b80be5e6b9a25bf
3
+ metadata.gz: e119a79e2c879e9e0ec02476523620eab84c6ae4c354d382207cd8be6bde7a9a
4
+ data.tar.gz: e2f7f495ee705f5dada2456b425fa739a9a67b801dce461c86e5f4a04c6266d0
5
5
  SHA512:
6
- metadata.gz: 0e5acf3561f2b2f106362e3738050197188bd9d51b32c8534feb30ba2bcba67b51ca514a20e529818ac28388b1823371131735adb61ae5e095150908e4542b5e
7
- data.tar.gz: e35978336a7642085f9daca1eff1922d80abd9e769bffef46f6ba6d4c36d066fe99b39f0dba262710c0d8469c7e5f7b9567dc702268d9d4712247ef22dc8f9a1
6
+ metadata.gz: 8857214426445395aa62c0c56dae0d598d5f673f2d1b8d42bd00a7a8ba1ef037513ca5449a93c182f0556ef45fe6e6345bd798ffa2137a7e78f92f5f1902f0a1
7
+ data.tar.gz: 9de452b1a5d98b40777ac59224e57a134368ae91c9c24ecc7d792a199c66fd5f92c22d62689c6cadbc409d3d330057d4c487e50f6100dc697ea5295b83761066
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ == 0.6.0
2
+ - added active_record storage
3
+ - redis dependency will become optional in v1.0
4
+ - more test coverage
5
+
1
6
  == 0.5.0
2
7
  - more work on ui
3
8
  - refactored to separate deprecations storage from other logic
@@ -18,14 +18,7 @@ Gem::Specification.new do |spec|
18
18
  spec.metadata["source_code_uri"] = "https://github.com/Vasfed/deprecation_collector"
19
19
  spec.metadata["changelog_uri"] = "https://github.com/Vasfed/deprecation_collector/blob/main/CHANGELOG.md"
20
20
 
21
- # Specify which files should be added to the gem when it is released.
22
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
- spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
- `git ls-files -z`.split("\x0").reject do |f|
25
- (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)}) ||
26
- f.match(%r{\Alib/deprecation_collector/web/views/.+\.slim\z})
27
- end
28
- end + Dir["lib/deprecation_collector/web/views/*.slim"].map { |template| template.sub(/\.slim\z/, ".template.rb") }
21
+ spec.files = Dir['lib/**/*', 'sig/**/*', '*.md', '*.txt', '*.gemspec'].select { |f| File.file?(f) }
29
22
  spec.require_paths = ["lib"]
30
23
 
31
24
  spec.add_dependency "redis", ">= 3.0"
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DeprecationCollector
4
+ module Storage
5
+ # NB: this will not work in tests because of transactions, and may be affected by transactions of the app
6
+ # TODO: use separate db connection to mitigate this
7
+ class ActiveRecord < DeprecationCollector::Storage::Base
8
+ def initialize(model: nil, mutex: nil, count: false, write_interval: 900, write_interval_jitter: 60,
9
+ key_prefix: nil)
10
+ super
11
+ raise "key prefix not supported in AR" if key_prefix && key_prefix != "deprecations"
12
+
13
+ self.model = model if model
14
+ @last_write_time = current_time
15
+ @count = count
16
+ @write_interval = write_interval
17
+ @write_interval_jitter = write_interval_jitter
18
+ # on cruby hash itself is threadsafe, but we need to prevent races
19
+ @deprecations_mutex = mutex || Mutex.new
20
+ @deprecations = {}
21
+ @known_digests = Set.new
22
+ end
23
+
24
+ def model=(model)
25
+ expected_class_methods = %i[column_names where pluck delete_all upsert_all find_in_batches find_by]
26
+ unless expected_class_methods.all? { |method_name| model.respond_to?(method_name) }
27
+ raise ArgumentError, "model expected to be a AR-like class responding to #{expected_class_methods.join(', ')}"
28
+ end
29
+
30
+ expected_fields = %w[digest data notes created_at updated_at]
31
+ unless expected_fields.all? { |column_name| model.column_names.include?(column_name) }
32
+ raise ArgumentError, "model expected to be a AR-like class with fields #{expected_fields.join(', ')}"
33
+ end
34
+
35
+ @model = model
36
+ end
37
+
38
+ def model
39
+ @model ||= ::Deprecation
40
+ end
41
+
42
+ attr_writer :key_prefix
43
+
44
+ def unsent_deprecations
45
+ @deprecations
46
+ end
47
+
48
+ def delete(remove_digests)
49
+ model.where(digest: remove_digests).delete_all
50
+ end
51
+
52
+ def clear(enable: false) # rubocop:disable Lint/UnusedMethodArgument
53
+ model.delete_all
54
+ @known_digests.clear
55
+ @deprecations.clear
56
+ end
57
+
58
+ def fetch_known_digests
59
+ @known_digests.merge(model.pluck(:digest))
60
+ end
61
+
62
+ def store(deprecation)
63
+ fresh = !@deprecations.key?(deprecation.digest)
64
+ @deprecations_mutex.synchronize do
65
+ (@deprecations[deprecation.digest] ||= deprecation).touch
66
+ end
67
+
68
+ flush if current_time - @last_write_time > (@write_interval + rand(@write_interval_jitter))
69
+ fresh
70
+ end
71
+
72
+ def flush(force: false)
73
+ return unless force || (current_time > @last_write_time + @write_interval)
74
+
75
+ deprecations_to_flush = nil
76
+ @deprecations_mutex.synchronize do
77
+ deprecations_to_flush = @deprecations
78
+ @deprecations = {}
79
+ @last_write_time = current_time
80
+ # checking in this section to prevent multiple parallel check requests
81
+ return DeprecationCollector.instance.instance_variable_set(:@enabled, false) unless enabled?
82
+ end
83
+
84
+ # write_count_to_redis(deprecations_to_flush) if @count
85
+
86
+ # make as few writes as possible, other workers may already have reported our warning
87
+ fetch_known_digests
88
+ deprecations_to_flush.reject! { |digest, _val| @known_digests.include?(digest) }
89
+ return unless deprecations_to_flush.any?
90
+
91
+ @known_digests.merge(deprecations_to_flush.keys)
92
+
93
+ model.upsert_all(
94
+ deprecations_to_flush.map do |key, deprecation|
95
+ {
96
+ digest: key, data: deprecation.as_json,
97
+ created_at: timestamp_to_time(deprecation.first_timestamp),
98
+ updated_at: timestamp_to_time(deprecation.first_timestamp)
99
+ }
100
+ end,
101
+ unique_by: :digest # , update_only: %i[data updated_at] # rails 7
102
+ )
103
+ end
104
+
105
+ def read_each
106
+ model.find_in_batches do |batch| # this is find_each, but do not require it to be implemented
107
+ batch.each do |record|
108
+ yield(record.digest, record.data.to_json, record.data&.dig("count"), record.notes)
109
+ end
110
+ end
111
+ end
112
+
113
+ def read_one(digest)
114
+ return [nil] * 4 unless (record = model.find_by(digest: digest))
115
+
116
+ [record.digest, record.data.to_json, record.data&.dig("count"), record.notes]
117
+ end
118
+
119
+ def import(dump_hash)
120
+ attrs = dump_hash.map do |key, deprecation|
121
+ time = deprecation["first_timestamp"] || deprecation[:first_timestamp]
122
+ time = time&.yield_self { |tme| timestamp_to_time(tme) } || current_time
123
+ { digest: key, data: deprecation, created_at: time, updated_at: time }
124
+ end
125
+ model.upsert_all(attrs, unique_by: :digest) # , update_only: %i[data updated_at])
126
+ end
127
+
128
+ def cleanup(&_block)
129
+ removed = total = 0
130
+
131
+ model.find_in_batches do |batch|
132
+ total += batch.size
133
+ removed += delete(
134
+ batch.select { |record| yield(record.data.deep_symbolize_keys) }.map(&:digest)
135
+ )
136
+ end
137
+ "#{removed} removed, #{total - removed} left"
138
+ end
139
+
140
+ protected
141
+
142
+ def current_time
143
+ return Time.zone.now if Time.respond_to?(:zone) && Time.zone
144
+
145
+ Time.now
146
+ end
147
+
148
+ def timestamp_to_time(timestamp)
149
+ return Time.zone.at(timestamp) if Time.respond_to?(:zone) && Time.zone
150
+
151
+ Time.at(timestamp)
152
+ end
153
+ end
154
+ end
155
+ end
@@ -21,6 +21,11 @@ class DeprecationCollector
21
21
  def flush(**); end
22
22
 
23
23
  def store(_deprecation); raise("Not implemented"); end
24
+
25
+ def read_each; end
26
+ def read_one(_digest); [nil] * 4 end
27
+
28
+ def import(_dump); end
24
29
  # rubocop:enable Style/SingleLineMethods
25
30
  end
26
31
 
@@ -36,7 +41,7 @@ class DeprecationCollector
36
41
  attr_accessor :write_interval, :write_interval_jitter, :redis, :count
37
42
 
38
43
  def initialize(redis: nil, mutex: nil, count: false, write_interval: 900, write_interval_jitter: 60,
39
- key_prefix: nil)
44
+ key_prefix: nil)
40
45
  super
41
46
  @key_prefix = key_prefix || "deprecations"
42
47
  @redis = redis
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class DeprecationCollector
4
- VERSION = "0.5.2"
4
+ VERSION = "0.6.0"
5
5
  end
@@ -24,17 +24,17 @@ class DeprecationCollector
24
24
 
25
25
  root do # index
26
26
  @deprecations = collector_instance.read_each.to_a.compact
27
- @deprecations = @deprecations.sort_by { |dep| dep[:message] } unless params[:sort] == "0"
27
+ @deprecations = @deprecations.sort_by { |dep| dep[:message].to_s } unless params[:sort] == "0"
28
28
 
29
29
  if params[:reject]
30
- @deprecations = @deprecations.reject { |dep| dep[:message].match?(Regexp.union(Array(params[:reject]))) }
30
+ @deprecations = @deprecations.reject { |dep| dep[:message]&.match?(Regexp.union(Array(params[:reject]))) }
31
31
  end
32
32
 
33
33
  if params[:realm]
34
- @deprecations = @deprecations.select { |dep| dep[:realm].match?(Regexp.union(Array(params[:realm]))) }
34
+ @deprecations = @deprecations.select { |dep| dep[:realm]&.match?(Regexp.union(Array(params[:realm]))) }
35
35
  end
36
36
 
37
- render slim: "index.html"
37
+ render slim: "index.html", locals: { deprecations: @deprecations }
38
38
  end
39
39
 
40
40
  get "/dump.json" do
@@ -42,12 +42,13 @@ class DeprecationCollector
42
42
  end
43
43
 
44
44
  get "/import" do
45
- return "Import not enabled" unless import_enabled?
45
+ halt 403, "Import not enabled" unless import_enabled?
46
46
 
47
47
  render slim: "import.html"
48
48
  end
49
49
 
50
50
  post "/import" do
51
+ halt 403, "Import not enabled" unless import_enabled?
51
52
  unless env["CONTENT_TYPE"]&.start_with?("multipart/form-data") && params.dig(:file, :tempfile)
52
53
  halt 422, "need multipart json file"
53
54
  end
@@ -14,7 +14,7 @@ class DeprecationCollector
14
14
 
15
15
  def root_path
16
16
  # request.base_url ?
17
- "#{env["SCRIPT_NAME"]}/"
17
+ "#{env['SCRIPT_NAME']}/"
18
18
  end
19
19
 
20
20
  def current_path
@@ -26,7 +26,7 @@ class DeprecationCollector
26
26
  end
27
27
 
28
28
  def deprecation_path(id, format: nil)
29
- ["#{root_path}#{id}", format].compact.join('.')
29
+ ["#{root_path}#{id}", format].compact.join(".")
30
30
  end
31
31
 
32
32
  def enable_deprecations_path
@@ -62,14 +62,14 @@ class DeprecationCollector
62
62
  end
63
63
 
64
64
  def detect_tag(deprecation)
65
- msg = deprecation[:message]
65
+ msg = deprecation[:message].to_s
66
66
  return :kwargs if msg.include?("Using the last argument as keyword parameters is deprecated") ||
67
67
  msg.include?("Passing the keyword argument as the last hash parameter is deprecated")
68
68
  end
69
69
 
70
70
  def test_deprecation?(deprecation)
71
- %w[trigger_kwargs_error_warning trigger_rails_deprecation].any? do
72
- |method| deprecation[:message].include?(method)
71
+ %w[trigger_kwargs_error_warning trigger_rails_deprecation].any? do |method|
72
+ deprecation[:message].to_s.include?(method)
73
73
  end
74
74
  end
75
75
 
@@ -82,11 +82,13 @@ class DeprecationCollector
82
82
  tags << deprecation[:realm] if deprecation[:realm] && deprecation[:realm] != "rails"
83
83
  tags.merge(deprecation.dig(:notes, :tags) || [])
84
84
 
85
- tags.to_h do |tag|
86
- next [tag, "bg-success"] if tag == :test
87
-
88
- [tag, "bg-secondary"]
89
- end
85
+ tags.map do |tag|
86
+ if tag == :test
87
+ [tag, "bg-success"]
88
+ else
89
+ [tag, "bg-secondary"]
90
+ end
91
+ end.to_h
90
92
  end
91
93
  end
92
94
  end
@@ -54,7 +54,7 @@ class DeprecationCollector
54
54
  return [
55
55
  404,
56
56
  { "content-type" => "text/plain", "x-cascade" => "pass" },
57
- ["Not Found #{env["REQUEST_METHOD"].inspect} #{env[PATH_INFO].inspect}"]
57
+ ["Not Found #{env['REQUEST_METHOD'].inspect} #{env[PATH_INFO].inspect}"]
58
58
  ]
59
59
  end
60
60
 
@@ -178,12 +178,10 @@ class DeprecationCollector
178
178
 
179
179
  def render_template(template, &block)
180
180
  template_target = template.gsub(/\.slim\z/, ".template.rb")
181
- template_method_name = "_template_#{template_target.gsub(/[^\w]/, "_")}"
181
+ template_method_name = "_template_#{template_target.gsub(/[^\w]/, '_')}"
182
182
  template_filename = File.join(VIEW_PATH, template_target.to_s)
183
183
 
184
- if ENV["DEPRECATION_COLLECTOR_RELOAD_WEB_TEMPLATES"]
185
- _recompile_template(template, template_filename, template_method_name)
186
- end
184
+ _recompile_template(template, template_filename, template_method_name) if _recompile_enabled?
187
185
 
188
186
  _load_template(template_filename, template_method_name) unless respond_to?(template_method_name)
189
187
 
@@ -202,6 +200,10 @@ class DeprecationCollector
202
200
  ActionContext.class_eval(src, template_filename.gsub(/\.template\.rb\z/, ".slim"), 1)
203
201
  end
204
202
 
203
+ def _recompile_enabled?
204
+ ENV["DEPRECATION_COLLECTOR_RELOAD_WEB_TEMPLATES"]
205
+ end
206
+
205
207
  def _recompile_template(template, template_filename, template_method_name)
206
208
  original_template_name = File.join(VIEW_PATH, template.to_s)
207
209
  puts "Recompiling #{original_template_name}"
@@ -0,0 +1,12 @@
1
+ header
2
+ h1 Import dump
3
+
4
+ main
5
+ form method="post" action=import_deprecations_path enctype="multipart/form-data"
6
+ .mb-3
7
+ label.form-label> for="file" Choose a JSON file to upload:
8
+ input.form-comtrol type="file" name="file" id="file"
9
+ .mb-3
10
+ button.btn.btn-primary> type="submit" Upload
11
+ a.btn.btn-secondary> href=dump_deprecations_path Dump
12
+ a.btn.btn-secondary> href=deprecations_path Back
@@ -1,4 +1,4 @@
1
- _buf = ''; _buf << ("<header><h1>Import dump</h1></header><main><form".freeze);
1
+ _buf = ''.dup; _buf << ("<header><h1>Import dump</h1></header><main><form".freeze);
2
2
  ;
3
3
  ;
4
4
  ;
@@ -0,0 +1,121 @@
1
+ header.mb-3
2
+ h1 Deprecations
3
+
4
+ a.btn.btn-primary>(data-method="post" href=deprecation_path(:trigger) rel="nofollow") Trigger a couple
5
+ a.btn.btn-danger>(data-method="delete" data-confirm="Sure?" href=deprecation_path(:all) rel="nofollow")
6
+ i.bi.bi-trash
7
+ ' Delete all
8
+
9
+ - if DeprecationCollector.instance.storage.support_disabling?
10
+ - if DeprecationCollector.instance.storage.enabled?
11
+ a.btn.btn-danger>(
12
+ data-method="delete" href=disable_deprecations_path rel="nofollow"
13
+ data-confirm="Sure? Will need to restart workers after enabling"
14
+ ) Disable
15
+ - else
16
+ a.btn.btn-secondary>(
17
+ data-method="post" href=enable_deprecations_path rel="nofollow"
18
+ ) Turn on (after workers restart)
19
+
20
+ main
21
+ table.table.table-striped
22
+ tr
23
+ th Count
24
+ th Message
25
+ th Location
26
+ th Ruby/Rails
27
+
28
+ - total = 0
29
+ - by_realm = Hash.new(0)
30
+ - deprecations.each do |deprecation|
31
+ - total += 1
32
+ - by_realm[deprecation[:realm]] += 1
33
+ tr data-digest=deprecation[:digest]
34
+ td
35
+ a href=deprecation_path(deprecation[:digest]) = deprecation[:count]
36
+ br
37
+ - deprecation_tags(deprecation).each_pair do |tag, cls|
38
+ .badge> class=cls = tag
39
+
40
+ td
41
+ ruby:
42
+ msg = deprecation[:message].to_s
43
+ delete_prefixes = Gem.path + [defined?(Rails) && Rails.root.to_s].compact
44
+ delete_prefixes.each { |path| msg = msg.gsub(path, '') }
45
+ msg.delete_prefix! deprecation[:gem_traceline].gsub(/:in .+/, ':') if deprecation[:gem_traceline]
46
+ msg.delete_prefix! deprecation[:app_traceline].gsub(/:in .+/, ':') if deprecation[:app_traceline]
47
+ msg.strip!
48
+ msg.delete_prefix!("DEPRECATION WARNING: ")
49
+ msg.delete_prefix!("warning: ")
50
+
51
+ - if msg.lines.size > 2
52
+ pre.pre-scrollable.p-1(style="overflow: auto; max-width: 700px; max-height: 200px; font-size: 11px")
53
+ code = msg
54
+ - else
55
+ .msg = msg
56
+ - if deprecation.dig(:notes, :comment)
57
+ = deprecation.dig(:notes, :comment)
58
+
59
+ - if deprecation.dig(:context, :action)
60
+ i.small.controller = deprecation.dig(:context, :action)
61
+ - elsif deprecation.dig(:context, :params, :controller)
62
+ i.small.controller = deprecation.dig(:context, :params).slice(:controller, :action).values.join('#')
63
+
64
+ td.small
65
+ - if deprecation[:gem_traceline]
66
+ .gem_location
67
+ - location, function = deprecation[:gem_traceline].split(':in `', 2)
68
+ - full_gemname = location.delete_prefix('/gems/').gsub(%r{/.*}, '')
69
+ - location_in_gem = location.delete_prefix("/gems/#{full_gemname}/")
70
+ i>= full_gemname
71
+ code.code_location> data-copy-value=location_in_gem = location_in_gem.delete_prefix('lib/')
72
+ i= function.delete_suffix("'")
73
+ - if deprecation[:app_traceline]
74
+ .app_location
75
+ - location, function = deprecation[:app_traceline].split(':in `', 2)
76
+ code.code_location>= location
77
+ i= function.delete_suffix("'")
78
+ td
79
+ .small.ruby = deprecation[:ruby_version]
80
+ .small.rails = deprecation[:rails_version]
81
+
82
+ a href=deprecation_path(deprecation[:digest]) data-method="delete" data-confirm="Delete?" rel="nofollow" title="Delete"
83
+ i.bi.bi-trash
84
+ - if total.zero?
85
+ tr
86
+ td colspan=4
87
+ p Looks like there're no deprecations (or workers have not yet wrote to redis)
88
+ p
89
+ ' You can try
90
+ a href=deprecation_path(:trigger) data-method="post" rel="nofollow" trigger a couple
91
+ - if import_enabled?
92
+ |> , or
93
+ a> href=import_deprecations_path import
94
+
95
+
96
+ footer
97
+ - if total > 3
98
+ => total
99
+ ' deprecations
100
+ - by_realm.each_pair do |realm, count|
101
+ a.btn.btn-sm.btn-outline-secondary> href="?realm=#{realm}"
102
+ => realm
103
+ .badge> class=(count == 0 ? 'bg-success' : 'bg-secondary') = count
104
+ - if params[:realm] && params[:realm] != ''
105
+ a.btn.btn-sm.btn-outline-primary> href="?realm="
106
+ ' Other realms
107
+
108
+ css:
109
+ .code_location {
110
+ cursor: pointer;
111
+ }
112
+
113
+ javascript:
114
+ document.querySelectorAll('.code_location').forEach(function(elem){
115
+ elem.addEventListener('click', function () {
116
+ let textToCopy = elem.getAttribute('data-copy-value');
117
+ if(!textToCopy) textToCopy = elem.innerText;
118
+ console.log("Copying", textToCopy)
119
+ navigator.clipboard.writeText(textToCopy);
120
+ }, false);
121
+ });
@@ -1,4 +1,4 @@
1
- _buf = ''; _buf << ("<header class=\"mb-3\"><h1>Deprecations</h1><a class=\"btn btn-primary\" data-method=\"post\"".freeze);
1
+ _buf = ''.dup; _buf << ("<header class=\"mb-3\"><h1>Deprecations</h1><a class=\"btn btn-primary\" data-method=\"post\"".freeze);
2
2
  ;
3
3
  ;
4
4
  ; _slim_codeattributes1 = deprecation_path(:trigger); if _slim_codeattributes1; if _slim_codeattributes1 == true; _buf << (" href=\"\"".freeze); else; _buf << (" href=\"".freeze); _buf << ((::Temple::Utils.escape_html((_slim_codeattributes1))).to_s); _buf << ("\"".freeze); end; end; _buf << (" rel=\"nofollow\">Trigger a couple</a> <a class=\"btn btn-danger\" data-confirm=\"Sure?\" data-method=\"delete\"".freeze);
@@ -27,7 +27,7 @@ _buf = ''; _buf << ("<header class=\"mb-3\"><h1>Deprecations</h1><a class=\"btn
27
27
  ;
28
28
  ; total = 0;
29
29
  ; by_realm = Hash.new(0);
30
- ; @deprecations.each do |deprecation|;
30
+ ; deprecations.each do |deprecation|;
31
31
  ; total += 1;
32
32
  ; by_realm[deprecation[:realm]] += 1;
33
33
  ; _buf << ("<tr".freeze); _slim_codeattributes5 = deprecation[:digest]; if _slim_codeattributes5; if _slim_codeattributes5 == true; _buf << (" data-digest=\"\"".freeze); else; _buf << (" data-digest=\"".freeze); _buf << ((::Temple::Utils.escape_html((_slim_codeattributes5))).to_s); _buf << ("\"".freeze); end; end; _buf << ("><td><a".freeze);
@@ -35,13 +35,13 @@ _buf = ''; _buf << ("<header class=\"mb-3\"><h1>Deprecations</h1><a class=\"btn
35
35
  ; _slim_codeattributes6 = deprecation_path(deprecation[:digest]); if _slim_codeattributes6; if _slim_codeattributes6 == true; _buf << (" href=\"\"".freeze); else; _buf << (" href=\"".freeze); _buf << ((::Temple::Utils.escape_html((_slim_codeattributes6))).to_s); _buf << ("\"".freeze); end; end; _buf << (">".freeze); _buf << ((::Temple::Utils.escape_html((deprecation[:count]))).to_s);
36
36
  ; _buf << ("</a><br />".freeze);
37
37
  ; deprecation_tags(deprecation).each_pair do |tag, cls|;
38
- ; _buf << ("<div".freeze); _temple_html_attributeremover1 = ''; _temple_html_attributemerger1 = []; _temple_html_attributemerger1[0] = "badge"; _temple_html_attributemerger1[1] = ''; _slim_codeattributes7 = cls; if Array === _slim_codeattributes7; _slim_codeattributes7 = _slim_codeattributes7.flatten; _slim_codeattributes7.map!(&:to_s); _slim_codeattributes7.reject!(&:empty?); _temple_html_attributemerger1[1] << ((::Temple::Utils.escape_html((_slim_codeattributes7.join(" ")))).to_s); else; _temple_html_attributemerger1[1] << ((::Temple::Utils.escape_html((_slim_codeattributes7))).to_s); end; _temple_html_attributemerger1[1]; _temple_html_attributeremover1 << ((_temple_html_attributemerger1.reject(&:empty?).join(" ")).to_s); _temple_html_attributeremover1; if !_temple_html_attributeremover1.empty?; _buf << (" class=\"".freeze); _buf << ((_temple_html_attributeremover1).to_s); _buf << ("\"".freeze); end; _buf << (">".freeze); _buf << ((::Temple::Utils.escape_html((tag))).to_s);
38
+ ; _buf << ("<div".freeze); _temple_html_attributeremover1 = ''.dup; _temple_html_attributemerger1 = []; _temple_html_attributemerger1[0] = "badge"; _temple_html_attributemerger1[1] = ''.dup; _slim_codeattributes7 = cls; if Array === _slim_codeattributes7; _slim_codeattributes7 = _slim_codeattributes7.flatten; _slim_codeattributes7.map!(&:to_s); _slim_codeattributes7.reject!(&:empty?); _temple_html_attributemerger1[1] << ((::Temple::Utils.escape_html((_slim_codeattributes7.join(" ")))).to_s); else; _temple_html_attributemerger1[1] << ((::Temple::Utils.escape_html((_slim_codeattributes7))).to_s); end; _temple_html_attributemerger1[1]; _temple_html_attributeremover1 << ((_temple_html_attributemerger1.reject(&:empty?).join(" ")).to_s); _temple_html_attributeremover1; if !_temple_html_attributeremover1.empty?; _buf << (" class=\"".freeze); _buf << ((_temple_html_attributeremover1).to_s); _buf << ("\"".freeze); end; _buf << (">".freeze); _buf << ((::Temple::Utils.escape_html((tag))).to_s);
39
39
  ;
40
40
  ; _buf << ("</div> ".freeze); end; _buf << ("</td><td>".freeze);
41
41
  ;
42
- ; msg = deprecation[:message]
42
+ ; msg = deprecation[:message].to_s
43
43
  delete_prefixes = Gem.path + [defined?(Rails) && Rails.root.to_s].compact
44
- delete_prefixes.each { |path| msg.gsub!(path, '') }
44
+ delete_prefixes.each { |path| msg = msg.gsub(path, '') }
45
45
  msg.delete_prefix! deprecation[:gem_traceline].gsub(/:in .+/, ':') if deprecation[:gem_traceline]
46
46
  msg.delete_prefix! deprecation[:app_traceline].gsub(/:in .+/, ':') if deprecation[:app_traceline]
47
47
  msg.strip!
@@ -100,7 +100,7 @@ msg.delete_prefix!("warning: ")
100
100
  ; end; by_realm.each_pair do |realm, count|;
101
101
  ; _buf << ("<a class=\"btn btn-sm btn-outline-secondary\" href=\"?realm=".freeze); _buf << ((::Temple::Utils.escape_html((realm))).to_s); _buf << ("\">".freeze);
102
102
  ; _buf << ((::Temple::Utils.escape_html((realm))).to_s);
103
- ; _buf << (" <div".freeze); _temple_html_attributeremover2 = ''; _temple_html_attributemerger2 = []; _temple_html_attributemerger2[0] = "badge"; _temple_html_attributemerger2[1] = ''; _slim_codeattributes13 = (count == 0 ? 'bg-success' : 'bg-secondary'); if Array === _slim_codeattributes13; _slim_codeattributes13 = _slim_codeattributes13.flatten; _slim_codeattributes13.map!(&:to_s); _slim_codeattributes13.reject!(&:empty?); _temple_html_attributemerger2[1] << ((::Temple::Utils.escape_html((_slim_codeattributes13.join(" ")))).to_s); else; _temple_html_attributemerger2[1] << ((::Temple::Utils.escape_html((_slim_codeattributes13))).to_s); end; _temple_html_attributemerger2[1]; _temple_html_attributeremover2 << ((_temple_html_attributemerger2.reject(&:empty?).join(" ")).to_s); _temple_html_attributeremover2; if !_temple_html_attributeremover2.empty?; _buf << (" class=\"".freeze); _buf << ((_temple_html_attributeremover2).to_s); _buf << ("\"".freeze); end; _buf << (">".freeze); _buf << ((::Temple::Utils.escape_html((count))).to_s);
103
+ ; _buf << (" <div".freeze); _temple_html_attributeremover2 = ''.dup; _temple_html_attributemerger2 = []; _temple_html_attributemerger2[0] = "badge"; _temple_html_attributemerger2[1] = ''.dup; _slim_codeattributes13 = (count == 0 ? 'bg-success' : 'bg-secondary'); if Array === _slim_codeattributes13; _slim_codeattributes13 = _slim_codeattributes13.flatten; _slim_codeattributes13.map!(&:to_s); _slim_codeattributes13.reject!(&:empty?); _temple_html_attributemerger2[1] << ((::Temple::Utils.escape_html((_slim_codeattributes13.join(" ")))).to_s); else; _temple_html_attributemerger2[1] << ((::Temple::Utils.escape_html((_slim_codeattributes13))).to_s); end; _temple_html_attributemerger2[1]; _temple_html_attributeremover2 << ((_temple_html_attributemerger2.reject(&:empty?).join(" ")).to_s); _temple_html_attributeremover2; if !_temple_html_attributeremover2.empty?; _buf << (" class=\"".freeze); _buf << ((_temple_html_attributeremover2).to_s); _buf << ("\"".freeze); end; _buf << (">".freeze); _buf << ((::Temple::Utils.escape_html((count))).to_s);
104
104
  ; _buf << ("</div> </a> ".freeze); end; if params[:realm] && params[:realm] != '';
105
105
  ; _buf << ("<a class=\"btn btn-sm btn-outline-primary\" href=\"?realm=\">Other realms </a> ".freeze);
106
106
  ;
@@ -0,0 +1,90 @@
1
+ doctype html
2
+ html data-bs-theme=current_color_theme lang="en"
3
+ head
4
+ meta charset="utf-8"
5
+ meta http-equiv="X-UA-Compatible" content="IE=edge;chrome=1"
6
+
7
+ title="Deprecations"
8
+ meta name="description" content="Deprecation collector ui"
9
+ meta name="viewport" content="width=device-width"
10
+
11
+ link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" integrity="sha256-Fu5/PVNGJlC70y4mPEjA6nWVdPz2IMaBrXGQCJEsRho= sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous"
12
+ link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.2/font/bootstrap-icons.css" integrity="sha256-4RctOgogjPAdwGbwq+rxfwAmSpZhWaafcZR9btzUk18= sha384-b6lVK+yci+bfDmaY1u0zE8YYJt0TZxLEAFyYSLHId4xoVvsrQu3INevFKo+Xir8e" crossorigin="anonymous"
13
+
14
+ body
15
+ .container-fluid== yield
16
+
17
+ script async=true src="https://cdn.jsdelivr.net/npm/@rails/ujs@7.0.4-3/lib/assets/compiled/rails-ujs.js" integrity="sha256-9Mbe9mGA7d+AJbnz0O6CoLGabCbe6JAYnGshvGIADiE=" crossorigin="anonymous"
18
+ script async=true src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.min.js" integrity="sha256-QucgBAKNM4KKPJHqTfH8e+JON1G/gmPPqtMmBb+wHpc= sha384-Y4oOpwW3duJdCWv5ly8SCFYWqFDsfob/3GkgExXKV4idmbt98QcxXYs9UoXAB7BZ" crossorigin="anonymous"
19
+
20
+ javascript:
21
+ (() => {
22
+ 'use strict'
23
+
24
+ const storedTheme = localStorage.getItem('theme')
25
+
26
+ const getPreferredTheme = () => {
27
+ if (storedTheme) { return storedTheme }
28
+ let fromServer = document.documentElement.getAttribute('data-bs-theme')
29
+ if(fromServer) { return fromServer }
30
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
31
+ }
32
+
33
+ const setTheme = function (theme) {
34
+ if (theme === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches) {
35
+ document.documentElement.setAttribute('data-bs-theme', 'dark')
36
+ } else {
37
+ document.documentElement.setAttribute('data-bs-theme', theme)
38
+ }
39
+ }
40
+
41
+ setTheme(getPreferredTheme())
42
+
43
+ const showActiveTheme = (theme, focus = false) => {
44
+ const themeSwitcher = document.querySelector('#bd-theme')
45
+
46
+ if (!themeSwitcher) {
47
+ return
48
+ }
49
+
50
+ const themeSwitcherText = document.querySelector('#bd-theme-text')
51
+ const activeThemeIcon = document.querySelector('.theme-icon-active use')
52
+ const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`)
53
+ const svgOfActiveBtn = btnToActive.querySelector('svg use').getAttribute('href')
54
+
55
+ document.querySelectorAll('[data-bs-theme-value]').forEach(element => {
56
+ element.classList.remove('active')
57
+ element.setAttribute('aria-pressed', 'false')
58
+ })
59
+
60
+ btnToActive.classList.add('active')
61
+ btnToActive.setAttribute('aria-pressed', 'true')
62
+ activeThemeIcon.setAttribute('href', svgOfActiveBtn)
63
+ const themeSwitcherLabel = `${themeSwitcherText.textContent} (${btnToActive.dataset.bsThemeValue})`
64
+ themeSwitcher.setAttribute('aria-label', themeSwitcherLabel)
65
+
66
+ if (focus) {
67
+ themeSwitcher.focus()
68
+ }
69
+ }
70
+
71
+ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
72
+ if (storedTheme !== 'light' || storedTheme !== 'dark') {
73
+ setTheme(getPreferredTheme())
74
+ }
75
+ })
76
+
77
+ window.addEventListener('DOMContentLoaded', () => {
78
+ showActiveTheme(getPreferredTheme())
79
+
80
+ document.querySelectorAll('[data-bs-theme-value]')
81
+ .forEach(toggle => {
82
+ toggle.addEventListener('click', () => {
83
+ const theme = toggle.getAttribute('data-bs-theme-value')
84
+ localStorage.setItem('theme', theme)
85
+ setTheme(theme)
86
+ showActiveTheme(theme, true)
87
+ })
88
+ })
89
+ })
90
+ })()
@@ -1,4 +1,4 @@
1
- _buf = ''; _buf << ("<!DOCTYPE html><html".freeze);
1
+ _buf = ''.dup; _buf << ("<!DOCTYPE html><html".freeze);
2
2
  ; _slim_codeattributes1 = current_color_theme; if _slim_codeattributes1; if _slim_codeattributes1 == true; _buf << (" data-bs-theme=\"\"".freeze); else; _buf << (" data-bs-theme=\"".freeze); _buf << ((::Temple::Utils.escape_html((_slim_codeattributes1))).to_s); _buf << ("\"".freeze); end; end; _buf << (" lang=\"en\"><head><meta charset=\"utf-8\" /><meta content=\"IE=edge;chrome=1\" http-equiv=\"X-UA-Compatible\" /><title>".freeze);
3
3
  ;
4
4
  ;