log_bench 0.1.4 → 0.1.5

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: f5ade69636de4054fff54cdb450391a0c238da4349b60be90b3d8faecd80040b
4
- data.tar.gz: 7e28cf786b5ba7a54487da0ce6581a24a713eb1449f12127dfd922a7cf294e4f
3
+ metadata.gz: eb8aba2b715d494f771f7bf0b1dda90719ae1a72e36823e86832f6ccbd7966b3
4
+ data.tar.gz: e7f5a7f4322778e74a4de3f33603c5de448e2e021c78e1506cf97507226ffa80
5
5
  SHA512:
6
- metadata.gz: e2199487aae647d6ea13619a6cc8506e65b2e26d569581b16151cbcde0bad0a674241d463b0d04771c378716e0c55ce3547f4a8d17978e8157925e6ecb0f8850
7
- data.tar.gz: c9d41acd6b3497fcef99e326aed6655f02837c2fc6f27c9b2777aa7a76280a953202086566b29aafe5661011053ab61b05dd41d0c563b88daa399ff17a08d7ed
6
+ metadata.gz: 966d732d29bdbeaf30f1428b1f26126733120d2922c4a711a74e629fe78eae088702a51a6d20c311f097e98484bb93334a5eb8387b412b31449934e205013604
7
+ data.tar.gz: 628a28c650da6ed06b837397e3f79c09fc115ad36c2082ebb8dc9207a87e27034b04696a8d3462b2bb6be278a611ae08909ee4ecac3782509dce908dea5dda45
data/exe/log_bench CHANGED
@@ -72,6 +72,7 @@ rescue => e
72
72
  puts " - Log file doesn't exist or is empty"
73
73
  puts " - Lograge not configured (see README.md for setup)"
74
74
  puts " - Log format not supported (LogBench requires lograge JSON format)"
75
+ puts " Error backtrace: \n #{e.backtrace.join("\n")}"
75
76
  puts
76
77
  puts "For help: log_bench --help"
77
78
  exit 1
@@ -17,9 +17,10 @@ module LogBench
17
17
  # UI constants
18
18
  DEFAULT_VISIBLE_HEIGHT = 20
19
19
 
20
- def initialize(state, screen)
20
+ def initialize(state, screen, renderer = nil)
21
21
  self.state = state
22
22
  self.screen = screen
23
+ self.renderer = renderer
23
24
  self.mouse_handler = MouseHandler.new(state, screen)
24
25
  end
25
26
 
@@ -27,6 +28,11 @@ module LogBench
27
28
  ch = getch
28
29
  return if ch == -1 || ch.nil?
29
30
 
31
+ # Check if update modal should handle input first
32
+ if renderer&.handle_modal_input(ch)
33
+ return
34
+ end
35
+
30
36
  if ch == KEY_MOUSE
31
37
  mouse_handler.handle_mouse_input
32
38
  elsif filter_mode_active?
@@ -38,7 +44,7 @@ module LogBench
38
44
 
39
45
  private
40
46
 
41
- attr_accessor :state, :screen, :mouse_handler
47
+ attr_accessor :state, :screen, :renderer, :mouse_handler
42
48
 
43
49
  def filter_mode_active?
44
50
  state.filter_mode || state.detail_filter_mode
@@ -11,7 +11,7 @@ module LogBench
11
11
  DEFAULT_LOG_PATHS = %w[log/development.log].freeze
12
12
 
13
13
  # Timing
14
- MAIN_LOOP_SLEEP_INTERVAL = 0.05
14
+ MAIN_LOOP_SLEEP_INTERVAL = 1.0 / 1000 # 1ms
15
15
 
16
16
  # Error messages
17
17
  LOG_FILE_NOT_FOUND = "Error: No log file found at %s!"
@@ -27,6 +27,7 @@ module LogBench
27
27
  setup_screen
28
28
  setup_components
29
29
  load_initial_data
30
+ check_for_updates
30
31
  initial_draw
31
32
  start_monitoring
32
33
  main_loop
