fargo 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,41 @@
1
+ require 'fargo/ext/readline'
2
+
3
+ module Fargo
4
+ module CLI
5
+ module Completion
6
+
7
+ def setup_console
8
+ old_proc = Readline.completion_proc
9
+ Readline.basic_word_break_characters = " \t\n\\'"
10
+ Readline.basic_quote_characters = ''
11
+
12
+ Readline.completion_proc = lambda { |str|
13
+ input = Readline.get_input
14
+
15
+ candidates, data = [], nil
16
+ regex, proc = @completions.detect{ |k, _|
17
+ data = input.match(k)
18
+ }
19
+ if data
20
+ data = data.to_a
21
+ data.shift
22
+ candidates = proc.call *data
23
+ end
24
+
25
+ if candidates.empty?
26
+ old_proc.call str
27
+ else
28
+ str = str.gsub /^"/, ''
29
+ candidates.select{ |n| n.start_with? str }.map{ |s| s.inspect }
30
+ end
31
+ }
32
+ end
33
+
34
+ def add_completion regex, &block
35
+ @completions ||= {}
36
+ @completions[regex] = block
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,57 @@
1
+ module Fargo
2
+ module CLI
3
+ module Downloads
4
+
5
+ def setup_console
6
+ super
7
+
8
+ add_completion(/^(download|get)\s+\d+,\s*[^\s]*$/) do
9
+ client.searches
10
+ end
11
+
12
+ add_logger(:download_started) do |message|
13
+ "Download of #{message[:download][:file]} " +
14
+ "(#{humanize_bytes message[:length]}) " +
15
+ "from #{message[:nick]} started"
16
+ end
17
+
18
+ add_logger(:download_finished) do |message|
19
+ "Download of #{message[:download][:file]} " +
20
+ "finished into #{message[:file]}"
21
+ end
22
+ end
23
+
24
+ def download index, search = nil
25
+ search ||= client.searches[0]
26
+
27
+ if search.nil?
28
+ puts "Nothing to download!"
29
+ return
30
+ end
31
+
32
+ item = client.search_results(search)[index]
33
+
34
+ if item.nil?
35
+ puts 'That is not something to download!'
36
+ else
37
+ client.download item[:nick], item[:file], item[:tth], item[:size]
38
+ end
39
+ end
40
+
41
+ def transfers
42
+ max_nick = client.current_downloads.keys.map(&:size).max
43
+ client.current_downloads.each_pair do |nick, download|
44
+ printf "%#{max_nick}s %10s (%.2f%%) -- %s\n", nick,
45
+ humanize_bytes(download.size), 100 * download.percent,
46
+ download.file
47
+ end
48
+
49
+ puts "Upload slots avail: " +
50
+ "#{client.open_upload_slots}/#{client.config.upload_slots} " +
51
+ "Download slots avail: " +
52
+ "#{client.open_download_slots}/#{client.config.download_slots}"
53
+ end
54
+
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,80 @@
1
+ module Fargo
2
+ module CLI
3
+ module Help
4
+
5
+ def setup_console
6
+ super
7
+
8
+ add_completion(/^man\s+[^\s]*$/){
9
+ ['man', 'results', 'search', 'ls', 'pwd', 'cwd', 'cd', 'browse',
10
+ 'download', 'get', 'who', 'say', 'send_chat', 'transfers']
11
+ }
12
+ end
13
+
14
+ def man cmd = nil
15
+ cmd ||= 'man'
16
+
17
+ case cmd.to_sym
18
+ when :man
19
+ puts "Usage: man 'cmd'"
20
+ when :results
21
+ puts "Usage: results ['search string' | index], opts = {}"
22
+ puts ""
23
+ puts " Show the results for a previous search. The search can be"
24
+ puts " identified by its search string or its index of the search."
25
+ puts " If no search string or index is given, the last search"
26
+ puts " results are displayed if they exist."
27
+ puts ""
28
+ puts " Recognized options:"
29
+ puts " :full - if true, show full filenames instead of just base"
30
+ puts " :sort - if 'size', sort results by size"
31
+ puts " :grep - A regexp to filter results by"
32
+ when :search
33
+ puts "Usage: search ['string' | Search]"
34
+ puts ""
35
+ puts " Search the hub for something. Either a string or a Search"
36
+ puts " object can be specified."
37
+ when :ls
38
+ puts "Usage: ls ['dir']"
39
+ puts ""
40
+ puts " Lists a directory. If no directory is given, lists the"
41
+ puts " current one."
42
+ when :pwd, :cwd
43
+ puts "Usage: pwd/cwd"
44
+ puts ""
45
+ puts " Show your current directory when browsing a user"
46
+ when :cd
47
+ puts "Usage: cd ['dir']"
48
+ puts ""
49
+ puts " Works just like on UNIX. No argument means go to root"
50
+ when :browse
51
+ puts "Usage: browse 'nick'"
52
+ puts ""
53
+ puts " Begin browisng a nick. If no file list has been downloaded,"
54
+ puts " one is queued for download and you will be notified when"
55
+ puts " browsing is ready."
56
+ when :download, :get
57
+ puts "Usage: "
58
+ when :who
59
+ puts "Usage: who ['nick' | 'size' | 'name']"
60
+ puts ""
61
+ puts " If no argument is given, shows all users on the hub. If a"
62
+ puts " name is given, shows that user on the hub. If 'size' or"
63
+ puts " 'name' is given, the users are sorted by that attribute."
64
+ when :say, :send_chat
65
+ puts "Usage: say/send_chat 'msg'"
66
+ puts ""
67
+ puts " Send a message to the hub"
68
+ when :transfers
69
+ puts "Usage: transfers"
70
+ puts ""
71
+ puts " Show some statistics about transfers happening."
72
+ else
73
+ puts "Unknown commnand: #{cmd}"
74
+ end
75
+
76
+ end
77
+
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,55 @@
1
+ require 'active_support/core_ext/module/delegation'
2
+
3
+ module Fargo
4
+ module CLI
5
+ module Info
6
+
7
+ delegate :send_chat, :to => :client
8
+ alias :say :send_chat
9
+
10
+ def setup_console
11
+ super
12
+
13
+ add_completion(/^who\s+[^\s]*$/) { client.nicks + ['size', 'name'] }
14
+ end
15
+
16
+ def who sort_by = nil
17
+ print_nick = lambda{ |p|
18
+ printf "%10s %s\n", humanize_bytes(p[1]), p[0]
19
+ }
20
+
21
+ if client.nicks.include? sort_by
22
+ info = client.info(sort_by)
23
+ key_len = info.keys.map{ |k| k.to_s.length }.max
24
+
25
+ info.each_pair do |k, v|
26
+ next if k == :type
27
+ printf "%#{key_len}s: %s\n", k,
28
+ v.is_a?(Numeric) ? humanize_bytes(v) : v
29
+ end
30
+ elsif sort_by.nil?
31
+ client.nicks.each do |n|
32
+ print_nick.call [n, client.info(n)[:sharesize]]
33
+ end
34
+ else
35
+ pairs = client.nicks.map{ |n|
36
+ [n, client.info(n)[:sharesize]]
37
+ }
38
+
39
+ if sort_by == 'name'
40
+ pairs = pairs.sort_by{ |p| p[0] }
41
+ elsif sort_by == 'size'
42
+ pairs = pairs.sort_by{ |p| p[1] }
43
+ else
44
+ pairs = []
45
+ puts "Unknown sorting by: #{sort_by.inspect}"
46
+ end
47
+ pairs.each &print_nick
48
+ end
49
+
50
+ true
51
+ end
52
+
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,87 @@
1
+ require 'drb'
2
+ require 'em-http-request'
3
+
4
+ module Fargo
5
+ module CLI
6
+ module Logging
7
+
8
+ attr_writer :client
9
+
10
+ def setup_console
11
+ super
12
+
13
+ add_logger(:chat) do |message|
14
+ "<#{message[:from]}>: #{message[:text]}"
15
+ end
16
+
17
+ add_logger(:hub_disconnected) do |_|
18
+ puts "Hub disconnected, exiting..."
19
+ exit
20
+ end
21
+ end
22
+
23
+ def add_logger type, &block
24
+ @logging[type.to_s] << block
25
+ end
26
+
27
+ def client
28
+ @client ||= DRbObject.new_with_uri 'druby://127.0.0.1:8082'
29
+ end
30
+
31
+ def log_published_messages
32
+ @logging = Hash.new{ |h, k| h[k] = [] }
33
+
34
+ streamer = proc {
35
+ host = "ws://#{client.config.websocket_host}" +
36
+ ":#{client.config.websocket_port}/"
37
+
38
+ ws = EventMachine::HttpRequest.new(host).get(:timeout => 0)
39
+
40
+ ws.disconnect {
41
+ Readline.above_prompt{ puts "Stopping logging stream." }
42
+ }
43
+
44
+ ws.callback {
45
+ Readline.above_prompt{ puts "Streaming logging messages." }
46
+ }
47
+
48
+ ws.stream { |msg|
49
+ to_log = nil
50
+ type, message = Marshal.load(Base64.decode64(msg))
51
+
52
+ @logging[type.to_s].each{ |l|
53
+ to_log = l.call message
54
+ Readline.above_prompt{ puts to_log } unless to_log.nil?
55
+ }
56
+ }
57
+ }
58
+
59
+ if EventMachine.reactor_running?
60
+ EventMachine.schedule streamer
61
+ else
62
+ Thread.start{ EventMachine.run streamer }
63
+ end
64
+ end
65
+
66
+ protected
67
+
68
+ def humanize_bytes bytes
69
+ suffix = 'B'
70
+ while bytes > 1024
71
+ suffix = case suffix
72
+ when 'B' then 'K'
73
+ when 'K' then 'M'
74
+ when 'M' then 'G'
75
+ when 'G' then 'T'
76
+ when 'T' then break
77
+ end
78
+
79
+ bytes /= 1024.0
80
+ end
81
+
82
+ '%.2f %s' % [bytes, suffix]
83
+ end
84
+
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,159 @@
1
+ require 'pathname'
2
+
3
+ module Fargo
4
+ module CLI
5
+ module NickBrowser
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ alias :get :download
10
+ end
11
+
12
+ module InstanceMethods
13
+ def setup_console
14
+ super
15
+
16
+ @fixed_completions = {}
17
+
18
+ add_completion(/^browse\s+[^\s]*$/) { client.nicks }
19
+
20
+ file_regex = /(?:\s+(?:[^\s,]*))+/
21
+ add_completion(/^(?:get|download)#{file_regex}$/) { completion true }
22
+ add_completion(/^(?:ls|cd)#{file_regex}$/) { completion }
23
+
24
+ add_logger(:download_finished) do |message|
25
+ if message[:file].end_with? 'files.xml.bz2'
26
+ begin_browsing message[:nick]
27
+ end
28
+ end
29
+ end
30
+
31
+ def download file, other = nil
32
+ if file.is_a?(String)
33
+ resolved = resolve(file).to_s
34
+ listing = drilldown resolved, @file_list
35
+
36
+ if listing.nil?
37
+ puts "No file to download!: #{file}"
38
+ elsif listing.is_a? Hash
39
+ # Recursively download the entire directory
40
+ listing.keys.each do |k|
41
+ download File.join(resolved, k)
42
+ end
43
+ else
44
+ client.download listing
45
+ end
46
+ else
47
+ super
48
+ end
49
+ end
50
+ end
51
+
52
+ def browse nick
53
+ @browsing = nick
54
+ @file_list = nil
55
+ list = client.file_list nick
56
+ begin_browsing nick, false if list.is_a?(Hash)
57
+ end
58
+
59
+ def cd dir = '/'
60
+ cwd = resolve(dir)
61
+ if drilldown(cwd, @file_list).nil?
62
+ puts "#{dir.inspect} doesn't exist!"
63
+ else
64
+ @cwd = cwd
65
+ pwd
66
+ end
67
+ end
68
+
69
+ def pwd
70
+ puts @cwd.to_s
71
+ end
72
+
73
+ alias :cwd :pwd
74
+
75
+ def ls dir = ''
76
+ if @cwd.nil? || @file_list.nil?
77
+ puts "Note browsing any nick!"
78
+ return
79
+ end
80
+
81
+ hash = drilldown(resolve(dir), @file_list)
82
+
83
+ hash.keys.sort_by(&:downcase).
84
+ sort_by{ |k| hash[k].is_a?(Hash) ? 0 : 1 }.each do |key|
85
+ if hash[key].is_a?(Hash)
86
+ puts "#{key}/"
87
+ else
88
+ printf "%10s -- %s\n", humanize_bytes(hash[key].size), key
89
+ end
90
+ end
91
+
92
+ true
93
+ end
94
+
95
+ def begin_browsing nick, above_prompt = true
96
+ @cwd = Pathname.new '/'
97
+ @file_list = client.file_list(@browsing)
98
+
99
+ if above_prompt
100
+ Readline.above_prompt{ puts "#{@browsing} ready for browsing" }
101
+ else
102
+ puts "#{@browsing} ready for browsing"
103
+ end
104
+ end
105
+
106
+ protected
107
+
108
+ def completion include_files = false
109
+ if @browsing
110
+ all_input = Readline.get_input
111
+ dirs = []
112
+ while tmp = all_input.slice!(/[^\s]+?\s+/)
113
+ dirs << tmp.rstrip.gsub!(/"/, '')
114
+ end
115
+ dirs.shift # original command
116
+
117
+ resolved = resolve dirs.join('/'), false
118
+ hash = drilldown resolved, @file_list
119
+
120
+ keys = hash.keys rescue []
121
+ keys = keys.select{ |k|
122
+ include_files || k == '..' || hash[k].is_a?(Hash)
123
+ }
124
+ keys << '..' unless keys.empty? && dirs.size != 0
125
+
126
+ keys.map{ |k|
127
+ suffix = hash[k].is_a?(Hash) ? '/' : ''
128
+
129
+ # Readline doesn't like completing words with spaces in the file
130
+ # name, so just display them as periods when in actuality we'll
131
+ # convert back to a space later
132
+ (k.gsub(' ', '.') + suffix).tap do |str|
133
+ key = @cwd.join(*dirs).join(str).expand_path.to_s
134
+ @fixed_completions[key] = resolved.join k
135
+ end
136
+ }
137
+ else
138
+ []
139
+ end
140
+ end
141
+
142
+ def resolve dir, clear_cache = true
143
+ return '' if @cwd.nil?
144
+
145
+ res = @fixed_completions[@cwd.join(dir).expand_path.to_s] ||
146
+ @cwd.join(dir).expand_path
147
+ @fixed_completions.clear if clear_cache
148
+ res.expand_path
149
+ end
150
+
151
+ def drilldown path, list
152
+ path.to_s.gsub(/^\//, '').split('/').inject(list) { |hash, part|
153
+ hash ? hash[part] : nil
154
+ }
155
+ end
156
+
157
+ end
158
+ end
159
+ end