fogbugz 1.0.3 → 1.0.4
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/fogbugz +24 -76
- data/bin/fogbugz-areas +3 -49
- data/bin/fogbugz-assign +3 -54
- data/bin/fogbugz-categories +4 -50
- data/bin/fogbugz-close +1 -1
- data/bin/fogbugz-edit +46 -285
- data/bin/fogbugz-filter +2 -1
- data/bin/fogbugz-filters +6 -60
- data/bin/fogbugz-list +39 -42
- data/bin/fogbugz-login +3 -34
- data/bin/fogbugz-logoff +3 -47
- data/bin/fogbugz-milestones +6 -52
- data/bin/fogbugz-open +39 -0
- data/bin/fogbugz-people +5 -51
- data/bin/fogbugz-priorities +4 -51
- data/bin/fogbugz-projects +5 -51
- data/bin/fogbugz-reactivate +2 -1
- data/bin/fogbugz-reopen +2 -1
- data/bin/fogbugz-resolve +5 -72
- data/bin/fogbugz-show +22 -138
- data/bin/fogbugz-start +3 -52
- data/bin/fogbugz-statuses +5 -38
- data/bin/fogbugz-stop +3 -47
- data/lib/fogbugz/common.rb +370 -0
- metadata +19 -7
- data/bin/fogbugz-new +0 -175
- data/lib/dummy.rb +0 -1
data/bin/fogbugz-filter
CHANGED
data/bin/fogbugz-filters
CHANGED
|
@@ -1,65 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
+
require 'fogbugz/common'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
require 'optparse'
|
|
8
|
-
require 'highline'
|
|
9
|
-
|
|
10
|
-
HighLine.use_color = STDOUT.isatty
|
|
11
|
-
begin
|
|
12
|
-
require 'win32console' if RUBY_PLATFORM =~ /mingw/
|
|
13
|
-
rescue LoadError
|
|
14
|
-
HighLine.use_color = false
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
api_url = ENV['FOGBUGZ_API_URL']
|
|
18
|
-
unless api_url
|
|
19
|
-
puts "Environment variable FOGBUGZ_API_URL must be set."
|
|
20
|
-
exit 1
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
api_token = ENV['FOGBUGZ_API_TOKEN']
|
|
24
|
-
unless api_token
|
|
25
|
-
puts "Environment variable FOGBUGZ_API_TOKEN must be set."
|
|
26
|
-
exit 1
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
options = {}
|
|
30
|
-
optparse = OptionParser.new do |opts|
|
|
31
|
-
opts.banner = "usage: #{File::basename(__FILE__)} [options]"
|
|
32
|
-
|
|
33
|
-
opts.on_tail('-h', '--help') do
|
|
34
|
-
puts optparse.help
|
|
35
|
-
exit 1
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
optparse.parse!
|
|
39
|
-
|
|
40
|
-
uri = URI format("#{api_url}?cmd=listFilters&token=%s", URI.escape(api_token))
|
|
41
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
|
42
|
-
if uri.scheme == 'https'
|
|
43
|
-
http.use_ssl = true
|
|
44
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
45
|
-
end
|
|
46
|
-
response = http.start { |h| h.request Net::HTTP::Get.new(uri.request_uri) }
|
|
47
|
-
if response.code != '200'
|
|
48
|
-
puts "HTTP request to #{api_url} failed with code #{response.code}."
|
|
49
|
-
exit 1
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
result = REXML::Document.new(response.body)
|
|
53
|
-
error = result.elements['/response/error']
|
|
54
|
-
if error
|
|
55
|
-
puts "Failed with error: #{error.text}."
|
|
56
|
-
exit 1
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
result.elements.each('/response/filters/filter') do |filter|
|
|
60
|
-
if filter.attributes['status'] == 'current'
|
|
61
|
-
puts "* #{HighLine.new.color(filter.text, HighLine::GREEN)}"
|
|
4
|
+
parse_opts "usage: #{File::basename(__FILE__)} [options]", 0
|
|
5
|
+
do_api('listFilters').elements.each('/response/filters/filter') do |f|
|
|
6
|
+
if f.attributes['status'] == 'current'
|
|
7
|
+
puts "* #{g}#{f.text}#{c}"
|
|
62
8
|
else
|
|
63
|
-
puts " #{
|
|
9
|
+
puts " #{f.text}"
|
|
64
10
|
end
|
|
65
11
|
end
|
data/bin/fogbugz-list
CHANGED
|
@@ -1,22 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
-
|
|
3
|
-
require 'rubygems'
|
|
4
|
-
require 'net/https'
|
|
5
|
-
require 'uri'
|
|
6
|
-
require 'rexml/document'
|
|
7
|
-
require 'optparse'
|
|
8
|
-
|
|
9
|
-
api_url = ENV['FOGBUGZ_API_URL']
|
|
10
|
-
unless api_url
|
|
11
|
-
puts "Environment variable FOGBUGZ_API_URL must be set."
|
|
12
|
-
exit 1
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
api_token = ENV['FOGBUGZ_API_TOKEN']
|
|
16
|
-
unless api_token
|
|
17
|
-
puts "Environment variable FOGBUGZ_API_TOKEN must be set."
|
|
18
|
-
exit 1
|
|
19
|
-
end
|
|
2
|
+
require 'fogbugz/common'
|
|
20
3
|
|
|
21
4
|
options = {}
|
|
22
5
|
optparse = OptionParser.new do |opts|
|
|
@@ -33,35 +16,49 @@ optparse = OptionParser.new do |opts|
|
|
|
33
16
|
end
|
|
34
17
|
end
|
|
35
18
|
optparse.parse!
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
URI.escape(api_token),
|
|
39
|
-
URI.escape('ixBug,sPersonAssignedTo,sFixFor,sPriority,sTitle'),
|
|
40
|
-
ARGV[0] ? "&q=#{URI.escape(ARGV[0])}" : '',
|
|
41
|
-
options[:max] ? "&max=#{URI.escape(options[:max])}" : '')
|
|
42
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
|
43
|
-
if uri.scheme == 'https'
|
|
44
|
-
http.use_ssl = true
|
|
45
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
46
|
-
end
|
|
47
|
-
response = http.start { |h| h.request Net::HTTP::Get.new(uri.request_uri) }
|
|
48
|
-
if response.code != '200'
|
|
49
|
-
puts "HTTP request to #{api_url} failed with code #{response.code}."
|
|
19
|
+
unless ARGV.length < 2
|
|
20
|
+
puts optparse.help
|
|
50
21
|
exit 1
|
|
51
22
|
end
|
|
52
23
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if
|
|
56
|
-
|
|
57
|
-
exit 1
|
|
58
|
-
end
|
|
24
|
+
params = {}
|
|
25
|
+
params[:max] = options[:max] if options[:max]
|
|
26
|
+
params[:q] = ARGV[0] if ARGV.length == 1
|
|
27
|
+
params[:cols] = 'ixBug,sPersonAssignedTo,sFixFor,sStatus,dtDue,sTitle,dtDue,plugin_projectbacklog_at_fogcreek_com_ibacklog'
|
|
59
28
|
|
|
60
|
-
result
|
|
61
|
-
|
|
29
|
+
result = do_api('search', params)
|
|
30
|
+
|
|
31
|
+
if result.elements['/response/description']
|
|
32
|
+
puts "#{result.elements['/response/description'].text}:"
|
|
33
|
+
end
|
|
34
|
+
result.elements.to_a('/response/cases/case').sort { |a,b|
|
|
35
|
+
a = a.elements['plugin_projectbacklog_at_fogcreek_com_ibacklog'] || ''
|
|
36
|
+
b = b.elements['plugin_projectbacklog_at_fogcreek_com_ibacklog'] || ''
|
|
37
|
+
a_i = a.text.to_i
|
|
38
|
+
b_i = b.text.to_i
|
|
39
|
+
if a_i === 0
|
|
40
|
+
1
|
|
41
|
+
elsif b_i === 0
|
|
42
|
+
-1
|
|
43
|
+
else
|
|
44
|
+
a_i <=> b_i
|
|
45
|
+
end
|
|
46
|
+
}.each do |bug|
|
|
47
|
+
due = bug.elements['dtDue']
|
|
48
|
+
days_diff = (due and due.text ?
|
|
49
|
+
(Time.parse(due.text) - Time.now) / 60 / 60 / 24 : 1)
|
|
50
|
+
puts format("%-6.6s %-20.20s %-16.16s %s%-8.8s#{c} %4.4s %s\n",
|
|
62
51
|
bug.elements['ixBug'].text,
|
|
63
52
|
bug.elements['sPersonAssignedTo'].text,
|
|
64
|
-
bug.elements['
|
|
65
|
-
|
|
53
|
+
bug.elements['sStatus'].text,
|
|
54
|
+
if days_diff.floor < 0
|
|
55
|
+
r
|
|
56
|
+
elsif days_diff.floor == 0
|
|
57
|
+
y
|
|
58
|
+
else
|
|
59
|
+
''
|
|
60
|
+
end,
|
|
61
|
+
time_short_string(bug.elements['dtDue'].text),
|
|
62
|
+
bug.elements['plugin_projectbacklog_at_fogcreek_com_ibacklog'].text,
|
|
66
63
|
bug.elements['sTitle'].text).strip!
|
|
67
64
|
end
|
data/bin/fogbugz-login
CHANGED
|
@@ -1,21 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
-
|
|
3
|
-
require 'rubygems'
|
|
4
|
-
require 'net/https'
|
|
5
|
-
require 'uri'
|
|
6
|
-
require 'rexml/document'
|
|
7
|
-
require 'optparse'
|
|
8
|
-
require 'highline'
|
|
9
|
-
|
|
10
|
-
api_url = ENV['FOGBUGZ_API_URL']
|
|
11
|
-
unless api_url
|
|
12
|
-
puts "Environment variable FOGBUGZ_API_URL must be set."
|
|
13
|
-
exit 1
|
|
14
|
-
end
|
|
2
|
+
require 'fogbugz/common'
|
|
15
3
|
|
|
16
4
|
options = {}
|
|
17
5
|
optparse = OptionParser.new do |opts|
|
|
18
|
-
opts.banner = "usage: #{File::basename(__FILE__)} [options] <email>
|
|
6
|
+
opts.banner = "usage: #{File::basename(__FILE__)} [options] <email>"
|
|
19
7
|
|
|
20
8
|
options[:password] = nil
|
|
21
9
|
opts.on_tail('-p', '--password=<password>') do |password|
|
|
@@ -36,26 +24,7 @@ end
|
|
|
36
24
|
password = options[:password] ||
|
|
37
25
|
HighLine.new.ask('Password: ') { |q| q.echo = false }
|
|
38
26
|
|
|
39
|
-
|
|
40
|
-
URI.escape(ARGV[0]), password)
|
|
41
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
|
42
|
-
if uri.scheme == 'https'
|
|
43
|
-
http.use_ssl = true
|
|
44
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
45
|
-
end
|
|
46
|
-
response = http.start { |h| h.request Net::HTTP::Get.new(uri.request_uri) }
|
|
47
|
-
if response.code != '200'
|
|
48
|
-
puts "HTTP request to #{api_url} failed with code #{response.code}."
|
|
49
|
-
exit 1
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
result = REXML::Document.new(response.body)
|
|
53
|
-
error = result.elements['/response/error']
|
|
54
|
-
if error
|
|
55
|
-
puts "Failed with error: #{error.text}."
|
|
56
|
-
exit 1
|
|
57
|
-
end
|
|
58
|
-
|
|
27
|
+
result = do_api('logon', :email => ARGV[0], :password => password)
|
|
59
28
|
puts <<HERE
|
|
60
29
|
Login successful. Set the following environment variable:
|
|
61
30
|
export FOGBUGZ_API_TOKEN=#{result.elements['/response/token'].text}
|
data/bin/fogbugz-logoff
CHANGED
|
@@ -1,49 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
+
require 'fogbugz/common'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
require 'uri'
|
|
6
|
-
require 'rexml/document'
|
|
7
|
-
require 'optparse'
|
|
8
|
-
|
|
9
|
-
api_url = ENV['FOGBUGZ_API_URL']
|
|
10
|
-
unless api_url
|
|
11
|
-
puts "Environment variable FOGBUGZ_API_URL must be set."
|
|
12
|
-
exit 1
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
api_token = ENV['FOGBUGZ_API_TOKEN']
|
|
16
|
-
unless api_token
|
|
17
|
-
puts "Environment variable FOGBUGZ_API_TOKEN must be set."
|
|
18
|
-
exit 1
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
options = {}
|
|
22
|
-
optparse = OptionParser.new do |opts|
|
|
23
|
-
opts.banner = "usage: #{File::basename(__FILE__)} [options]"
|
|
24
|
-
|
|
25
|
-
opts.on_tail('-h', '--help') do
|
|
26
|
-
puts optparse.help
|
|
27
|
-
exit 1
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
optparse.parse!
|
|
31
|
-
|
|
32
|
-
uri = URI format("#{api_url}?cmd=logoff&token=%s", URI.escape(api_token))
|
|
33
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
|
34
|
-
if uri.scheme == 'https'
|
|
35
|
-
http.use_ssl = true
|
|
36
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
37
|
-
end
|
|
38
|
-
response = http.start { |h| h.request Net::HTTP::Get.new(uri.request_uri) }
|
|
39
|
-
if response.code != '200'
|
|
40
|
-
puts "HTTP request to #{api_url} failed with code #{response.code}."
|
|
41
|
-
exit 1
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
result = REXML::Document.new(response.body)
|
|
45
|
-
error = result.elements['/response/error']
|
|
46
|
-
if error
|
|
47
|
-
puts "Failed with error: #{error.text}."
|
|
48
|
-
exit 1
|
|
49
|
-
end
|
|
4
|
+
parse_opts "usage: #{File::basename(__FILE__)} [options]", 0
|
|
5
|
+
do_api 'logoff'
|
data/bin/fogbugz-milestones
CHANGED
|
@@ -1,56 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
+
require 'fogbugz/common'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
require 'uri'
|
|
6
|
-
require 'rexml/document'
|
|
7
|
-
require 'optparse'
|
|
8
|
-
|
|
9
|
-
api_url = ENV['FOGBUGZ_API_URL']
|
|
10
|
-
unless api_url
|
|
11
|
-
puts "Environment variable FOGBUGZ_API_URL must be set."
|
|
12
|
-
exit 1
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
api_token = ENV['FOGBUGZ_API_TOKEN']
|
|
16
|
-
unless api_token
|
|
17
|
-
puts "Environment variable FOGBUGZ_API_TOKEN must be set."
|
|
18
|
-
exit 1
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
options = {}
|
|
22
|
-
optparse = OptionParser.new do |opts|
|
|
23
|
-
opts.banner = "usage: #{File::basename(__FILE__)} [options]"
|
|
24
|
-
|
|
25
|
-
opts.on_tail('-h', '--help') do
|
|
26
|
-
puts optparse.help
|
|
27
|
-
exit 1
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
optparse.parse!
|
|
31
|
-
|
|
32
|
-
uri = URI format("#{api_url}?cmd=listFixFors&token=%s", URI.escape(api_token))
|
|
33
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
|
34
|
-
if uri.scheme == 'https'
|
|
35
|
-
http.use_ssl = true
|
|
36
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
37
|
-
end
|
|
38
|
-
response = http.start { |h| h.request Net::HTTP::Get.new(uri.request_uri) }
|
|
39
|
-
if response.code != '200'
|
|
40
|
-
puts "HTTP request to #{api_url} failed with code #{response.code}."
|
|
41
|
-
exit 1
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
result = REXML::Document.new(response.body)
|
|
45
|
-
error = result.elements['/response/error']
|
|
46
|
-
if error
|
|
47
|
-
puts "Failed with error: #{error.text}."
|
|
48
|
-
exit 1
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
result.elements.each('/response/fixfors/fixfor') do |milestones|
|
|
4
|
+
parse_opts "usage: #{File::basename(__FILE__)} [options]", 0
|
|
5
|
+
do_api('listFixFors').elements.each('/response/fixfors/fixfor') do |m|
|
|
52
6
|
puts format("%-30.30s %-16.16s %s\n",
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
7
|
+
m.elements['sFixFor'].text,
|
|
8
|
+
m.elements['dt'].text,
|
|
9
|
+
m.elements['sProject'].text).strip!
|
|
56
10
|
end
|
data/bin/fogbugz-open
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
require 'fogbugz/common'
|
|
3
|
+
|
|
4
|
+
options = parse_stdin_opts "usage: #{File::basename(__FILE__)} [options] [-]", 0
|
|
5
|
+
|
|
6
|
+
data = get_editor_content options, <<HERE
|
|
7
|
+
# Fill in metadata for the case.
|
|
8
|
+
# title: <title>
|
|
9
|
+
# assignee: <person>
|
|
10
|
+
# parent: <case>
|
|
11
|
+
# tags: [bug, enhancement]
|
|
12
|
+
# project: <project>
|
|
13
|
+
# area: <area>
|
|
14
|
+
# milestone: <milestone>
|
|
15
|
+
# category: <category>
|
|
16
|
+
# priority: <priority>
|
|
17
|
+
# due: <date>
|
|
18
|
+
# estimate: <hours>
|
|
19
|
+
|
|
20
|
+
# Enter an optional description of the case after the dashes.
|
|
21
|
+
---
|
|
22
|
+
HERE
|
|
23
|
+
|
|
24
|
+
params = {}
|
|
25
|
+
params[:sTitle] = data['title'] if data['title']
|
|
26
|
+
params[:ixBugParent] = data['parent'] if data['parent']
|
|
27
|
+
params[:sTags] = data['tags'].join(',') if data['tags']
|
|
28
|
+
params[:sProject] = data['project'] if data['project']
|
|
29
|
+
params[:sArea] = data['area'] if data['area']
|
|
30
|
+
params[:sFixFor] = data['milestone'] if data['milestone']
|
|
31
|
+
params[:sCategory] = data['category'] if data['category']
|
|
32
|
+
params[:sPersonAssignedTo] = data['assignee'] if data['assignee']
|
|
33
|
+
params[:sPriority] = data['priority'] if data['priority']
|
|
34
|
+
params[:dtDue] = parse_time(data['due']) if parse_time(data['due'])
|
|
35
|
+
params[:hrsCurrEst] = data['estimate'] if data['estimate']
|
|
36
|
+
params[:sEvent] = data['body'] if data['body']
|
|
37
|
+
|
|
38
|
+
result = do_api('new', params)
|
|
39
|
+
puts "Case #{result.elements['/response/case'].attributes['ixBug']} opened."
|
data/bin/fogbugz-people
CHANGED
|
@@ -1,55 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
+
require 'fogbugz/common'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
require 'uri'
|
|
6
|
-
require 'rexml/document'
|
|
7
|
-
require 'optparse'
|
|
8
|
-
|
|
9
|
-
api_url = ENV['FOGBUGZ_API_URL']
|
|
10
|
-
unless api_url
|
|
11
|
-
puts "Environment variable FOGBUGZ_API_URL must be set."
|
|
12
|
-
exit 1
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
api_token = ENV['FOGBUGZ_API_TOKEN']
|
|
16
|
-
unless api_token
|
|
17
|
-
puts "Environment variable FOGBUGZ_API_TOKEN must be set."
|
|
18
|
-
exit 1
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
options = {}
|
|
22
|
-
optparse = OptionParser.new do |opts|
|
|
23
|
-
opts.banner = "usage: #{File::basename(__FILE__)} [options]"
|
|
24
|
-
|
|
25
|
-
opts.on_tail('-h', '--help') do
|
|
26
|
-
puts optparse.help
|
|
27
|
-
exit 1
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
optparse.parse!
|
|
31
|
-
|
|
32
|
-
uri = URI format("#{api_url}?cmd=listPeople&token=%s", URI.escape(api_token))
|
|
33
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
|
34
|
-
if uri.scheme == 'https'
|
|
35
|
-
http.use_ssl = true
|
|
36
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
37
|
-
end
|
|
38
|
-
response = http.start { |h| h.request Net::HTTP::Get.new(uri.request_uri) }
|
|
39
|
-
if response.code != '200'
|
|
40
|
-
puts "HTTP request to #{api_url} failed with code #{response.code}."
|
|
41
|
-
exit 1
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
result = REXML::Document.new(response.body)
|
|
45
|
-
error = result.elements['/response/error']
|
|
46
|
-
if error
|
|
47
|
-
puts "Failed with error: #{error.text}."
|
|
48
|
-
exit 1
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
result.elements.each('/response/people/person') do |person|
|
|
4
|
+
parse_opts "usage: #{File::basename(__FILE__)} [options]", 0
|
|
5
|
+
do_api('listPeople').elements.each('/response/people/person') do |p|
|
|
52
6
|
puts format("%-30.30s %s\n",
|
|
53
|
-
|
|
54
|
-
|
|
7
|
+
p.elements['sFullName'].text,
|
|
8
|
+
p.elements['sEmail'].text).strip!
|
|
55
9
|
end
|