fargo 0.3.0 → 0.4.0

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.
@@ -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