@@ -55,8 +56,8 @@ module LogBench
55
56
  end
56
57
 
57
58
  def setup_components
58
- self.input_handler = InputHandler.new(state, screen)
59
59
  self.renderer = Renderer::Main.new(screen, state)
60
+ self.input_handler = InputHandler.new(state, screen, renderer)
60
61
  end
61
62
 
62
63
  def load_initial_data
@@ -64,6 +65,11 @@ module LogBench
64
65
  state.requests = log_file.requests
65
66
  end
66
67
 
68
+ def check_for_updates
69
+ latest_version = VersionChecker.check_for_update
70
+ state.set_update_available(latest_version) if latest_version
71
+ end
72
+
67
73
  def initial_draw
68
74
  renderer.draw
69
75
  end
@@ -40,11 +40,9 @@ module LogBench
40
40
  request_index = click_to_request_index(y)
41
41
  return unless request_index
42
42
 
43
- # Update selection
44
43
  max_index = state.filtered_requests.size - 1
45
44
  state.selected = [request_index, max_index].min
46
45
  state.auto_scroll = false
47
- state.adjust_scroll_for_selection(visible_height)
48
46
  elsif click_in_right_pane?(x, y)
49
47
  # Switch to right pane
50
48
  state.switch_to_right_pane unless state.right_pane_focused?
@@ -12,18 +12,27 @@ module LogBench
12
12
  self.header = Header.new(screen, state)
13
13
  self.request_list = RequestList.new(screen, state, scrollbar)
14
14
  self.details = Details.new(screen, state, scrollbar, ansi_renderer)
15
+ self.update_modal = UpdateModal.new(screen, state)
15
16
  end
16
17
 
17
18
  def draw
18
- header.draw
19
- request_list.draw
20
- details.draw
21
- screen.refresh_all
19
+ if update_modal.should_show?
20
+ update_modal.draw
21
+ else
22
+ header.draw
23
+ request_list.draw
24
+ details.draw
25
+ screen.refresh_all
26
+ end
27
+ end
28
+
29
+ def handle_modal_input(ch)
30
+ update_modal.handle_input(ch)
22
31
  end
23
32
 
24
33
  private
25
34
 
26
- attr_accessor :screen, :state, :header, :scrollbar, :request_list, :ansi_renderer, :details
35
+ attr_accessor :screen, :state, :header, :scrollbar, :request_list, :ansi_renderer, :details, :update_modal
27
36
  end
28
37
  end
29
38
  end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LogBench
