deprecation_collector 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 60dd6c4c552576ae19c4cb2a84232c5f7e6b83b0d8fb8e11b3a98bc5abf69bdf
4
- data.tar.gz: f9f638c079489229bed7e525caaed0fb995b19ceff250146644d405462739e39
3
+ metadata.gz: e119a79e2c879e9e0ec02476523620eab84c6ae4c354d382207cd8be6bde7a9a
4
+ data.tar.gz: e2f7f495ee705f5dada2456b425fa739a9a67b801dce461c86e5f4a04c6266d0
5
5
  SHA512:
6
- metadata.gz: b80b9bcfdbbccaf112b447f3ab04960f042ce98c74b53b35b76e9c4f3740a37fa1cc894ce632e7206c76bcb7eaaf8e13f596bcaa53398eb611fcd245a8b77214
7
- data.tar.gz: 35992cd452d01b83a5884299873bd2d80c2e5d42f8977d77b72e0635f24d5bda475200eed936e74102181205575d62e511085606af3868b059377780e9efc8e1
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.1"
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,13 +62,15 @@ 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? { |method| deprecation[:message]}
71
+ %w[trigger_kwargs_error_warning trigger_rails_deprecation].any? do |method|
72
+ deprecation[:message].to_s.include?(method)
73
+ end
72
74
  end
73
75
 
74
76
  def deprecation_tags(deprecation)
@@ -80,11 +82,13 @@ class DeprecationCollector
80
82
  tags << deprecation[:realm] if deprecation[:realm] && deprecation[:realm] != "rails"
81
83
  tags.merge(deprecation.dig(:notes, :tags) || [])
82
84
 
83
- tags.to_h do |tag|
84
- next [tag, "bg-success"] if tag == :test
85
-
86
- [tag, "bg-secondary"]
87
- 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
88
92
  end
89
93
  end
90
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
  ;