catpm 0.6.0 → 0.6.2

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: 72b1f8ad18b14d62b8ace369a8d064396ff4fc2988f1dc4eb911c71278c05766
4
- data.tar.gz: f6153a851adc6ff5dd810cf648b069b512117bd25ae87f6e7715382ea6c43960
3
+ metadata.gz: eac4dbe3e1304875bb2ead1573dc51e3acd760b8dcc18901c122d8004b58e976
4
+ data.tar.gz: c2a94022740f767cebdf44a98e737e495f182ada282528f145ab36deac28daca
5
5
  SHA512:
6
- metadata.gz: 485696711db8a1ed56901065ee90b151292a466c4a21bf6585e63e36a0ee6f873a14584b229f1963bd9fa14ac5143d4fbc3641763177dec0ec2fc5c5421ba11d
7
- data.tar.gz: c6e0dd6391af5921e4fa79ad77a69fe7d637adf77f714cf19eb66182dbac128c7c45d75ee1ac7365620824afc34d6672d788169c230c72d8ac0af27460167eff
6
+ metadata.gz: 499d38cbc6db1313bfb9f77a23476a450617024efbeb0de15c77c33819bf9844d45bbfe3525d8d1b849897a925113aeb4ca93e826f9da49bdd23d0d710bb8f6a
7
+ data.tar.gz: 7db602ccd776bb8882e634c25845ac8792e50644afc72c7c17759ccc701eb4785664f84fc0ec17b46de97142df3a095247b18bbbed000a2f637cd68b12cdc315
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  gem build catpm.gemspec
2
- gem push catpm-0.5.0.gem
2
+ gem push catpm-0.6.0.gem
3
3
 
4
4
  # Catpm
5
5
 
Binary file
@@ -28,7 +28,7 @@ module Catpm
28
28
 
29
29
  @total_count = scope.count
30
30
  @page = [params[:page].to_i, 1].max
31
- @errors = scope.order(@sort => @dir).offset((@page - 1) * PER_PAGE).limit(PER_PAGE)
31
+ @errors = scope.order(pinned: :desc, @sort => @dir).offset((@page - 1) * PER_PAGE).limit(PER_PAGE)
32
32
  end
33
33
 
34
34
  def show
@@ -106,19 +106,41 @@ module Catpm
106
106
  def resolve
107
107
  error = Catpm::ErrorRecord.find(params[:id])
108
108
  error.resolve!
109
- redirect_to catpm.error_path(error), notice: 'Marked as resolved'
109
+ if request.xhr?
110
+ render json: { resolved: true }
111
+ else
112
+ redirect_to catpm.error_path(error), notice: 'Marked as resolved'
113
+ end
110
114
  end
111
115
 
112
116
  def unresolve
113
117
  error = Catpm::ErrorRecord.find(params[:id])
114
118
  error.unresolve!
115
- redirect_to catpm.error_path(error), notice: 'Reopened'
119
+ if request.xhr?
120
+ render json: { resolved: false }
121
+ else
122
+ redirect_to catpm.error_path(error), notice: 'Reopened'
123
+ end
124
+ end
125
+
126
+ def toggle_pin
127
+ error = Catpm::ErrorRecord.find(params[:id])
128
+ error.update!(pinned: !error.pinned)
129
+ if request.xhr?
130
+ render json: { pinned: error.pinned }
131
+ else
132
+ redirect_back fallback_location: catpm.error_path(error)
133
+ end
116
134
  end
117
135
 
118
136
  def destroy
119
137
  error = Catpm::ErrorRecord.find(params[:id])
120
138
  error.destroy!
121
- redirect_to catpm.errors_path, notice: 'Error deleted'
139
+ if request.xhr?
140
+ render json: { deleted: true }
141
+ else
142
+ redirect_to catpm.errors_path, notice: 'Error deleted'
143
+ end
122
144
  end
123
145
 
124
146
  def resolve_all
@@ -64,9 +64,10 @@ module Catpm
64
64
  # Sort (pinned always on top)
65
65
  @sort = %w[name total_count last_seen].include?(params[:sort]) ? params[:sort] : 'total_count'
66
66
  @dir = params[:dir] == 'asc' ? 'asc' : 'desc'
