rediscover 0.0.1
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.
- data/bin/rediscover +6 -0
- data/lib/rediscover/app.rb +57 -0
- data/lib/rediscover/dialog/confirm.rb +12 -0
- data/lib/rediscover/dialog/error.rb +12 -0
- data/lib/rediscover/exception_handler.rb +21 -0
- data/lib/rediscover/frame/connect.rb +44 -0
- data/lib/rediscover/frame/create_key.rb +56 -0
- data/lib/rediscover/frame/main.rb +59 -0
- data/lib/rediscover/icon.rb +12 -0
- data/lib/rediscover/icons/add.png +0 -0
- data/lib/rediscover/icons/application.png +0 -0
- data/lib/rediscover/icons/key_add.png +0 -0
- data/lib/rediscover/icons/refresh.png +0 -0
- data/lib/rediscover/icons/server_connect.png +0 -0
- data/lib/rediscover/key_list_ctrl.rb +117 -0
- data/lib/rediscover/key_viewer.rb +17 -0
- data/lib/rediscover/list_element_list.rb +29 -0
- data/lib/rediscover/panel/browser.rb +44 -0
- data/lib/rediscover/panel/key_list.rb +89 -0
- data/lib/rediscover/panel/list_view.rb +72 -0
- data/lib/rediscover/panel/server.rb +42 -0
- data/lib/rediscover/panel/set_view.rb +55 -0
- data/lib/rediscover/panel/sorted_set_view.rb +55 -0
- data/lib/rediscover/panel/string_view.rb +50 -0
- data/lib/rediscover/set_element_list.rb +70 -0
- data/lib/rediscover/sorted_set_element_list.rb +70 -0
- data/lib/rediscover.rb +4 -0
- metadata +100 -0
data/bin/rediscover
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'forwardable'
|
3
|
+
require 'rediscover/exception_handler'
|
4
|
+
require 'rediscover/icon'
|
5
|
+
require 'rediscover/frame/connect'
|
6
|
+
require 'rediscover/frame/main'
|
7
|
+
require 'rediscover/dialog/error'
|
8
|
+
require 'rediscover/dialog/confirm'
|
9
|
+
|
10
|
+
module Rediscover
|
11
|
+
class App < Wx::App
|
12
|
+
|
13
|
+
DEFAULTS = {
|
14
|
+
:host => 'localhost',
|
15
|
+
:port => 6379
|
16
|
+
}
|
17
|
+
|
18
|
+
attr_reader :logger, :host, :port, :redis
|
19
|
+
|
20
|
+
def on_init
|
21
|
+
@logger = Logger.new(STDOUT)
|
22
|
+
ExceptionHandler.set_logger(@logger)
|
23
|
+
@host = DEFAULTS[:host]
|
24
|
+
@port = DEFAULTS[:port]
|
25
|
+
|
26
|
+
display_connect
|
27
|
+
end
|
28
|
+
|
29
|
+
def display_connect
|
30
|
+
@connect_frame = Frame::Connect.new
|
31
|
+
end
|
32
|
+
|
33
|
+
def display_main
|
34
|
+
@main_frame = Frame::Main.new
|
35
|
+
end
|
36
|
+
|
37
|
+
def connect(host, port)
|
38
|
+
begin
|
39
|
+
@redis = Redis.new(:host => host, :port => port)
|
40
|
+
@redis.connect_to_server # connection errors will be rescued by Frame::Connect
|
41
|
+
@host, @port = host, port
|
42
|
+
@connect_frame.close
|
43
|
+
rescue => e
|
44
|
+
logger.error(e)
|
45
|
+
@conn_refused_dlg = Dialog::Error.new(@connect_frame,
|
46
|
+
"Can't connect to server.",
|
47
|
+
'Unable to Connect')
|
48
|
+
@conn_refused_dlg.show_modal
|
49
|
+
return
|
50
|
+
end
|
51
|
+
|
52
|
+
@logger.info(@redis.to_s)
|
53
|
+
display_main
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Rediscover
|
2
|
+
class ExceptionHandler
|
3
|
+
|
4
|
+
def self.set_logger(logger)
|
5
|
+
@@logger = logger
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.modal(window, exception)
|
9
|
+
self.log(exception)
|
10
|
+
dlg = Rediscover::Dialog::Error.new(window,
|
11
|
+
'An error occurred: ' + exception.to_s,
|
12
|
+
'Error')
|
13
|
+
dlg.show_modal
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.log(exception)
|
17
|
+
@@logger.error(exception)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Rediscover
|
2
|
+
module Frame
|
3
|
+
|
4
|
+
class Connect < Wx::Frame
|
5
|
+
include Wx
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@app = get_app
|
9
|
+
super(nil, -1, 'Connect to Redis')
|
10
|
+
|
11
|
+
set_icon(Rediscover::Icon.new('server_connect'))
|
12
|
+
|
13
|
+
setup_panel
|
14
|
+
show
|
15
|
+
end
|
16
|
+
|
17
|
+
def setup_panel
|
18
|
+
@panel = Wx::Panel.new(self)
|
19
|
+
@host_label = StaticText.new(@panel, :label => 'Host')
|
20
|
+
@host_textbox = TextCtrl.new(@panel, :value => @app.host)
|
21
|
+
@port_label = StaticText.new(@panel, :label => 'Port')
|
22
|
+
@port_textbox = TextCtrl.new(@panel, :value => @app.port.to_s)
|
23
|
+
@connect_button = Button.new(@panel, :label => 'Connect')
|
24
|
+
|
25
|
+
evt_button @connect_button, :connect_button_evt
|
26
|
+
|
27
|
+
@panel_sizer = BoxSizer.new(VERTICAL)
|
28
|
+
@panel.set_sizer(@panel_sizer)
|
29
|
+
@panel_sizer.add(@host_label, 0, GROW|ALL, 2)
|
30
|
+
@panel_sizer.add(@host_textbox, 0, GROW|ALL, 2)
|
31
|
+
@panel_sizer.add(@port_label, 0, GROW|ALL, 2)
|
32
|
+
@panel_sizer.add(@port_textbox, 0, GROW|ALL, 2)
|
33
|
+
@panel_sizer.add(@connect_button, 0, GROW|ALL, 2)
|
34
|
+
end
|
35
|
+
|
36
|
+
def connect_button_evt
|
37
|
+
host = @host_textbox.get_value
|
38
|
+
port = @port_textbox.get_value.to_i
|
39
|
+
@app.connect(host, port)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Rediscover
|
2
|
+
module Frame
|
3
|
+
class CreateKey < Wx::Frame
|
4
|
+
include Wx
|
5
|
+
|
6
|
+
def initialize(window)
|
7
|
+
@redis = get_app.redis
|
8
|
+
super(window, -1, 'Create a Key')
|
9
|
+
|
10
|
+
set_icon(Rediscover::Icon.new('key_add'))
|
11
|
+
|
12
|
+
setup_panel
|
13
|
+
show
|
14
|
+
end
|
15
|
+
|
16
|
+
def setup_panel
|
17
|
+
@panel = Wx::Panel.new(self)
|
18
|
+
@key_label = StaticText.new(@panel, :label => 'Key')
|
19
|
+
@key_textbox = TextCtrl.new(@panel)
|
20
|
+
@value_label = StaticText.new(@panel, :label => 'Value')
|
21
|
+
@value_textbox = TextCtrl.new(@panel)
|
22
|
+
@create_button = Button.new(@panel, :label => 'Create')
|
23
|
+
|
24
|
+
evt_button @create_button, :create_evt
|
25
|
+
|
26
|
+
@panel_sizer = BoxSizer.new(VERTICAL)
|
27
|
+
@panel.set_sizer(@panel_sizer)
|
28
|
+
@panel_sizer.add(@key_label, 0, GROW|ALL, 2)
|
29
|
+
@panel_sizer.add(@key_textbox, 0, GROW|ALL, 2)
|
30
|
+
@panel_sizer.add(@value_label, 0, GROW|ALL, 2)
|
31
|
+
@panel_sizer.add(@value_textbox, 0, GROW|ALL, 2)
|
32
|
+
@panel_sizer.add(@create_button, 0, GROW|ALL, 2)
|
33
|
+
end
|
34
|
+
|
35
|
+
def on_create(&block)
|
36
|
+
@on_create_block = block
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_evt
|
40
|
+
key = @key_textbox.get_value
|
41
|
+
value = @value_textbox.get_value
|
42
|
+
|
43
|
+
begin
|
44
|
+
@redis[key] = value
|
45
|
+
rescue => e
|
46
|
+
ExceptionHandler.modal(self, e)
|
47
|
+
return
|
48
|
+
end
|
49
|
+
|
50
|
+
@on_create_block.call() if @on_create_block
|
51
|
+
close
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'rediscover/panel/server'
|
2
|
+
require 'rediscover/panel/browser'
|
3
|
+
require 'rediscover/panel/key_list'
|
4
|
+
require 'rediscover/key_viewer'
|
5
|
+
require 'rediscover/set_element_list'
|
6
|
+
require 'rediscover/sorted_set_element_list'
|
7
|
+
require 'rediscover/list_element_list'
|
8
|
+
require 'rediscover/panel/string_view'
|
9
|
+
require 'rediscover/panel/set_view'
|
10
|
+
require 'rediscover/panel/sorted_set_view'
|
11
|
+
require 'rediscover/panel/list_view'
|
12
|
+
require 'rediscover/frame/create_key'
|
13
|
+
|
14
|
+
module Rediscover
|
15
|
+
module Frame
|
16
|
+
class Main < Wx::Frame
|
17
|
+
include Wx
|
18
|
+
|
19
|
+
WINDOW_WIDTH = 600
|
20
|
+
WINDOW_HEIGHT = 400
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
super(nil, -1, 'Rediscover', DEFAULT_POSITION, Size.new(WINDOW_WIDTH, WINDOW_HEIGHT))
|
24
|
+
|
25
|
+
@redis = get_app.redis
|
26
|
+
@logger = get_app.logger
|
27
|
+
|
28
|
+
set_icon(Rediscover::Icon.new('application'))
|
29
|
+
|
30
|
+
setup_status_bar
|
31
|
+
setup_notebook
|
32
|
+
show
|
33
|
+
end
|
34
|
+
|
35
|
+
def setup_notebook
|
36
|
+
@notebook = Notebook.new(self)
|
37
|
+
|
38
|
+
@server_page = Panel::Server.new(@notebook)
|
39
|
+
@browser_page = Panel::Browser.new(@notebook)
|
40
|
+
@browser_page.on_status_change { |status| update_status_bar(status) }
|
41
|
+
|
42
|
+
@notebook.add_page(@server_page, 'Server Info', false)
|
43
|
+
@notebook.add_page(@browser_page, 'Key Browser', true)
|
44
|
+
end
|
45
|
+
|
46
|
+
def setup_status_bar
|
47
|
+
@status_bar = create_status_bar(2)
|
48
|
+
@status_bar.set_status_widths([-3, -1]) # set fields to variable widths
|
49
|
+
update_status_bar
|
50
|
+
end
|
51
|
+
|
52
|
+
def update_status_bar(status = nil)
|
53
|
+
@status_bar.set_status_text(@redis.to_s, 0) # connection info in left field
|
54
|
+
@status_bar.set_status_text(status, 1) if status # key count in right field
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module Rediscover
|
2
|
+
class KeyListCtrl < Wx::ListCtrl
|
3
|
+
include Wx
|
4
|
+
|
5
|
+
COLS = %w(Key Value Type TTL)
|
6
|
+
|
7
|
+
def initialize(window)
|
8
|
+
@redis = get_app.redis
|
9
|
+
@logger = get_app.logger
|
10
|
+
|
11
|
+
super(window, :style => LC_REPORT|LC_VIRTUAL)
|
12
|
+
|
13
|
+
i = 0
|
14
|
+
COLS.each do |name|
|
15
|
+
insert_column(i += 1, name)
|
16
|
+
end
|
17
|
+
set_column_width(0, 200)
|
18
|
+
set_column_width(1, 200)
|
19
|
+
|
20
|
+
evt_list_item_right_click self, :list_item_right_click_evt
|
21
|
+
end
|
22
|
+
|
23
|
+
def size
|
24
|
+
@keys.size
|
25
|
+
end
|
26
|
+
|
27
|
+
def on_get_keys(&block)
|
28
|
+
@on_get_keys_block = block
|
29
|
+
end
|
30
|
+
|
31
|
+
def update
|
32
|
+
@keys = @on_get_keys_block.call()
|
33
|
+
@logger.debug("KeyListCtrl loaded #{size} keys")
|
34
|
+
delete_all_items
|
35
|
+
set_item_count(size)
|
36
|
+
do_on_status_change
|
37
|
+
end
|
38
|
+
|
39
|
+
def delete(selections)
|
40
|
+
selections.each do |index|
|
41
|
+
key = @keys[index]
|
42
|
+
@redis.delete(key)
|
43
|
+
end
|
44
|
+
update
|
45
|
+
end
|
46
|
+
|
47
|
+
def on_get_item_text(item, column)
|
48
|
+
text = case column
|
49
|
+
when COLS.index('Key') then @keys[item]
|
50
|
+
when COLS.index('Value') then get_item_value(item)
|
51
|
+
when COLS.index('Type') then get_item_type(item)
|
52
|
+
when COLS.index('TTL') then @redis.ttl(@keys[item]).to_s
|
53
|
+
end
|
54
|
+
|
55
|
+
return text || ''
|
56
|
+
end
|
57
|
+
|
58
|
+
def get_item_type(item)
|
59
|
+
@redis.type?(@keys[item])
|
60
|
+
end
|
61
|
+
|
62
|
+
def get_item_value(item)
|
63
|
+
case get_item_type(item)
|
64
|
+
when 'string' then return @redis[@keys[item]]
|
65
|
+
when 'list' then return "#{@redis.list_length(@keys[item])} element(s)"
|
66
|
+
when 'set' then return "#{@redis.set_count(@keys[item])} element(s)"
|
67
|
+
when 'zset' then return "#{@redis.zset_count(@keys[item])} element(s)"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def list_item_right_click_evt(event)
|
72
|
+
menu = get_ctx_menu_for_selections(get_selections)
|
73
|
+
popup_menu(menu, event.get_point) if menu
|
74
|
+
end
|
75
|
+
|
76
|
+
def get_ctx_menu_for_selections(selections)
|
77
|
+
return nil if selections.empty?
|
78
|
+
@ctx_menu = Menu.new
|
79
|
+
|
80
|
+
if selections.size == 1 && @redis.type?(@keys[*selections]) != 'none'
|
81
|
+
@ctx_edit_item = MenuItem.new(@ctx_menu, -1, 'Edit')
|
82
|
+
evt_menu(@ctx_edit_item) { do_on_edit(*selections) }
|
83
|
+
end
|
84
|
+
|
85
|
+
# delete menu item
|
86
|
+
@ctx_delete_item = MenuItem.new(@ctx_menu, -1, 'Delete')
|
87
|
+
evt_menu(@ctx_delete_item) do
|
88
|
+
if ID_YES == Dialog::Confirm.new(self, 'Really delete selected key(s)?', 'Really delete?').show_modal
|
89
|
+
delete(selections)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
@ctx_menu.append_item(@ctx_edit_item) if @ctx_edit_item
|
94
|
+
@ctx_menu.append_item(@ctx_delete_item)
|
95
|
+
@ctx_menu
|
96
|
+
end
|
97
|
+
|
98
|
+
def on_edit(&block)
|
99
|
+
@on_edit_block = block
|
100
|
+
end
|
101
|
+
|
102
|
+
def do_on_edit(selection)
|
103
|
+
key = @keys[selection]
|
104
|
+
@on_edit_block.call(key, @redis.type?(key), selection)
|
105
|
+
end
|
106
|
+
|
107
|
+
def on_status_change(&block)
|
108
|
+
@on_status_change_block = block
|
109
|
+
do_on_status_change
|
110
|
+
end
|
111
|
+
|
112
|
+
def do_on_status_change
|
113
|
+
@on_status_change_block.call("#{size} keys") if @on_status_change_block
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module KeyViewer
|
2
|
+
def on_save(&block)
|
3
|
+
@on_save_block = block
|
4
|
+
end
|
5
|
+
|
6
|
+
def do_on_save
|
7
|
+
@on_save_block.call() if @on_save_block
|
8
|
+
end
|
9
|
+
|
10
|
+
def on_close(&block)
|
11
|
+
@on_close_block = block
|
12
|
+
end
|
13
|
+
|
14
|
+
def do_on_close
|
15
|
+
@on_close_block.call() if @on_close_block
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Rediscover
|
2
|
+
class ListElementList < Wx::ListCtrl
|
3
|
+
include Wx
|
4
|
+
|
5
|
+
def initialize(parent, key, elements)
|
6
|
+
@parent, @key = parent, key
|
7
|
+
@redis = get_app.redis
|
8
|
+
@logger = get_app.logger
|
9
|
+
|
10
|
+
super(parent, :style => LC_REPORT)
|
11
|
+
|
12
|
+
setup
|
13
|
+
set_elements(elements)
|
14
|
+
end
|
15
|
+
|
16
|
+
def setup
|
17
|
+
insert_column(0, 'Elements')
|
18
|
+
set_column_width(LIST_AUTOSIZE, -1)
|
19
|
+
end
|
20
|
+
|
21
|
+
def set_elements(elements)
|
22
|
+
delete_all_items
|
23
|
+
i = 0
|
24
|
+
elements.each { |element| insert_item(i, element); i += 1 }
|
25
|
+
@elements = elements
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Rediscover
|
2
|
+
module Panel
|
3
|
+
class Browser < Wx::SplitterWindow
|
4
|
+
include Wx
|
5
|
+
|
6
|
+
def initialize(parent)
|
7
|
+
@parent = parent
|
8
|
+
super(@parent, -1)
|
9
|
+
|
10
|
+
@redis = get_app.redis
|
11
|
+
@logger = get_app.logger
|
12
|
+
|
13
|
+
@key_list_panel = Panel::KeyList.new(self)
|
14
|
+
@key_list_panel.on_edit { |key, type, item| edit(key, type, item)}
|
15
|
+
|
16
|
+
init(@key_list_panel) # just show the key list initially
|
17
|
+
end
|
18
|
+
|
19
|
+
def edit(key, type, item)
|
20
|
+
unsplit(@view_panel) if @view_panel
|
21
|
+
|
22
|
+
@view_panel = get_view_panel(key, type)
|
23
|
+
@view_panel.on_save { @key_list_panel.refresh(item) }
|
24
|
+
@view_panel.on_close { unsplit(@view_panel) }
|
25
|
+
|
26
|
+
split_horizontally(@key_list_panel, @view_panel)
|
27
|
+
end
|
28
|
+
|
29
|
+
def get_view_panel(key, type)
|
30
|
+
case type
|
31
|
+
when 'string' then return Panel::StringView.new(self, key)
|
32
|
+
when 'set' then return Panel::SetView.new(self, key)
|
33
|
+
when 'zset' then return Panel::SortedSetView.new(self, key)
|
34
|
+
when 'list' then return Panel::ListView.new(self, key)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def on_status_change(&block)
|
39
|
+
@key_list_panel.on_status_change { |status| block.call(status) }
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'rediscover/key_list_ctrl'
|
2
|
+
|
3
|
+
module Rediscover
|
4
|
+
module Panel
|
5
|
+
class KeyList < Wx::Panel
|
6
|
+
include Wx
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def_delegators :@key_list, :on_edit
|
10
|
+
|
11
|
+
def initialize(parent)
|
12
|
+
@parent = parent
|
13
|
+
super(@parent, -1, :style => SUNKEN_BORDER)
|
14
|
+
|
15
|
+
@redis = get_app.redis
|
16
|
+
@logger = get_app.logger
|
17
|
+
@key_pattern = '*'
|
18
|
+
|
19
|
+
setup_sizer
|
20
|
+
setup_tool_bar
|
21
|
+
setup_key_browser
|
22
|
+
setup_key_shortcuts
|
23
|
+
end
|
24
|
+
|
25
|
+
def setup_sizer
|
26
|
+
@sizer = BoxSizer.new(VERTICAL)
|
27
|
+
set_sizer(@sizer)
|
28
|
+
end
|
29
|
+
|
30
|
+
def setup_tool_bar
|
31
|
+
@tool_bar_sizer = BoxSizer.new(HORIZONTAL)
|
32
|
+
@sizer.add_item(@tool_bar_sizer)
|
33
|
+
|
34
|
+
@add_button = Button.new(self, ID_ADD)
|
35
|
+
evt_button(@add_button) do
|
36
|
+
create_key_frame = Rediscover::Frame::CreateKey.new(self)
|
37
|
+
create_key_frame.on_create { refresh }
|
38
|
+
end
|
39
|
+
|
40
|
+
@refresh_button = Button.new(self, ID_REFRESH)
|
41
|
+
evt_button @refresh_button, :refresh
|
42
|
+
|
43
|
+
# filter input and button
|
44
|
+
@filter_textbox = TextCtrl.new(self,
|
45
|
+
:style => TE_PROCESS_ENTER,
|
46
|
+
:size => Size.new(200, -1))
|
47
|
+
evt_text_enter @filter_textbox, :filter # filter when enter is pressed in the textbox
|
48
|
+
@filter_button = Button.new(self, :label => 'Filter')
|
49
|
+
evt_button @filter_button, :filter # filter when button is pressed
|
50
|
+
|
51
|
+
# add elements to tool bar
|
52
|
+
@tool_bar_sizer.add_item(@add_button, :flag => ALL, :border => 1)
|
53
|
+
@tool_bar_sizer.add_item(@refresh_button, :flag => ALL, :border => 1)
|
54
|
+
@tool_bar_sizer.add_item(@filter_textbox, :flag => ALL, :border => 1)
|
55
|
+
@tool_bar_sizer.add_item(@filter_button, :flag => ALL, :border => 1)
|
56
|
+
end
|
57
|
+
|
58
|
+
def setup_key_shortcuts
|
59
|
+
evt_key_up { |event| refresh if event.get_key_code == K_F5 }
|
60
|
+
end
|
61
|
+
|
62
|
+
def setup_key_browser
|
63
|
+
@key_list = KeyListCtrl.new(self)
|
64
|
+
@sizer.add(@key_list, 1, EXPAND)
|
65
|
+
@key_list.on_get_keys { @redis.keys(@key_pattern).sort }
|
66
|
+
@key_list.update
|
67
|
+
end
|
68
|
+
|
69
|
+
def refresh(item = nil)
|
70
|
+
if(item)
|
71
|
+
@key_list.refresh_item(item)
|
72
|
+
else
|
73
|
+
@key_list.update
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def filter
|
78
|
+
@key_pattern = @filter_textbox.get_value.strip
|
79
|
+
@key_pattern = '*' if @key_pattern.nil? or @key_pattern == ''
|
80
|
+
refresh
|
81
|
+
end
|
82
|
+
|
83
|
+
def on_status_change(&block)
|
84
|
+
@key_list.on_status_change { |status| block.call(status) }
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Rediscover
|
2
|
+
module Panel
|
3
|
+
class ListView < Wx::Panel
|
4
|
+
include Wx
|
5
|
+
include KeyViewer
|
6
|
+
|
7
|
+
def initialize(parent, key)
|
8
|
+
@parent, @key = parent, key
|
9
|
+
@redis = get_app.redis
|
10
|
+
@logger = get_app.logger
|
11
|
+
|
12
|
+
super(@parent, -1, :style => SUNKEN_BORDER)
|
13
|
+
|
14
|
+
setup
|
15
|
+
end
|
16
|
+
|
17
|
+
def setup
|
18
|
+
members = @redis.lrange(@key, 0, -1)
|
19
|
+
|
20
|
+
@sizer = BoxSizer.new(VERTICAL)
|
21
|
+
set_sizer(@sizer)
|
22
|
+
|
23
|
+
@key_label = StaticText.new(self, :label => "Key: #{@key}")
|
24
|
+
|
25
|
+
@element_list = ListElementList.new(self, @key, members)
|
26
|
+
|
27
|
+
@button_sizer = BoxSizer.new(HORIZONTAL)
|
28
|
+
|
29
|
+
@lpush_button = Button.new(self, :label => 'Left Push')
|
30
|
+
evt_button(@lpush_button) { push(:lpush) }
|
31
|
+
|
32
|
+
@rpush_button = Button.new(self, :label => 'Right Push')
|
33
|
+
evt_button(@rpush_button) { push(:rpush) }
|
34
|
+
|
35
|
+
@lpop_button = Button.new(self, :label => 'Left Pop')
|
36
|
+
evt_button(@lpop_button) { pop(:lpop) }
|
37
|
+
|
38
|
+
@rpop_button = Button.new(self, :label => 'Right Pop')
|
39
|
+
evt_button(@rpop_button) { pop(:rpop) }
|
40
|
+
|
41
|
+
@close_button = Button.new(self, :label => 'Close')
|
42
|
+
evt_button @close_button, :do_on_close
|
43
|
+
|
44
|
+
@sizer.add_item(@key_label, :flag => ALL, :border => 2)
|
45
|
+
@sizer.add_item(@element_list, :proportion => 1, :flag => EXPAND|ALL, :border => 2)
|
46
|
+
@sizer.add_item(@button_sizer)
|
47
|
+
|
48
|
+
@button_sizer.add_item(@lpush_button, :flag => ALL, :border => 2)
|
49
|
+
@button_sizer.add_item(@rpush_button, :flag => ALL, :border => 2)
|
50
|
+
@button_sizer.add_item(@lpop_button, :flag => ALL, :border => 2)
|
51
|
+
@button_sizer.add_item(@rpop_button, :flag => ALL, :border => 2)
|
52
|
+
@button_sizer.add_item(@close_button, :flag => ALL, :border => 2)
|
53
|
+
end
|
54
|
+
|
55
|
+
def pop(dir = :lpop)
|
56
|
+
@redis.send(dir, @key)
|
57
|
+
@element_list.set_elements(@redis.lrange(@key, 0, -1))
|
58
|
+
do_on_save
|
59
|
+
end
|
60
|
+
|
61
|
+
def push(dir = :lpush)
|
62
|
+
@add_dlg = TextEntryDialog.new(self, 'Enter a new element:', 'Add Element')
|
63
|
+
if @add_dlg.show_modal == ID_OK
|
64
|
+
@redis.send(dir, @key, @add_dlg.get_value)
|
65
|
+
@element_list.set_elements(@redis.lrange(@key, 0, -1))
|
66
|
+
do_on_save
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Rediscover
|
2
|
+
module Panel
|
3
|
+
class Server < Wx::Panel
|
4
|
+
include Wx
|
5
|
+
|
6
|
+
REFRESH_INTERVAL = 5000
|
7
|
+
|
8
|
+
def initialize(parent)
|
9
|
+
@parent = parent
|
10
|
+
super(@parent, -1)
|
11
|
+
|
12
|
+
@redis = get_app.redis
|
13
|
+
@logger = get_app.logger
|
14
|
+
|
15
|
+
setup_sizer
|
16
|
+
setup_server_info
|
17
|
+
end
|
18
|
+
|
19
|
+
def setup_sizer
|
20
|
+
@sizer = BoxSizer.new(VERTICAL)
|
21
|
+
set_sizer(@sizer)
|
22
|
+
end
|
23
|
+
|
24
|
+
def setup_server_info
|
25
|
+
@server_info_text = StaticText.new(self, :label => get_info)
|
26
|
+
@sizer.add_item(@server_info_text)
|
27
|
+
timer = Timer.new(self)
|
28
|
+
evt_timer(timer.id) { refresh }
|
29
|
+
timer.start(REFRESH_INTERVAL)
|
30
|
+
end
|
31
|
+
|
32
|
+
def get_info
|
33
|
+
@redis.info.map { |key, value| "#{key}: #{value}" }.sort.join("\n")
|
34
|
+
end
|
35
|
+
|
36
|
+
def refresh
|
37
|
+
@server_info_text.set_label(get_info)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Rediscover
|
2
|
+
module Panel
|
3
|
+
class SetView < Wx::Panel
|
4
|
+
include Wx
|
5
|
+
include KeyViewer
|
6
|
+
|
7
|
+
def initialize(parent, key)
|
8
|
+
@parent, @key = parent, key
|
9
|
+
@redis = get_app.redis
|
10
|
+
@logger = get_app.logger
|
11
|
+
|
12
|
+
super(@parent, -1, :style => SUNKEN_BORDER)
|
13
|
+
|
14
|
+
setup
|
15
|
+
end
|
16
|
+
|
17
|
+
def setup
|
18
|
+
members = @redis.smembers(@key)
|
19
|
+
|
20
|
+
@sizer = BoxSizer.new(VERTICAL)
|
21
|
+
set_sizer(@sizer)
|
22
|
+
|
23
|
+
@key_label = StaticText.new(self, :label => "Key: #{@key}")
|
24
|
+
|
25
|
+
@element_list = SetElementList.new(self, @key, members)
|
26
|
+
@element_list.on_save { do_on_save }
|
27
|
+
|
28
|
+
@button_sizer = BoxSizer.new(HORIZONTAL)
|
29
|
+
|
30
|
+
@add_button = Button.new(self, :label => 'Add Element')
|
31
|
+
evt_button @add_button, :add
|
32
|
+
|
33
|
+
@close_button = Button.new(self, :label => 'Close')
|
34
|
+
evt_button @close_button, :do_on_close
|
35
|
+
|
36
|
+
@sizer.add_item(@key_label, :flag => ALL, :border => 2)
|
37
|
+
@sizer.add_item(@element_list, :proportion => 1, :flag => EXPAND|ALL, :border => 2)
|
38
|
+
@sizer.add_item(@button_sizer)
|
39
|
+
|
40
|
+
@button_sizer.add_item(@add_button, :flag => ALL, :border => 2)
|
41
|
+
@button_sizer.add_item(@close_button, :flag => ALL, :border => 2)
|
42
|
+
end
|
43
|
+
|
44
|
+
def add
|
45
|
+
@add_dlg = TextEntryDialog.new(self, 'Enter a new element:', 'Add Element')
|
46
|
+
if @add_dlg.show_modal == ID_OK
|
47
|
+
@redis.sadd(@key, @add_dlg.get_value)
|
48
|
+
@element_list.set_elements(@redis.smembers(@key))
|
49
|
+
do_on_save
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Rediscover
|
2
|
+
module Panel
|
3
|
+
class SortedSetView < Wx::Panel
|
4
|
+
include Wx
|
5
|
+
include KeyViewer
|
6
|
+
|
7
|
+
def initialize(parent, key)
|
8
|
+
@parent, @key = parent, key
|
9
|
+
@redis = get_app.redis
|
10
|
+
@logger = get_app.logger
|
11
|
+
|
12
|
+
super(@parent, -1, :style => SUNKEN_BORDER)
|
13
|
+
|
14
|
+
setup
|
15
|
+
end
|
16
|
+
|
17
|
+
def setup
|
18
|
+
members = @redis.zrange(@key, 0, -1)
|
19
|
+
|
20
|
+
@sizer = BoxSizer.new(VERTICAL)
|
21
|
+
set_sizer(@sizer)
|
22
|
+
|
23
|
+
@key_label = StaticText.new(self, :label => "Key: #{@key}")
|
24
|
+
|
25
|
+
@element_list = SortedSetElementList.new(self, @key, members)
|
26
|
+
@element_list.on_save { do_on_save }
|
27
|
+
|
28
|
+
@button_sizer = BoxSizer.new(HORIZONTAL)
|
29
|
+
|
30
|
+
@add_button = Button.new(self, :label => 'Add Element')
|
31
|
+
evt_button @add_button, :add
|
32
|
+
|
33
|
+
@close_button = Button.new(self, :label => 'Close')
|
34
|
+
evt_button @close_button, :do_on_close
|
35
|
+
|
36
|
+
@sizer.add_item(@key_label, :flag => ALL, :border => 2)
|
37
|
+
@sizer.add_item(@element_list, :proportion => 1, :flag => EXPAND|ALL, :border => 2)
|
38
|
+
@sizer.add_item(@button_sizer)
|
39
|
+
|
40
|
+
@button_sizer.add_item(@add_button, :flag => ALL, :border => 2)
|
41
|
+
@button_sizer.add_item(@close_button, :flag => ALL, :border => 2)
|
42
|
+
end
|
43
|
+
|
44
|
+
def add
|
45
|
+
@add_dlg = TextEntryDialog.new(self, 'Enter a new element:', 'Add Element')
|
46
|
+
if @add_dlg.show_modal == ID_OK
|
47
|
+
@redis.zadd(@key, 0, @add_dlg.get_value) # TODO get a priority score
|
48
|
+
@element_list.set_elements(@redis.zrange(@key, 0, -1))
|
49
|
+
do_on_save
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Rediscover
|
2
|
+
module Panel
|
3
|
+
class StringView < Wx::Panel
|
4
|
+
include Wx
|
5
|
+
include KeyViewer
|
6
|
+
|
7
|
+
def initialize(parent, key)
|
8
|
+
@parent = parent
|
9
|
+
super(@parent, -1, :style => SUNKEN_BORDER)
|
10
|
+
|
11
|
+
@key = key
|
12
|
+
@redis = get_app.redis
|
13
|
+
|
14
|
+
setup
|
15
|
+
end
|
16
|
+
|
17
|
+
def setup
|
18
|
+
value = @redis[@key]
|
19
|
+
|
20
|
+
@sizer = BoxSizer.new(VERTICAL)
|
21
|
+
set_sizer(@sizer)
|
22
|
+
|
23
|
+
@key_label = StaticText.new(self, :label => "Key: #{@key}")
|
24
|
+
|
25
|
+
@value_textbox = TextCtrl.new(self, :value => value, :style => TE_MULTILINE|TE_DONTWRAP)
|
26
|
+
|
27
|
+
@button_sizer = BoxSizer.new(HORIZONTAL)
|
28
|
+
|
29
|
+
@save_button = Button.new(self, :label => 'Save')
|
30
|
+
evt_button @save_button, :save
|
31
|
+
|
32
|
+
@close_button = Button.new(self, :label => 'Close')
|
33
|
+
evt_button @close_button, :do_on_close
|
34
|
+
|
35
|
+
@sizer.add_item(@key_label, :flag => ALL, :border => 2)
|
36
|
+
@sizer.add_item(@value_textbox, :proportion => 1, :flag => EXPAND|ALL, :border => 2)
|
37
|
+
@sizer.add_item(@button_sizer)
|
38
|
+
|
39
|
+
@button_sizer.add_item(@save_button, :flag => ALL, :border => 2)
|
40
|
+
@button_sizer.add_item(@close_button, :flag => ALL, :border => 2)
|
41
|
+
end
|
42
|
+
|
43
|
+
def save
|
44
|
+
@redis[@key] = @value_textbox.get_value
|
45
|
+
do_on_save
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Rediscover
|
2
|
+
class SetElementList < Wx::ListCtrl
|
3
|
+
include Wx
|
4
|
+
|
5
|
+
def initialize(parent, key, elements)
|
6
|
+
@parent, @key = parent, key
|
7
|
+
@redis = get_app.redis
|
8
|
+
@logger = get_app.logger
|
9
|
+
|
10
|
+
super(parent, :style => LC_REPORT)
|
11
|
+
|
12
|
+
setup
|
13
|
+
set_elements(elements)
|
14
|
+
end
|
15
|
+
|
16
|
+
def setup
|
17
|
+
insert_column(0, 'Elements')
|
18
|
+
set_column_width(LIST_AUTOSIZE, -1)
|
19
|
+
evt_list_item_right_click self, :list_item_right_click_evt
|
20
|
+
end
|
21
|
+
|
22
|
+
def set_elements(elements)
|
23
|
+
delete_all_items
|
24
|
+
i = 0
|
25
|
+
elements.each { |element| insert_item(i, element); i += 1 }
|
26
|
+
@elements = elements
|
27
|
+
end
|
28
|
+
|
29
|
+
def delete(selections)
|
30
|
+
delete_elements = selections.map { |index| @elements[index] }
|
31
|
+
delete_elements.each do |element|
|
32
|
+
@elements.delete(element)
|
33
|
+
@redis.srem(@key, element)
|
34
|
+
delete_item(find_item(0, element))
|
35
|
+
end
|
36
|
+
update
|
37
|
+
do_on_save
|
38
|
+
end
|
39
|
+
|
40
|
+
def list_item_right_click_evt(event)
|
41
|
+
menu = get_ctx_menu_for_selections(get_selections)
|
42
|
+
popup_menu(menu, event.get_point) if menu
|
43
|
+
end
|
44
|
+
|
45
|
+
def get_ctx_menu_for_selections(selections)
|
46
|
+
return nil if selections.empty?
|
47
|
+
@ctx_menu = Menu.new
|
48
|
+
|
49
|
+
# delete menu item
|
50
|
+
@ctx_delete_item = MenuItem.new(@ctx_menu, -1, 'Delete')
|
51
|
+
evt_menu(@ctx_delete_item) do
|
52
|
+
if ID_YES == Dialog::Confirm.new(self, 'Really delete selected element(s)?', 'Really delete?').show_modal
|
53
|
+
delete(selections)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
@ctx_menu.append_item(@ctx_delete_item)
|
58
|
+
@ctx_menu
|
59
|
+
end
|
60
|
+
|
61
|
+
def on_save(&block)
|
62
|
+
@on_save_block = block
|
63
|
+
end
|
64
|
+
|
65
|
+
def do_on_save
|
66
|
+
@on_save_block.call() if @on_save_block
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Rediscover
|
2
|
+
class SortedSetElementList < Wx::ListCtrl
|
3
|
+
include Wx
|
4
|
+
|
5
|
+
def initialize(parent, key, elements)
|
6
|
+
@parent, @key = parent, key
|
7
|
+
@redis = get_app.redis
|
8
|
+
@logger = get_app.logger
|
9
|
+
|
10
|
+
super(parent, :style => LC_REPORT)
|
11
|
+
|
12
|
+
setup
|
13
|
+
set_elements(elements)
|
14
|
+
end
|
15
|
+
|
16
|
+
def setup
|
17
|
+
insert_column(0, 'Elements')
|
18
|
+
set_column_width(LIST_AUTOSIZE, -1)
|
19
|
+
evt_list_item_right_click self, :list_item_right_click_evt
|
20
|
+
end
|
21
|
+
|
22
|
+
def set_elements(elements)
|
23
|
+
delete_all_items
|
24
|
+
i = 0
|
25
|
+
elements.each { |element| insert_item(i, element); i += 1 }
|
26
|
+
@elements = elements
|
27
|
+
end
|
28
|
+
|
29
|
+
def delete(selections)
|
30
|
+
delete_elements = selections.map { |index| @elements[index] }
|
31
|
+
delete_elements.each do |element|
|
32
|
+
@elements.delete(element)
|
33
|
+
@redis.zrem(@key, element)
|
34
|
+
delete_item(find_item(0, element))
|
35
|
+
end
|
36
|
+
update
|
37
|
+
do_on_save
|
38
|
+
end
|
39
|
+
|
40
|
+
def list_item_right_click_evt(event)
|
41
|
+
menu = get_ctx_menu_for_selections(get_selections)
|
42
|
+
popup_menu(menu, event.get_point) if menu
|
43
|
+
end
|
44
|
+
|
45
|
+
def get_ctx_menu_for_selections(selections)
|
46
|
+
return nil if selections.empty?
|
47
|
+
@ctx_menu = Menu.new
|
48
|
+
|
49
|
+
# delete menu item
|
50
|
+
@ctx_delete_item = MenuItem.new(@ctx_menu, -1, 'Delete')
|
51
|
+
evt_menu(@ctx_delete_item) do
|
52
|
+
if ID_YES == Dialog::Confirm.new(self, 'Really delete selected element(s)?', 'Really delete?').show_modal
|
53
|
+
delete(selections)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
@ctx_menu.append_item(@ctx_delete_item)
|
58
|
+
@ctx_menu
|
59
|
+
end
|
60
|
+
|
61
|
+
def on_save(&block)
|
62
|
+
@on_save_block = block
|
63
|
+
end
|
64
|
+
|
65
|
+
def do_on_save
|
66
|
+
@on_save_block.call() if @on_save_block
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
data/lib/rediscover.rb
ADDED
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rediscover
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Chris Kite
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-03-07 00:00:00 -06:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: wxruby
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 2.0.1
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: redis
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.1.2
|
34
|
+
version:
|
35
|
+
description:
|
36
|
+
email:
|
37
|
+
executables:
|
38
|
+
- rediscover
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files: []
|
42
|
+
|
43
|
+
files:
|
44
|
+
- lib/rediscover.rb
|
45
|
+
- lib/rediscover/key_list_ctrl.rb
|
46
|
+
- lib/rediscover/sorted_set_element_list.rb
|
47
|
+
- lib/rediscover/set_element_list.rb
|
48
|
+
- lib/rediscover/key_viewer.rb
|
49
|
+
- lib/rediscover/dialog/confirm.rb
|
50
|
+
- lib/rediscover/dialog/error.rb
|
51
|
+
- lib/rediscover/icon.rb
|
52
|
+
- lib/rediscover/panel/browser.rb
|
53
|
+
- lib/rediscover/panel/server.rb
|
54
|
+
- lib/rediscover/panel/list_view.rb
|
55
|
+
- lib/rediscover/panel/sorted_set_view.rb
|
56
|
+
- lib/rediscover/panel/string_view.rb
|
57
|
+
- lib/rediscover/panel/set_view.rb
|
58
|
+
- lib/rediscover/panel/key_list.rb
|
59
|
+
- lib/rediscover/frame/main.rb
|
60
|
+
- lib/rediscover/frame/create_key.rb
|
61
|
+
- lib/rediscover/frame/connect.rb
|
62
|
+
- lib/rediscover/exception_handler.rb
|
63
|
+
- lib/rediscover/list_element_list.rb
|
64
|
+
- lib/rediscover/icons/server_connect.png
|
65
|
+
- lib/rediscover/icons/refresh.png
|
66
|
+
- lib/rediscover/icons/key_add.png
|
67
|
+
- lib/rediscover/icons/application.png
|
68
|
+
- lib/rediscover/icons/add.png
|
69
|
+
- lib/rediscover/app.rb
|
70
|
+
- bin/rediscover
|
71
|
+
has_rdoc: true
|
72
|
+
homepage: http://github.com/chriskite/rediscover
|
73
|
+
licenses: []
|
74
|
+
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: "0"
|
85
|
+
version:
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: "0"
|
91
|
+
version:
|
92
|
+
requirements: []
|
93
|
+
|
94
|
+
rubyforge_project:
|
95
|
+
rubygems_version: 1.3.5
|
96
|
+
signing_key:
|
97
|
+
specification_version: 3
|
98
|
+
summary: Redis GUI
|
99
|
+
test_files: []
|
100
|
+
|