rbcurse 1.2.0.pre → 1.2.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.
- data/README.markdown +13 -2
- data/VERSION +1 -1
- data/examples/app.rb +1 -1
- data/examples/appemail.rb +5 -3
- data/examples/appgcompose.rb +5 -2
- data/examples/appgmail.rb +1 -0
- data/examples/sqlc.rb +17 -9
- data/examples/sqlm.rb +12 -7
- data/examples/test2.rb +7 -5
- data/examples/testmenu.rb +20 -20
- data/examples/testmulticomp.rb +1 -1
- data/examples/testree.rb +3 -3
- data/examples/testtodo.rb +22 -21
- data/examples/viewtodo.rb +13 -13
- data/lib/rbcurse/app.rb +5 -1
- data/lib/rbcurse/extras/bottomline.rb +1 -1
- data/lib/rbcurse/rbasiclistbox.rb +1 -1
- data/lib/rbcurse/rmulticontainer.rb +2 -2
- data/lib/rbcurse/rpopupmenu.rb +31 -27
- data/lib/rbcurse/rtable.rb +2 -1
- data/lib/rbcurse/rtextview.rb +1 -0
- metadata +16 -103
- data/examples/appcombo.rb +0 -17
- data/examples/cdir/testmultispl.rb +0 -150
- data/examples/focusmanager.rb +0 -31
- data/examples/imap.rb +0 -48
- data/examples/s.rb +0 -10
- data/examples/scrollbar.rb +0 -104
- data/examples/term.rb +0 -48
- data/examples/testgmail.rb +0 -46
data/examples/appcombo.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
require 'rbcurse/app'
|
2
|
-
|
3
|
-
App.new do
|
4
|
-
header = app_header "rbcurse 1.2.0", :text_center => "Combo Demo", :text_right =>"New Improved!", :color => :black, :bgcolor => :white, :attr => :bold
|
5
|
-
message "Press F1 to exit from here"
|
6
|
-
|
7
|
-
stack :margin_top => 2, :margin => 10, :width => 80 do
|
8
|
-
list = %w[ highline ncurses thor tzinfo rack mail]
|
9
|
-
flow do
|
10
|
-
label "a label"
|
11
|
-
a = combo :list => list, :color_pair => $reversecolor #:bgcolor => :white, :color => :black
|
12
|
-
end
|
13
|
-
blank
|
14
|
-
b = combo :list => list, :color_pair => $reversecolor # , :label => 'a bbael'
|
15
|
-
b.set_label "a label" # this is unaware of stack margin
|
16
|
-
end # stack
|
17
|
-
end # app
|
@@ -1,150 +0,0 @@
|
|
1
|
-
$LOAD_PATH << "/Users/rahul/work/projects/rbcurse/lib"
|
2
|
-
require 'rubygems'
|
3
|
-
require 'ncurses'
|
4
|
-
require 'logger'
|
5
|
-
require 'rbcurse'
|
6
|
-
require 'rbcurse/rmultisplit'
|
7
|
-
require 'rbcurse/rlistbox'
|
8
|
-
#
|
9
|
-
## this sample creates a multisplitpane with n objects
|
10
|
-
##+ and move divider around using - + and =.
|
11
|
-
#
|
12
|
-
if $0 == __FILE__
|
13
|
-
include RubyCurses
|
14
|
-
include RubyCurses::Utils
|
15
|
-
|
16
|
-
begin
|
17
|
-
# Initialize curses
|
18
|
-
VER::start_ncurses # this is initializing colors via ColorMap.setup
|
19
|
-
$log = Logger.new((File.join(ENV['LOGDIR'] || "./" ,"view.log")))
|
20
|
-
|
21
|
-
$log.level = Logger::DEBUG
|
22
|
-
|
23
|
-
@window = VER::Window.root_window
|
24
|
-
|
25
|
-
catch(:close) do
|
26
|
-
colors = Ncurses.COLORS
|
27
|
-
@form = Form.new @window
|
28
|
-
r = 3; c = 7; ht = 18
|
29
|
-
# filler just to see that we are covering correct space and not wasting lines or cols
|
30
|
-
filler = "*" * 88
|
31
|
-
(ht+2).times(){|i| @form.window.printstring(i,r, filler, $datacolor) }
|
32
|
-
|
33
|
-
|
34
|
-
@help = "q to quit. v h - + = : #{$0} . Check logger too"
|
35
|
-
RubyCurses::Label.new @form, {'text' => @help, "row" => ht+r, "col" => 2, "color" => "yellow"}
|
36
|
-
|
37
|
-
$message = Variable.new
|
38
|
-
$message.value = "Message Comes Here"
|
39
|
-
message_label = RubyCurses::Label.new @form, {'text_variable' => $message, "name"=>"message_label","row" => ht+r+2, "col" => 1, "display_length" => 60, "height" => 2, 'color' => 'cyan'}
|
40
|
-
$message.update_command() { message_label.repaint } # why ?
|
41
|
-
|
42
|
-
splitp = MultiSplit.new @form do
|
43
|
-
name "mypane"
|
44
|
-
row r
|
45
|
-
col c
|
46
|
-
width 70
|
47
|
-
height ht
|
48
|
-
split_count 3
|
49
|
-
unlimited true
|
50
|
-
# focusable false
|
51
|
-
orientation :VERTICAL_SPLIT
|
52
|
-
end
|
53
|
-
splitp.bind(:PROPERTY_CHANGE){|e| $message.value = e.to_s }
|
54
|
-
|
55
|
-
lists = []
|
56
|
-
mylist = Dir.glob('*')
|
57
|
-
listb = Listbox.new @form do
|
58
|
-
name "mylist"
|
59
|
-
list mylist
|
60
|
-
title "A short list"
|
61
|
-
title_attrib 'reverse'
|
62
|
-
end
|
63
|
-
listb.one_key_selection = false
|
64
|
-
splitp.add listb
|
65
|
-
listb.bind_key(13) {
|
66
|
-
#@status_row.text = "Selected #{tablelist.get_content()[tablelist.current_index]}"
|
67
|
-
table = "#{listb.get_content()[listb.current_index]}"
|
68
|
-
##table = table[0] if table.class==Array ## 1.9 ???
|
69
|
-
mylist = nil
|
70
|
-
if File.directory? table
|
71
|
-
d = Dir.new(table)
|
72
|
-
mylist = d.entries
|
73
|
-
end
|
74
|
-
if lists.empty?
|
75
|
-
listb = Listbox.new @form do
|
76
|
-
name "mylist"
|
77
|
-
list mylist
|
78
|
-
title table
|
79
|
-
title_attrib 'reverse'
|
80
|
-
end
|
81
|
-
splitp.add listb
|
82
|
-
lists << listb
|
83
|
-
else
|
84
|
-
l = lists.first
|
85
|
-
l.list_data_model.remove_all
|
86
|
-
l.list_data_model.insert 0, mylist
|
87
|
-
end
|
88
|
-
}
|
89
|
-
|
90
|
-
@form.repaint
|
91
|
-
@window.wrefresh
|
92
|
-
Ncurses::Panel.update_panels
|
93
|
-
counter = 0
|
94
|
-
while((ch = @window.getchar()) != ?q.getbyte(0) )
|
95
|
-
str = keycode_tos ch
|
96
|
-
case ch
|
97
|
-
when ?a.getbyte(0)
|
98
|
-
return
|
99
|
-
r = 0
|
100
|
-
mylist = []
|
101
|
-
counter.upto(counter+100) { |v| mylist << "#{v} scrollable data" }
|
102
|
-
counter+=100;
|
103
|
-
$listdata = Variable.new mylist
|
104
|
-
listb = Listbox.new @form do
|
105
|
-
name "mylist"
|
106
|
-
# list mylist
|
107
|
-
list_variable $listdata
|
108
|
-
selection_mode :SINGLE
|
109
|
-
#show_selector true
|
110
|
-
#row_selected_symbol "[X] "
|
111
|
-
#row_unselected_symbol "[ ] "
|
112
|
-
title "A short list"
|
113
|
-
title_attrib 'reverse'
|
114
|
-
#cell_editing_allowed true
|
115
|
-
end
|
116
|
-
#listb.insert 55, "hello ruby", "so long python", "farewell java", "RIP .Net"
|
117
|
-
#$listdata.value.insert 55, "hello ruby", "so long python", "farewell java", "RIP .Net"
|
118
|
-
#listb.list_data_model.insert 55, "hello ruby", "so long python", "farewell java", "RIP .Net", "hi lisp", "hi clojure"
|
119
|
-
splitp.add listb
|
120
|
-
when ?V.getbyte(0)
|
121
|
-
splitp.orientation(:VERTICAL_SPLIT)
|
122
|
-
#splitp.reset_to_preferred_sizes
|
123
|
-
when ?H.getbyte(0)
|
124
|
-
splitp.orientation(:HORIZONTAL_SPLIT)
|
125
|
-
#splitp.reset_to_preferred_sizes
|
126
|
-
when ?-.getbyte(0)
|
127
|
-
#splitp.set_divider_location(splitp.divider_location-1)
|
128
|
-
when ?+.getbyte(0)
|
129
|
-
#splitp.set_divider_location(splitp.divider_location+1)
|
130
|
-
when ?=.getbyte(0)
|
131
|
-
#splitp.set_resize_weight(0.50)
|
132
|
-
end
|
133
|
-
#splitp.get_buffer().wclear
|
134
|
-
#splitp << "#{ch} got (#{str})"
|
135
|
-
splitp.repaint # since the above keys are not being handled inside
|
136
|
-
@form.handle_key(ch)
|
137
|
-
@window.wrefresh
|
138
|
-
end
|
139
|
-
end
|
140
|
-
rescue => ex
|
141
|
-
ensure
|
142
|
-
@window.destroy if !@window.nil?
|
143
|
-
VER::stop_ncurses
|
144
|
-
p ex if ex
|
145
|
-
p(ex.backtrace.join("\n")) if ex
|
146
|
-
$log.debug( ex) if ex
|
147
|
-
$log.debug(ex.backtrace.join("\n")) if ex
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
data/examples/focusmanager.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
# Allow some objects to take focus when a certain key is pressed.
|
2
|
-
# This is for objects like scrollbars and dividers. We don't want these always
|
3
|
-
# getting focus, only sometimes when we want to resize panes.
|
4
|
-
# This will not only be included by Form but by containers such as Vimsplit
|
5
|
-
# or MasterDetail.
|
6
|
-
# Usage: the idea is that when you create dividers, you would add them to the FocusManager
|
7
|
-
# Thus they would remain non-focusable on creation. When hte user presses (say F3) then
|
8
|
-
# make_focusable is called, or toggle_focusable. Now user can press TAB and access
|
9
|
-
# these bars. When he is done he can toggle again.
|
10
|
-
# TODO: we might add a Circular class here so user can traverse only these objects
|
11
|
-
module RubyCurses
|
12
|
-
module FocusManager
|
13
|
-
extend self
|
14
|
-
attr_reader :focusables
|
15
|
-
# add a component to this list so it can be made focusable later
|
16
|
-
def add component
|
17
|
-
@focusables ||= []
|
18
|
-
@focusables << component
|
19
|
-
self
|
20
|
-
end
|
21
|
-
def make_focusable bool=true
|
22
|
-
@focusing = bool
|
23
|
-
@focusables.each { |e| e.focusable(bool) }
|
24
|
-
end
|
25
|
-
def toggle_focusable
|
26
|
-
return unless @focusables
|
27
|
-
alert "FocusManager Making #{@focusable.length} objects #{!@focusing} "
|
28
|
-
make_focusable !@focusing
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
data/examples/imap.rb
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
require 'net/imap'
|
2
|
-
require 'net/http'
|
3
|
-
|
4
|
-
def labels conn
|
5
|
-
(conn.list("", "%")+conn.list("[Gmail]/", "%")).inject([]) do |labels,label|
|
6
|
-
label[:name].each_line {|l| labels << l }
|
7
|
-
labels
|
8
|
-
end
|
9
|
-
end
|
10
|
-
def label_counts _labels
|
11
|
-
_labels.each { |e|
|
12
|
-
puts e
|
13
|
-
next if e == "[Gmail]"
|
14
|
-
imap.select e rescue next
|
15
|
-
u = imap.search('UNSEEN').count
|
16
|
-
s = imap.search('SEEN').count
|
17
|
-
puts " #{e} : #{u} #{s} "
|
18
|
-
}
|
19
|
-
end
|
20
|
-
@config={}
|
21
|
-
@config['server'] = 'imap.gmail.com'
|
22
|
-
@config['port'] = 993
|
23
|
-
@config['username'] = "rahul2012@gmail.com"
|
24
|
-
@config['password'] = "rahulku234"
|
25
|
-
imap = Net::IMAP.new(@config['server'],@config['port'],true)
|
26
|
-
imap.login(@config['username'], @config['password'])
|
27
|
-
puts "connceted -------"
|
28
|
-
_labels = labels(imap)
|
29
|
-
puts _labels
|
30
|
-
puts "-------"
|
31
|
-
imap.select('INBOX')
|
32
|
-
ctr = 0
|
33
|
-
imap.search(["SEEN"]).each do |message_id|
|
34
|
-
ctr += 1
|
35
|
-
puts message_id
|
36
|
-
x = imap.fetch(message_id, "RFC822")[0].attr["RFC822"]
|
37
|
-
puts x.class
|
38
|
-
msg = x.split("\n")
|
39
|
-
puts msg.grep /^Subject:/
|
40
|
-
puts msg.grep /^From:/
|
41
|
-
puts msg.grep /^Date:/
|
42
|
-
|
43
|
-
#break if ctr >= 5
|
44
|
-
#MailFetcher.receive(imap.fetch(message_id, "RFC822")[0].attr["RFC822"])
|
45
|
-
#imap.store(message_id, "+FLAGS", [:Deleted])
|
46
|
-
end
|
47
|
-
imap.logout()
|
48
|
-
imap.disconnect()
|
data/examples/s.rb
DELETED
data/examples/scrollbar.rb
DELETED
@@ -1,104 +0,0 @@
|
|
1
|
-
require 'rbcurse/app'
|
2
|
-
include Ncurses
|
3
|
-
include RubyCurses
|
4
|
-
|
5
|
-
# This paints a vertical white bar given row and col, and length. It also calculates and prints
|
6
|
-
# a small bar over this based on relaetd objects list.length and current_index.
|
7
|
-
# Typically, after setup one would keep updating only current_index from the repaint method
|
8
|
-
# of caller or in the traversal event. This would look best if the listbox also has a reverse video border, or none.
|
9
|
-
# @example
|
10
|
-
# lb = list_box ....
|
11
|
-
# sb = Scrollbar.new @form, :row => lb.row, :col => lb.col, :length => lb.height, :list_length => lb.list.length, :current_index => 0
|
12
|
-
# .... later as user traverses
|
13
|
-
# sb.current_index = lb.current_index
|
14
|
-
#
|
15
|
-
# At a later stage, we will integrate this with lists and tables, so it will happen automatically.
|
16
|
-
#
|
17
|
-
# @since 1.2.0 UNTESTED
|
18
|
-
module RubyCurses
|
19
|
-
class Scrollbar < Widget
|
20
|
-
# row to start, same as listbox, required.
|
21
|
-
dsl_property :row
|
22
|
-
# column to start, same as listbox, required.
|
23
|
-
dsl_property :col
|
24
|
-
# how many rows is this (should be same as listboxes height, required.
|
25
|
-
dsl_property :length
|
26
|
-
# vertical or horizontal currently only VERTICAL
|
27
|
-
dsl_property :orientation
|
28
|
-
# to take changes or data, unused as of now
|
29
|
-
dsl_property :parent
|
30
|
-
# which row is focussed, current_index of listbox, required.
|
31
|
-
dsl_property :current_index
|
32
|
-
# how many total rows of data does the list have, same as @list.length, required.
|
33
|
-
dsl_property :list_length
|
34
|
-
|
35
|
-
|
36
|
-
def initialize form, config={}, &block
|
37
|
-
|
38
|
-
# setting default first or else Widget will place its BW default
|
39
|
-
#@color, @bgcolor = ColorMap.get_colors_for_pair $bottomcolor
|
40
|
-
super
|
41
|
-
@color_pair = get_color $datacolor, @color, @bgcolor
|
42
|
-
@scroll_pair = get_color $bottomcolor, :green, :white
|
43
|
-
@window = form.window
|
44
|
-
@editable = false
|
45
|
-
@focusable = false
|
46
|
-
@row ||= 0
|
47
|
-
@col ||= 0
|
48
|
-
@repaint_required = true
|
49
|
-
@orientation = :V
|
50
|
-
end
|
51
|
-
|
52
|
-
##
|
53
|
-
# XXX need to move wrapping etc up and done once.
|
54
|
-
def repaint
|
55
|
-
raise ArgumentError, "current_index must be provided" unless @current_index
|
56
|
-
raise ArgumentError, "list_length must be provided" unless @list_length
|
57
|
-
my_win = @form ? @form.window : @target_window
|
58
|
-
@graphic = my_win unless @graphic
|
59
|
-
return unless @repaint_required
|
60
|
-
|
61
|
-
# first print a right side vertical line
|
62
|
-
bordercolor = @border_color || $datacolor
|
63
|
-
borderatt = @border_attrib || Ncurses::A_REVERSE
|
64
|
-
|
65
|
-
|
66
|
-
@graphic.attron(Ncurses.COLOR_PAIR(bordercolor) | borderatt)
|
67
|
-
@graphic.mvvline(@row+0, @col, 1, @length-0)
|
68
|
-
@graphic.attroff(Ncurses.COLOR_PAIR(bordercolor) | borderatt)
|
69
|
-
|
70
|
-
# now calculate and paint the scrollbar
|
71
|
-
pht = @length
|
72
|
-
listlen = @list_length * 1.0
|
73
|
-
@current_index = 0 if @current_index < 0
|
74
|
-
@current_index = listlen-1 if @current_index >= listlen
|
75
|
-
sclen = (pht/listlen)* @length
|
76
|
-
scloc = (@current_index/listlen)* @length
|
77
|
-
scloc = (@length - sclen) if scloc > @length - sclen # don't exceed end
|
78
|
-
@graphic.attron(Ncurses.COLOR_PAIR(@scroll_pair) | borderatt)
|
79
|
-
r = @row + scloc
|
80
|
-
c = @col + 0
|
81
|
-
@graphic.mvvline(r, c, 1, sclen)
|
82
|
-
@graphic.attroff(Ncurses.COLOR_PAIR(@scroll_pair) | borderatt)
|
83
|
-
@repaint_required = false
|
84
|
-
end
|
85
|
-
##
|
86
|
-
##
|
87
|
-
# ADD HERE
|
88
|
-
end
|
89
|
-
end
|
90
|
-
App.new do
|
91
|
-
r = 5
|
92
|
-
len = 20
|
93
|
-
hline :width => 20, :row => r, :attrib => Ncurses::A_REVERSE
|
94
|
-
sb = Scrollbar.new @form, :row => r, :col => 20, :length => len, :list_length => 50, :current_index => 0
|
95
|
-
hline :width => 20, :row => len+r
|
96
|
-
keypress do |ch|
|
97
|
-
case ch
|
98
|
-
when :down
|
99
|
-
sb.current_index += 1
|
100
|
-
when :up
|
101
|
-
sb.current_index -= 1
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
data/examples/term.rb
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
require 'rbcurse/app'
|
2
|
-
require 'terminal-table/import'
|
3
|
-
|
4
|
-
App.new do
|
5
|
-
header = app_header "rbcurse 1.2.0", :text_center => "**** Demo", :text_right =>"New Improved!", :color => :black, :bgcolor => :white, :attr => :bold
|
6
|
-
message "Press F1 to escape from here"
|
7
|
-
|
8
|
-
stack :margin_top => 2, :margin => 5, :width => 30 do
|
9
|
-
t = Terminal::Table.table(['a', 'b'], [1, 2], [3, 4])
|
10
|
-
$log.debug "YYY table #{t.class} "
|
11
|
-
list_box :list => t.render2
|
12
|
-
t = Terminal::Table.table ['a', 'b']
|
13
|
-
t << [1, 2]
|
14
|
-
t << [3, 4]
|
15
|
-
t << :separator
|
16
|
-
t << [4, 6]
|
17
|
-
#list_box :list => t.to_s.split("\n")
|
18
|
-
list_box :list => t.render2
|
19
|
-
end # stack
|
20
|
-
t = Terminal::Table.table do |t|
|
21
|
-
t.headings = 'First Name', 'Last Name', 'Email'
|
22
|
-
t << %w( TJ Holowaychuk tj@vision-media.ca )
|
23
|
-
t << %w( Bob Someone bob@vision-media.ca )
|
24
|
-
t << %w( Joe Whatever bob@vision-media.ca )
|
25
|
-
end
|
26
|
-
#t = t.to_s.split(/[|+]\n/)
|
27
|
-
t = t.render2
|
28
|
-
wid = t[0].length + 2
|
29
|
-
stack :margin_top => 2, :margin => 35, :width => wid do
|
30
|
-
list_box :list => t
|
31
|
-
t = Terminal::Table.table do
|
32
|
-
self.headings = 'First Name', 'Last Name', 'Email'
|
33
|
-
add_row ['TJ', 'Holowaychuk', 'tj@vision-media.ca']
|
34
|
-
add_row ['Bob', 'Someone', 'bob@vision-media.ca']
|
35
|
-
add_row ['Joe', 'Whatever', 'joe@vision-media.ca']
|
36
|
-
add_separator
|
37
|
-
add_row ['Total', { :value => '3', :colspan => 2, :alignment => :right }]
|
38
|
-
align_column 1, :center
|
39
|
-
end
|
40
|
-
#lb = list_box :list => t.render2
|
41
|
-
lb = textview :set_content => t.render2, :height => 10
|
42
|
-
lb.bind(:PRESS){|tae|
|
43
|
-
alert "Pressed list on #{tae.word_under_cursor(nil, nil, "|")} "
|
44
|
-
}
|
45
|
-
# make a textview that is vienabled by default.
|
46
|
-
end
|
47
|
-
|
48
|
-
end # app
|
data/examples/testgmail.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
require 'gmail'
|
2
|
-
$b = Time.now
|
3
|
-
def p
|
4
|
-
s= $b
|
5
|
-
$b = Time.now
|
6
|
-
" ----------> " + ($b - s).to_s
|
7
|
-
end
|
8
|
-
username = ENV['GMAIL_USER']+"@gmail.com"
|
9
|
-
pass = ENV['GMAIL_PASS']
|
10
|
-
puts "b4 connect " #+ p()
|
11
|
-
gmail = Gmail.connect!(username, pass)
|
12
|
-
puts "connected " + p()
|
13
|
-
puts gmail.labels.all
|
14
|
-
$b = Time.now
|
15
|
-
gmail.label('INBOX') do |mailbox|
|
16
|
-
urc = mailbox.count(:unread)
|
17
|
-
puts "unread: #{urc} " + p()
|
18
|
-
all = mailbox.count(:all)
|
19
|
-
puts "all: #{all} " + p()
|
20
|
-
x = mailbox.emails(:all)
|
21
|
-
puts "got x " + p()
|
22
|
-
imap = gmail.connection
|
23
|
-
puts "imap #{imap.class} #{imap} "
|
24
|
-
uids = []
|
25
|
-
#mailbox.emails(:all) do |email|
|
26
|
-
#uid = email.uid
|
27
|
-
#envelope = gmail.connection.uid_fetch(uid, "ENVELOPE")[0].attr["ENVELOPE"]
|
28
|
-
#puts envelope.subject
|
29
|
-
##print " #{email['From']} "
|
30
|
-
##print " #{email.header} "
|
31
|
-
#end
|
32
|
-
mailbox.emails(:all) do |email|
|
33
|
-
uid = email.uid
|
34
|
-
uids << uid
|
35
|
-
#puts envelope.subject
|
36
|
-
#print " #{email['From']} "
|
37
|
-
#print " #{email.header} "
|
38
|
-
end
|
39
|
-
envelopes = gmail.connection.uid_fetch(uids, "ENVELOPE")
|
40
|
-
envelopes.each { |ee|
|
41
|
-
e = ee.attr["ENVELOPE"]
|
42
|
-
puts e.subject
|
43
|
-
}
|
44
|
-
puts
|
45
|
-
puts "finished loop " + p()
|
46
|
-
end
|