67
- events_list = events_list.sort_by { |e| e[@sort.to_sym] || '' }
68
- events_list = events_list.reverse if @dir == 'desc'
69
- events_list = events_list.sort_by { |e| e[:pinned] ? 0 : 1 }
67
+ sorted = events_list.sort_by { |e| e[@sort.to_sym] || '' }
68
+ sorted = sorted.reverse if @dir == 'desc'
69
+ pinned, unpinned = sorted.partition { |e| e[:pinned] }
70
+ events_list = pinned + unpinned
70
71
 
71
72
  @total_event_names = events_list.size
72
73
 
@@ -153,6 +154,16 @@ module Catpm
153
154
  end
154
155
  end
155
156
 
157
+ def destroy_sample
158
+ sample = Catpm::EventSample.find(params[:sample_id])
159
+ sample.destroy
160
+ if request.xhr?
161
+ render json: { deleted: true }
162
+ else
163
+ redirect_back fallback_location: catpm.events_path, notice: 'Sample deleted'
164
+ end
165
+ end
166
+
156
167
  def ignored
157
168
  @range, period, _bucket_seconds = helpers.parse_range(remembered_range)
158
169
  ignored_prefs = Catpm::EventPref.ignored
@@ -17,7 +17,9 @@ module Catpm
17
17
  sample = Catpm::Sample.find(params[:id])
18
18
  bucket = sample.bucket
19
19
  sample.destroy
20
- if bucket
20
+ if request.xhr?
21
+ render json: { deleted: true }
22
+ elsif bucket
21
23
  redirect_to catpm.endpoint_path(kind: bucket.kind, target: bucket.target, operation: bucket.operation), notice: 'Sample deleted'
22
24
  else
23
25
  redirect_to catpm.status_index_path, notice: 'Sample deleted'
@@ -85,9 +85,10 @@ module Catpm
85
85
  # Server-side sort (pinned always on top)
86
86
  @sort = %w[target total_count avg_duration max_duration total_failures last_seen].include?(params[:sort]) ? params[:sort] : 'last_seen'
87
87
  @dir = params[:dir] == 'asc' ? 'asc' : 'desc'
88
- endpoints = endpoints.sort_by { |e| e[@sort.to_sym] || '' }
89
- endpoints = endpoints.reverse if @dir == 'desc'
90
- endpoints = endpoints.sort_by { |e| e[:pinned] ? 0 : 1 }
88
+ sorted = endpoints.sort_by { |e| e[@sort.to_sym] || '' }
89
+ sorted = sorted.reverse if @dir == 'desc'
90
+ pinned, unpinned = sorted.partition { |e| e[:pinned] }
91
+ endpoints = pinned + unpinned
91
92
 
92
93
  @total_endpoint_count = endpoints.size
93
94
 
@@ -9,6 +9,7 @@ module Catpm
9
9
  @config = Catpm.config
10
10
  @oldest_bucket = Catpm::Bucket.minimum(:bucket_start)
11
11
  @active_error_count = Catpm::ErrorRecord.unresolved.count
12
+ @table_sizes = Catpm::Adapter.current.table_sizes
12
13
  end
13
14
  end
14
15
  end
@@ -10,6 +10,7 @@ module Catpm
10
10
  scope :by_kind, ->(kind) { where(kind: kind) }
11
11
  scope :unresolved, -> { where(resolved_at: nil) }
12
12
  scope :resolved, -> { where.not(resolved_at: nil) }
13
+ scope :pinned, -> { where(pinned: true) }
13
14
  scope :recent, ->(period = 24.hours) { where(last_occurred_at: period.ago..) }
14
15
 
15
16
  def resolved?
@@ -7,6 +7,7 @@
7
7
  <th>Duration</th>
8
8
  <th>Segments</th>
9
9
  <th>Time</th>
10
+ <th></th>
10
11
  </tr>
11
12
  </thead>
12
13
  <tbody>
@@ -24,6 +25,12 @@
24
25
  <% end %>
25
26
  </td>
26
27
  <td><%= time_with_tooltip(s.recorded_at) %></td>
28
+ <td style="position:relative; z-index:2; width:28px; padding:4px 2px; text-align:center">
29
+ <button class="action-menu-btn">&#x22EE;</button>
30
+ <div class="action-menu">
31
+ <button class="menu-danger" data-action="delete" data-url="<%= catpm.sample_path(s) %>">Delete</button>
32
+ </div>
33
+ </td>
27
34
  </tr>
