rediscover 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|