4
+ module App
5
+ module Renderer
6
+ class UpdateModal
7
+ include Curses
8
+
9
+ # Modal dimensions
10
+ MODAL_WIDTH = 40
11
+ MODAL_HEIGHT = 7
12
+ COUNTDOWN_SECONDS = 5
13
+
14
+ # Color constants
15
+ HEADER_CYAN = 1
16
+ SUCCESS_GREEN = 3
17
+ WARNING_YELLOW = 4
18
+
19
+ def initialize(screen, state)
20
+ self.screen = screen
21
+ self.state = state
22
+ self.countdown = COUNTDOWN_SECONDS
23
+ self.modal_win = nil
24
+ self.last_countdown_update = Time.now
25
+ self.dismissed = false
26
+ end
27
+
28
+ def should_show?
29
+ state.update_available? && !dismissed
30
+ end
31
+
32
+ def draw
33
+ return unless should_show?
34
+
35
+ create_modal_window
36
+ update_countdown_timer
37
+ draw_content
38
+ modal_win&.refresh
39
+ end
40
+
41
+ def handle_input(ch)
42
+ return false unless should_show?
43
+
44
+ # Any key dismisses the modal
45
+ if ch != -1
46
+ dismiss_modal
47
+ return true
48
+ end
49
+
50
+ false
51
+ end
52
+
53
+ private
54
+
55
+ attr_accessor :screen, :state, :countdown, :modal_win, :last_countdown_update, :dismissed
56
+
57
+ def create_modal_window
58
+ return if modal_win
59
+
60
+ # Calculate center position
61
+ center_y = (screen.height - MODAL_HEIGHT) / 2
62
+ center_x = (screen.width - MODAL_WIDTH) / 2
63
+
64
+ # Create modal window
65
+ self.modal_win = Window.new(MODAL_HEIGHT, MODAL_WIDTH, center_y, center_x)
66
+ end
67
+
68
+ def draw_content
69
+ return unless modal_win
70
+
71
+ modal_win.erase
72
+ modal_win.box(0, 0)
73
+
74
+ # Header
75
+ modal_win.setpos(1, 2)
76
+ modal_win.attron(color_pair(HEADER_CYAN) | A_BOLD) { modal_win.addstr("🚀 LogBench Update Available!") }
77
+
78
+ # Version info
79
+ modal_win.setpos(3, 2)
80
+ modal_win.addstr("Current: ")
81
+ modal_win.attron(color_pair(SUCCESS_GREEN)) { modal_win.addstr(LogBench::VERSION) }
82
+ modal_win.addstr(" → Latest: ")
83
+ modal_win.attron(color_pair(SUCCESS_GREEN) | A_BOLD) { modal_win.addstr(state.update_version) }
84
+
85
+ # Instructions with countdown
86
+ modal_win.setpos(5, 2)
87
+ modal_win.addstr("Press any key to continue or wait ")
88
+ modal_win.attron(color_pair(WARNING_YELLOW) | A_BOLD) { modal_win.addstr("#{countdown}s") }
89
+ end
90
+
91
+ def update_countdown_timer
92
+ now = Time.now
93
+ if now - last_countdown_update >= 1.0
94
+ self.countdown -= 1
95
+ self.last_countdown_update = now
96
+
97
+ if countdown <= 0
98
+ dismiss_modal
99
+ end
100
+ end
101
+ end
102
+
103
+ def dismiss_modal
104
+ state.dismiss_update_notification
105
+ modal_win&.close
106
+ self.modal_win = nil
107
+ clear
108
+ end
109
+
110
+ def color_pair(n)
111
+ screen.color_pair(n)
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -4,7 +4,7 @@ module LogBench
4
4
  module App
5
5
  class State
6
6
  attr_reader :main_filter, :sort, :detail_filter
7
- attr_accessor :requests, :auto_scroll, :scroll_offset, :selected, :detail_scroll_offset, :text_selection_mode
7
+ attr_accessor :requests, :auto_scroll, :scroll_offset, :selected, :detail_scroll_offset, :text_selection_mode, :update_available, :update_version
8
8
 
9
9
  def initialize
10
10
  self.requests = []
@@ -18,6 +18,8 @@ module LogBench
18
18
  self.main_filter = Filter.new
19
19
  self.detail_filter = Filter.new
20
20
  self.sort = Sort.new
21
+ self.update_available = false
22
+ self.update_version = nil
21
23
  end
22
24
 
23
25
  def running?
@@ -40,6 +42,20 @@ module LogBench
40
42
  text_selection_mode
41
43
  end
42
44
 
45
+ def set_update_available(version)
46
+ self.update_available = true
47
+ self.update_version = version
48
+ end
49
+
50
+ def dismiss_update_notification
51
+ self.update_available = false
52
+ self.update_version = nil
53
+ end
54
+
55
+ def update_available?
56
+ update_available
57
+ end
58
+
43
59
  def clear_filter
44
60
  main_filter.clear
45
61
  self.selected = 0
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LogBench
4
- VERSION = "0.1.4"
4
+ VERSION = "0.1.5"
5
5
  end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "json"