28
35
  <% end %>
29
36
  </tbody>
@@ -27,6 +27,7 @@
27
27
  <table id="errors-table">
28
28
  <thead>
29
29
  <tr>
30
+ <th></th>
30
31
  <th>Kind</th>
31
32
  <th><%= sort_header("Error Class", "error_class", @sort, @dir, extra_params: extra) %></th>
32
33
  <th>Message</th>
@@ -34,11 +35,13 @@
34
35
  <th>Trend</th>
35
36
  <th><%= sort_header("Last Seen", "last_occurred_at", @sort, @dir, extra_params: extra) %></th>
36
37
  <% if @tab == "resolved" %><th>Resolved</th><% end %>
38
+ <th></th>
37
39
  </tr>
38
40
  </thead>
39
41
  <tbody>
40
42
  <% @errors.each do |e| %>
41
43
  <tr class="linked">
44
+ <td style="position:relative; z-index:1; width:28px; padding:4px 2px; text-align:center"><button class="star-btn<%= ' pinned' if e.pinned %>" data-pin-url="<%= catpm.toggle_pin_error_path(e) %>"><%= e.pinned ? "&#x2605;".html_safe : "&#x2606;".html_safe %></button></td>
42
45
  <td><a href="<%= catpm.error_path(e) %>" class="row-link"><%= type_badge(e.kind) %></a></td>
43
46
  <td class="mono"><%= e.error_class %></td>
44
47
  <td style="color:var(--text-1)"><%= truncate(e.message, length: 60) %></td>
@@ -46,6 +49,17 @@
46
49
  <td><%= trend_indicator(e) %></td>
47
50
  <td><%= time_with_tooltip(e.last_occurred_at) %></td>
48
51
  <% if @tab == "resolved" %><td><%= time_with_tooltip(e.resolved_at) %></td><% end %>
52
+ <td style="position:relative; z-index:2; width:28px; padding:4px 2px; text-align:center">
53
+ <button class="action-menu-btn">&#x22EE;</button>
54
+ <div class="action-menu">
55
+ <% if e.resolved? %>
56
+ <button data-action="unresolve" data-url="<%= catpm.unresolve_error_path(e) %>">Reopen</button>
57
+ <% else %>
58
+ <button data-action="resolve" data-url="<%= catpm.resolve_error_path(e) %>">Resolve</button>
59
+ <% end %>
60
+ <button class="menu-danger" data-action="delete" data-url="<%= catpm.error_path(e) %>">Delete</button>
61
+ </div>
62
+ </td>
49
63
  </tr>
50
64
  <% end %>
51
65
  </tbody>
@@ -106,17 +106,24 @@
106
106
  <th>Status</th>
107
107
  <th>Target</th>
108
108
  <th>Segments</th>
109
+ <th></th>
109
110
  </tr>
110
111
  </thead>
111
112
  <tbody>
112
113
  <% @samples.each do |sample| %>
113
114
  <% ctx = sample.parsed_context %>
114
- <tr class="clickable-row" onclick="window.location='<%= catpm.sample_path(sample) %>';" style="cursor:pointer">
115
- <td><%= time_with_tooltip(sample.recorded_at) %></td>
115
+ <tr class="linked">
116
+ <td><a href="<%= catpm.sample_path(sample) %>" class="row-link"><%= time_with_tooltip(sample.recorded_at) %></a></td>
116
117
  <td class="mono"><%= format_duration(sample.duration) %></td>
117
118
  <td><%= status_badge(ctx["status"] || ctx[:status]) %></td>
118
119
  <td class="mono"><%= sample.bucket&.target || "—" %></td>
119
120
  <td class="mono text-muted"><%= segment_count_summary(ctx["segment_summary"] || ctx[:segment_summary]).presence || "—" %></td>
121
+ <td style="position:relative; z-index:2; width:28px; padding:4px 2px; text-align:center">
122
+ <button class="action-menu-btn">&#x22EE;</button>
123
+ <div class="action-menu">
124
+ <button class="menu-danger" data-action="delete" data-url="<%= catpm.sample_path(sample) %>">Delete</button>
125
+ </div>
126
+ </td>
120
127
  </tr>
121
128
  <% end %>
122
129
  </tbody>
@@ -63,11 +63,13 @@
63
63
  <colgroup>
64
64
  <col style="width:120px">
65
65
  <col>
66
+ <col style="width:36px">
66
67
  </colgroup>
67
68
  <thead>
68
69
  <tr>
69
70
  <th>Recorded At</th>
70
71
  <th>Payload</th>
72
+ <th></th>
71
73
  </tr>
72
74
  </thead>
73
75
  <tbody>
@@ -87,6 +89,12 @@
87
89
  <span class="text-muted">—</span>
88
90
  <% end %>
89
91
  </td>
92
+ <td style="position:relative; z-index:2; width:28px; padding:4px 2px; text-align:center; vertical-align:top">
93
+ <button class="action-menu-btn">&#x22EE;</button>
94
+ <div class="action-menu">
95
+ <button class="menu-danger" data-action="delete" data-url="<%= catpm.destroy_sample_events_path(sample_id: sample.id) %>">Delete</button>
96
+ </div>
97
+ </td>
90
98
  </tr>
91
99
  <% end %>
92
100
  </tbody>
@@ -43,6 +43,53 @@
43
43
  </div>
44
44
  </div>
45
45
 
46
+ <%# ─── Storage ─── %>
47
+ <h2>Database Storage</h2>
48
+ <%= section_description("Disk space used by catpm tables in your database.") %>
49
+
50
+ <% total_bytes = @table_sizes.sum { |t| t[:total_bytes].to_i } %>
51
+ <% has_bytes = @table_sizes.any? { |t| t[:total_bytes] } %>
52
+
53
+ <% if has_bytes %>
54
+ <div class="storage-total">
55
+ Total: <strong><%= number_to_human_size(total_bytes) %></strong>
56
+ </div>
57
+ <% end %>
58
+
59
+ <div class="config-table">
60
+ <div class="table-scroll">
61
+ <table>
62
+ <thead>
63
+ <tr>
64
+ <th>Table</th>
65
+ <th style="text-align:right">Rows</th>
66
+ <% if has_bytes %>
67
+ <th style="text-align:right">Size</th>
68
+ <% end %>
69
+ </tr>
70
+ </thead>
71
+ <tbody>
72
+ <% @table_sizes.each do |t| %>
73
+ <tr>
74
+ <td class="mono"><%= t[:name] %></td>
75
+ <td class="mono" style="text-align:right"><%= number_with_delimiter(t[:row_estimate].to_i) %></td>
76
+ <% if has_bytes %>
77
+ <td class="mono" style="text-align:right"><%= number_to_human_size(t[:total_bytes].to_i) %></td>
78
+ <% end %>
79
+ </tr>
80
+ <% end %>
81
+ <% if has_bytes %>
82
+ <tr style="font-weight:600;border-top:2px solid var(--border)">
83
+ <td>Total</td>
84
+ <td class="mono" style="text-align:right"><%= number_with_delimiter(@table_sizes.sum { |t| t[:row_estimate].to_i }) %></td>
85
+ <td class="mono" style="text-align:right"><%= number_to_human_size(total_bytes) %></td>
86
+ </tr>
87
+ <% end %>
88
+ </tbody>
89
+ </table>
90
+ </div>
91
+ </div>
92
+
46
93
  <%# ─── Configuration ─── %>
47
94
  <h2>Configuration</h2>
48
95
  <%= section_description("Current catpm settings. Configure via initializer.") %>
@@ -3,6 +3,10 @@
3
3
  <head>
4
4
  <title>catpm<%= " — #{yield :title}" if content_for?(:title) %></title>
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <link rel="icon" type="image/png" sizes="32x32" href="<%= image_path('catpm/favicon-32.png') %>">
7
+ <link rel="icon" type="image/png" sizes="512x512" href="<%= image_path('catpm/favicon-512.png') %>">
8
+ <link rel="icon" type="image/x-icon" href="<%= image_path('catpm/favicon.ico') %>">
9
+ <link rel="apple-touch-icon" href="<%= image_path('catpm/favicon-512.png') %>">
6
10
  <%= csrf_meta_tags %>
7
11
  <%= yield :head_extra if content_for?(:head_extra) %>
8
12
  <style>
@@ -159,6 +163,9 @@
159
163
  .sample-sidebar { flex: 0 0 320px; }
160
164
  @media (max-width: 900px) { .sample-layout { flex-direction: column; } .sample-sidebar { flex: auto; width: 100%; } }