5
+ require "fileutils"
6
+
7
+ module LogBench
8
+ class VersionChecker
9
+ # Cache file location
10
+ CACHE_DIR = File.expand_path("~/.cache/log_bench")
11
+ CACHE_FILE = File.join(CACHE_DIR, "version_check.json")
12
+
13
+ # Cache duration (24 hours)
14
+ CACHE_DURATION = 24 * 60 * 60
15
+
16
+ # RubyGems API endpoint
17
+ RUBYGEMS_API_URL = "https://rubygems.org/api/v1/gems/log_bench.json"
18
+
19
+ # Timeout for HTTP requests
20
+ REQUEST_TIMEOUT = 5
21
+
22
+ def self.check_for_update
23
+ new.check_for_update
24
+ end
25
+
26
+ def check_for_update
27
+ return nil unless should_check?
28
+
29
+ latest_version = fetch_latest_version
30
+ return nil unless latest_version
31
+
32
+ update_cache(latest_version)
33
+
34
+ if newer_version_available?(latest_version)
35
+ latest_version
36
+ end
37
+ rescue
38
+ # Silently fail - don't interrupt the user experience
39
+ nil
40
+ end
41
+
42
+ private
43
+
44
+ def should_check?
45
+ return true unless File.exist?(CACHE_FILE)
46
+
47
+ cache_data = read_cache
48
+ return true unless cache_data
49
+
50
+ # Check if cache is expired
51
+ Time.now - Time.parse(cache_data["checked_at"]) > CACHE_DURATION
52
+ rescue
53
+ true
54
+ end
55
+
56
+ def fetch_latest_version
57
+ uri = URI(RUBYGEMS_API_URL)
58
+
59
+ Net::HTTP.start(uri.host, uri.port, use_ssl: true, read_timeout: REQUEST_TIMEOUT) do |http|
60
+ request = Net::HTTP::Get.new(uri)
61
+ response = http.request(request)
62
+
63
+ return nil unless response.code == "200"
64
+
65
+ data = JSON.parse(response.body)
66
+ data["version"]
67
+ end
68
+ rescue
69
+ nil
70
+ end
71
+
72
+ def read_cache
73
+ return nil unless File.exist?(CACHE_FILE)
74
+
75
+ JSON.parse(File.read(CACHE_FILE))
76
+ rescue
77
+ nil
78
+ end
79
+
80
+ def update_cache(latest_version)
81
+ FileUtils.mkdir_p(CACHE_DIR)
82
+
83
+ cache_data = {
84
+ "latest_version" => latest_version,
85
+ "checked_at" => Time.now.iso8601
86
+ }
87
+
88
+ File.write(CACHE_FILE, JSON.pretty_generate(cache_data))
89
+ rescue
90
+ # Ignore cache write errors
91
+ nil
92
+ end
93
+
94
+ def newer_version_available?(latest_version)
95
+ Gem::Version.new(latest_version) > Gem::Version.new(LogBench::VERSION)
96
+ rescue
97
+ false
98
+ end
99
+ end
100
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: log_bench
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benjamín Silva
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-06-07 00:00:00.000000000 Z
10
+ date: 2025-06-09 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: zeitwerk
@@ -51,6 +51,20 @@ dependencies:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
53
  version: '0.14'
54
+ - !ruby/object:Gem::Dependency
55
+ name: net-http
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.6'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.6'
54
68
  - !ruby/object:Gem::Dependency
55
69
  name: rake
56
70
  requirement: !ruby/object:Gem::Requirement
@@ -121,6 +135,7 @@ files:
121
135
  - lib/log_bench/app/renderer/main.rb
122
136
  - lib/log_bench/app/renderer/request_list.rb
123
137
  - lib/log_bench/app/renderer/scrollbar.rb
138
+ - lib/log_bench/app/renderer/update_modal.rb
124
139
  - lib/log_bench/app/screen.rb
125
140
  - lib/log_bench/app/sort.rb
126
141
  - lib/log_bench/app/state.rb
@@ -135,6 +150,7 @@ files:
135
150
  - lib/log_bench/log/request.rb
136
151
  - lib/log_bench/railtie.rb
137
152
  - lib/log_bench/version.rb
153
+ - lib/log_bench/version_checker.rb
138
154
  - lib/tasks/log_bench.rake
139
155
  - logbench-preview.png
140
156
  homepage: https://github.com/silva96/log_bench