161
165
 
166
+ /* ─── Storage ─── */
167
+ .storage-total { font-size: 14px; color: var(--text-1); margin-bottom: 12px; }
168
+
162
169
  /* ─── Config Table ─── */
163
170
  .config-table { background: var(--bg-1); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
164
171
  .config-table table { margin: 0; }
@@ -541,10 +548,14 @@
541
548
  if (action === 'ignore') {
542
549
  apiRequest(url, 'PATCH').then(function(data) { if (data.ignored && row) fadeOutRow(row); });
543
550
  } else if (action === 'delete') {
544
- if (!confirm('Delete this endpoint and all its data?')) return;
551
+ if (!confirm('Delete this and all its data?')) return;
545
552
  apiRequest(url, 'DELETE').then(function(data) { if (data.deleted && row) fadeOutRow(row); });
546
553
  } else if (action === 'unignore') {
547
554
  apiRequest(url, 'PATCH').then(function(data) { if (!data.ignored && row) fadeOutRow(row); });
555
+ } else if (action === 'resolve') {
556
+ apiRequest(url, 'PATCH').then(function(data) { if (data.resolved && row) fadeOutRow(row); });
557
+ } else if (action === 'unresolve') {
558
+ apiRequest(url, 'PATCH').then(function(data) { if (!data.resolved && row) fadeOutRow(row); });
548
559
  }
549
560
  return;
550
561
  }
data/config/routes.rb CHANGED
@@ -10,7 +10,11 @@ Catpm::Engine.routes.draw do
10
10
  patch 'endpoint/ignore', to: 'endpoints#toggle_ignore', as: :endpoint_ignore
11
11
  get 'endpoints/ignored', to: 'endpoints#ignored', as: :ignored_endpoints
12
12
  resources :samples, only: [:show, :destroy]
13
- resources :events, only: [:index, :show, :destroy], param: :name
13
+ resources :events, only: [:index, :show, :destroy], param: :name do
14
+ collection do
15
+ delete 'samples/:sample_id', to: 'events#destroy_sample', as: :destroy_sample
16
+ end
17
+ end
14
18
  patch 'events/:name/pin', to: 'events#toggle_pin', as: :event_pin
15
19
  patch 'events/:name/ignore', to: 'events#toggle_ignore', as: :event_ignore
16
20
  get 'events_ignored', to: 'events#ignored', as: :ignored_events
@@ -21,6 +25,7 @@ Catpm::Engine.routes.draw do
21
25
  member do
22
26
  patch :resolve
23
27
  patch :unresolve
28
+ patch :toggle_pin
24
29
  end
25
30
  end
26
31
  end
@@ -50,6 +50,7 @@ class CreateCatpmTables < ActiveRecord::Migration[8.0]
50
50
  t.json :contexts
51
51
  t.json :occurrence_buckets
52
52
  t.datetime :resolved_at
53
+ t.boolean :pinned, null: false, default: false
53
54
  end
54
55
 
55
56
  add_index :catpm_errors, :fingerprint, unique: true, name: 'idx_catpm_errors_fingerprint'
@@ -49,6 +49,10 @@ module Catpm
49
49
  raise NotImplementedError
50
50
  end
51
51
 
52
+ def table_sizes
53
+ raise NotImplementedError
54
+ end
55
+
52
56
  def merge_metadata_sum(existing, incoming)
53
57
  existing = parse_json(existing)
54
58
  incoming = parse_json(incoming)
@@ -182,6 +182,23 @@ module Catpm
182
182
  "EXTRACT(EPOCH FROM bucket_start)::integer % #{interval.to_i} = 0"
183
183
  end
184
184
 
185
+ def table_sizes
186
+ ActiveRecord::Base.connection_pool.with_connection do |conn|
187
+ rows = conn.select_all(<<~SQL)
188
+ SELECT c.relname AS name,
189
+ pg_total_relation_size(c.oid) AS total_bytes,
190
+ pg_table_size(c.oid) AS table_bytes,
191
+ pg_indexes_size(c.oid) AS index_bytes,
192
+ c.reltuples::bigint AS row_estimate
193
+ FROM pg_class c
194
+ JOIN pg_namespace n ON n.oid = c.relnamespace
195
+ WHERE c.relname LIKE 'catpm_%' AND c.relkind = 'r' AND n.nspname = 'public'
196
+ ORDER BY pg_total_relation_size(c.oid) DESC
197
+ SQL
198
+ rows.map(&:symbolize_keys)
199
+ end
200
+ end
201
+
185
202
  private
186
203
 
187
204
  def advisory_lock_key(identifier)
@@ -152,6 +152,35 @@ module Catpm
152
152
  "CAST(strftime('%s', bucket_start) AS INTEGER) % #{interval.to_i} = 0"
153
153
  end
154
154
 
155
+ def table_sizes
156
+ conn = ActiveRecord::Base.connection
157
+
158
+ tables = conn.select_values(
159
+ "SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'catpm_%' ORDER BY name"
160
+ )
161
+
162
+ has_dbstat = begin
163
+ conn.select_value("SELECT 1 FROM dbstat LIMIT 1")
164
+ true
165
+ rescue StandardError
166
+ false
167
+ end
168
+
169
+ tables.map do |table|
170
+ row_count = conn.select_value("SELECT COUNT(*) FROM \"#{table}\"").to_i
171
+
172
+ total_bytes = if has_dbstat
173
+ objects = [table] + conn.select_values(
174
+ "SELECT name FROM sqlite_master WHERE type='index' AND tbl_name=#{conn.quote(table)}"
175
+ )
176
+ placeholders = objects.map { |n| conn.quote(n) }.join(',')
177
+ conn.select_value("SELECT SUM(pgsize) FROM dbstat WHERE name IN (#{placeholders})").to_i
178
+ end
179
+
180
+ { name: table, total_bytes: total_bytes, row_estimate: row_count }
181
+ end.sort_by { |r| -(r[:total_bytes] || 0) }
182
+ end
183
+
155
184
  private
156
185
 
157
186
  def with_write_lock(&block)
data/lib/catpm/engine.rb CHANGED
@@ -16,9 +16,9 @@ module Catpm
16
16
 
17
17
  if Catpm.config.instrument_middleware_stack
18
18
  app = Rails.application
19
- names = app.middleware.filter_map { |m| m.name }.reject { |n| n.start_with?('Catpm::') }
20
- names.reverse_each do |name|
21
- app.middleware.insert_before(name, Catpm::MiddlewareProbe, name)
19
+ middlewares = app.middleware.reject { |m| m.name&.start_with?('Catpm::') }
20
+ middlewares.reverse_each do |middleware|
21
+ app.middleware.insert_before(middleware, Catpm::MiddlewareProbe, middleware.name)
22
22
  rescue ArgumentError, RuntimeError
23
23
  # Middleware not found in stack — skip
24
24
  end
@@ -53,6 +53,7 @@ module Catpm
53
53
  duration: elapsed_ms(env),
54
54
  started_at: Time.current,
55
55
  status: 500,
56
+ sample_type: 'error',
56
57
  error_class: exception.class.name,
57
58
  error_message: exception.message,
58
59
  backtrace: exception.backtrace,
data/lib/catpm/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Catpm
4
- VERSION = '0.6.0'
4
+ VERSION = '0.6.2'
5
5
  end
@@ -35,6 +35,13 @@ namespace :catpm do
35
35
  puts '[catpm] catpm_endpoint_prefs table already exists, skipping'
36
36
  end
37
37
 
38
+ unless connection.column_exists?(:catpm_errors, :pinned)
39
+ connection.add_column :catpm_errors, :pinned, :boolean, null: false, default: false
40
+ puts '[catpm] Added pinned column to catpm_errors'
41
+ else
42
+ puts '[catpm] catpm_errors.pinned already exists, skipping'
43
+ end
44
+
38
45
  unless connection.table_exists?(:catpm_event_prefs)
39
46
  connection.create_table :catpm_event_prefs do |t|
40
47
  t.string :name, null: false
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: catpm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - ''
@@ -33,6 +33,9 @@ files:
33
33
  - MIT-LICENSE
34
34
  - README.md
35
35
  - Rakefile
36
+ - app/assets/images/catpm/favicon-32.png
37
+ - app/assets/images/catpm/favicon-512.png
38
+ - app/assets/images/catpm/favicon.ico
36
39
  - app/assets/stylesheets/catpm/application.css
37
40
  - app/controllers/catpm/application_controller.rb
38
41
  - app/controllers/catpm/endpoints_controller.rb