Related
Open-uri and nokogiri are slow to scrape the site I want hence the Net::OpenTimeout execution expired error. I attempted to code a custom error with rescue however I do not know what condition I can look for to raise that custom error.
I attempted to few if else statements however I really just guessed how to check if I was gonna get that error. I hard coded a condition that failed and thus rescued the error. I am very new to ruby and custom errors. In fact this is my first.
class Scrape
Base = 'http://www.wine.com'
##menu = []
##pages = []
def self.index
index_url = Base + "/list/wine/7155?sortBy=savings&pricemax=90"
#below is where I need to check for the condition to
raise the error
if doc = Nokogiri::HTML(open(index_url))
container = doc.css('.prodList')
wines = container.css('.prodItem')
wines.each do |wine|
##menu << {
:link => wine.css('.prodItemInfo_link').attribute('href').value,
:name => wine.css('.prodItemInfo_name').text,
:rating => (wine.css('.averageRating_average').text.to_i) > 0 ?
(wine.css('.averageRating_average').text) : 'no rating',
:price => wine.css('.productPrice_price-saleWhole').text.strip
}
end
##menu.each do |item|
Bottle.new.create(item)
end
else
begin
raise Custom_error
rescue Custom_error => error
puts error.message
end
end
end
def self.scrape_page(wine_obj)
wine_link = wine_obj.link
individual_page = Base + wine_link
docu = Nokogiri::HTML(open(individual_page))
y = docu.css('.viewMoreModule_text')
more = docu.css('.viewMoreModule_text')
##pages << {
:obj => wine_obj,
:name => docu.css('.pipName').text,
:alcohol_percent => y
x = docu.css('.mobileProdAttrs').css('.prodAlcoholPercent')
y = x.css('.prodAlcoholPercent_percent').text,
:price => docu.css('span.productPrice_price-saleWhole').text,
:origin => docu.css('span.prodItemInfo_originText a').text,
:winemaker_notes => docu.css('.viewMoreModule_text').first.text,
:more => y[2].text,
:rating => docu.css('span.averageRating_average').first.text
}
Page.create_find_by_name( ##pages.last )
end
def self.pages
##pages
end
end
class Cli
def run
puts 'loading from cyberspace'
Scrape.index
Bottle.make_list
controller
end
def controller
input = ''
response = ''
puts ' '
view
while input != 11
response = gets.chomp.to_i
input = "#{response}11".to_i
if input == 111
menu
elsif input == 11
exit
elsif input > 0 && input < 26
find_by_input(input)
elsif input != 0 && input != 111
error_1
end
end
end
def view
puts "welcome to the wine bar"
puts "================="
puts " W I N E "
puts " B A R "
puts "================="
puts " "
puts "type 1 for list of wine"
puts " "
puts "type 0 to exit "
end
def menu
wines = Bottle.list
second_input = ''
while second_input != 0
puts "<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>"
puts "type the corresponding number to view more wine info"
puts
"__________________________________________________________________"
wines.each do |wine|
puts "#{wine.index}) #{wine.name} #{wine.price}"
end
second_input = gets.chomp.to_i
if second_input > 0 && second_input < 26
find_by_input(second_input)
elsif second_input == 0
exit
second_input = 0
elsif second_input > 25 && second_input != 101
error_1
end
end
puts <<-DOC
the the wine number again
for winemaker notes
DOC
end
def find_by_input(input)
while input != 0
selection = Bottle.find_by_input(input)
puts "NAME: #{selection.name}"
puts "PRICE: $#{selection.price}"
puts "RATING: #{selection.rating}"
puts "________________________________________"
puts " type #{input} again "
puts " for more info "
puts " provided by the winemaker "
# reseting input and extending user control functionality
third_input = ''
third_input = gets.chomp.to_i
if third_input == selection.index
response = Scrape.scrape_page(selection)
view_2(response, third_input)
elsif input == 0
exit
end
end
end
def view_2(response, old_input)
next_input = ''
while next_input != 0
puts "Alcohol Percent: #{response.alcohol_percent}"
puts "Winemaker Notes: #{response.winemaker_notes}"
puts " "
puts "Type #{old_input} again for more!!"
next_input = gets.chomp.to_i
if next_input == old_input
input = 0
next_input = 0
# refacort as it puts out 88 again and should not. Also 0 is not
exiting with correct behavior
# refactor so looking for "#{input}"1 to prevent the recall of
input
more(response)
end
end
end
def more(response)
puts response.more
puts menu
end
def error_1
puts " WHOA coder "
puts "type a better number!"
end
def exit
puts <<-DOC
well that was fun
Thank you for checking out
my first cli program
DOC
end
end ```
```class Page
attr_accessor :alcohol_percent, :price, :name, :origin, :winemaker_notes,
:rating, :more, :obj
##web_pages = []
def self.create_find_by_name(hash)
if answer = ##web_pages.find{ |obj| obj.name == hash[:name]}
answer
else
self.new.create(hash)
end
end
def create(hash)
hash.each do |key, value|
self.send(("#{key}="), value)
end
save
view_more
end
def view_more
##web_pages.last
end
def save
##web_pages << self
end
end
attr_accessor :link, :name, :price, :rating, :index
##bottles = []
def create(hash)
hash.each do |key, words|
self.send(("#{key}="), words )
end
save
end
def save
##bottles << self
end
def self.make_list
##numbered_list = ##bottles.sort{ |x,y| x.price <=>
y.price}.map.with_index(1) do
|w,i| w.index = i
w
end
end
def self.list
##numbered_list
end
def self.find_by_input(input)
a = ##numbered_list.find{ |wine| wine.index == input}
# puts "#{a.name} $#{a.price} rating: #{a.rating}"
# puts "type #{input} again for winemaker notes"
# more = ''
# while more != 0
# more = gets.chomp.to_i
# (input == more) ? (Scrape.scrape_page(a.link)) : (self.list)
# end
end
end
class Scrape
Base = 'http://www.wine.com'
##menu = []
##pages = []
def self.index
index_url = Base + "/list/wine/7155?sortBy=savings&pricemax=90"
if doc = Nokogiri::HTML(open(index_url))
container = doc.css('.prodList')
wines = container.css('.prodItem')
wines.each do |wine|
##menu << {
:link => wine.css('.prodItemInfo_link').attribute('href').value,
:name => wine.css('.prodItemInfo_name').text,
:rating => (wine.css('.averageRating_average').text.to_i) > 0 ?
(wine.css('.averageRating_average').text) : 'no rating',
:price => wine.css('.productPrice_price-saleWhole').text.strip
}
end
##menu.each do |item|
Bottle.new.create(item)
end
else
begin
raise Custom_error
rescue Custom_error => error
puts error.message
end
end
end
def self.scrape_page(wine_obj)
wine_link = wine_obj.link
individual_page = Base + wine_link
docu = Nokogiri::HTML(open(individual_page))
y = docu.css('.viewMoreModule_text')
more = docu.css('.viewMoreModule_text')
##pages << {
:obj => wine_obj,
:name => docu.css('.pipName').text,
alcholo = docu.css('.mobileProdAttrs').css('.prodAlcoholPercent'),
:alcohol_percent => alcholo.css('.prodAlcoholPercent_percent').text,
:price => docu.css('span.productPrice_price-saleWhole').text,
:origin => docu.css('span.prodItemInfo_originText a').text,
:winemaker_notes => docu.css('.viewMoreModule_text').first.text,
:more => y[2].text,
:rating => docu.css('span.averageRating_average').first.text
}
Page.create_find_by_name( ##pages.last )
end
def self.pages
##pages
end
end
When the internet connection is down/too slow the custom error is raised.
When an exception is thrown, the program stops its normal flow. You need to surround the part of the code that can throw an exception with a begin..rescue clause, and attempt to handle it, re-raise it, or raise another exception instead.
In your example, that would be:
begin
Nokogiri::HTML(open(url))
rescue Net::OpenTimeoutError => e
# log the error message if needed, raise your CustomError instead
raise CustomError, e.message
end
You can omit the begin, and put a rescue clause at the end of the method, ruby will interpret this as if the entire method body was wrapped in a begin..rescue block, something like this:
def open_page(url)
return Nokogiri::HTML(open(url))
rescue Net::OpenTimeoutError => e
raise CustomError, e.message
end
I have this program I wrote to automate a task I have at work. This is my first attempt at programming, with zero experience or training so forgive any silly mistakes. (I removed the website and my username/password)
require "rubygems"
require "selenium-webdriver"
require "nokogiri"
browser = Selenium::WebDriver.for :firefox
wait = Selenium::WebDriver::Wait.new(:timeout => 15)
#loads to unassigned results page
browser.get "-----------------"
p browser.title
browser.find_element(name: "user[username]").send_keys "--------"
browser.find_element(name: "user[password]").send_keys "--------"
browser.find_element(name: "commit").click
p browser.title
browser.find_element(class_name: "status_notification").click
begin
browser.find_element(:xpath => ".//*[#id='sub_nav_content']/table/tbody/tr[2]/td[3]/a").click
table = wait.until {
element = browser.find_element(id: "possible_matched")
element if element.displayed?
}
if table
puts "Table Found"
else
puts "Table Error"
end
#creates an 2D array containing patient name, admit date and prints to screen
names = browser.find_elements(:xpath => ".//*[#id='possible_matched']/table/tbody/tr/td[1]")
name_array = []
names.each { |name| name_array << name.text}
admits = browser.find_elements(:xpath => ".//*[#id='possible_matched']/table/tbody/tr/td[5]")
admit_array = []
admits.each { |date| admit_array << date.text }
name_admit_array = name_array.zip(admit_array)
name_admit_array.each do |name, date|
puts "#{name}: #{date}"
end
#finds the location of the sub-array containing patient name and collection associated admit date
patient_name = browser.find_element(:xpath => ".//*[#id='dialog-modal-cancel-hl7-preview']/table/tbody/tr[2]/td[1]").text
collected_date = browser.find_element(:xpath => ".//*[#id='dialog-modal-cancel-hl7-preview']/table/tbody/tr[2]/td[4]").text
mo, da, yr = collected_date.split('/').map(&:to_i)
cd = [yr, mo, da]
bl = name_admit_array.each_with_index.select { |(name, date), i|
m, d, y = date.split('/').map(&:to_i)
dt = [y, m, d]
name.downcase == patient_name.downcase and (dt <=> cd)<0
}.map {|x, i| i }
# presses the button associated with the correct sub-array
blf = name_admit_array.values_at(*bl)
if bl.any?
bf = blf.rindex(blf.max) + 2
browser.find_element(:xpath => ".//*[#id='possible_matched']/table/tbody/tr[#{bf}]/td[6]/div/a").click
else
browser.find_element(:xpath => "html/body/div[6]/div[1]/a/span").click
end
end while bl.any?
puts "no name :("
So it runs the loop until there is nothing found in the array bl. What I want to do is have this loop run again but with the next link on the list of links. So at the beginning of the loop it should do browser.find_element(:xpath => ".//*[#id='sub_nav_content']/table/tbody/tr[3]/td[3]/a").clickinstead of browser.find_element(:xpath => ".//*[#id='sub_nav_content']/table/tbody/tr[2]/td[3]/a").click. Then is should run the rest of the loop in the same way. I want it to continue to increment tr[] each time the loop runs into br.any? => false.
you can create an array of xpaths, and run this code for each of them:
[
".//*[#id='sub_nav_content']/table/tbody/tr[3]/td[3]/a",
".//*[#id='sub_nav_content']/table/tbody/tr[2]/td[3]/a"
].each do |path|
begin
browser.find_element(:xpath => path).click
// etc....
end while bl.any?
end
I believe that more changes need to adjust your code reusable, depending on your needs
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I have gotten this syntax error message:
/usr/src/check_in/lib/check_in.rb:105: syntax error, unexpected keyword_end, expecting '}'
class CheckIn < Adhearsion::CallController
def run
answer
#play 'welcome.wav' # "Welcome to the PSG check in application."
logger.info "#{call.from.to_s} called at #{time.now.to_s}"
if verify_phone(call.from) == false # If it's not a site phone
logger.info "Number not recognised, routing to Ops"
#play 'not_site_phone.wav' # "Number not recognized."
#dial(sip:number) # Dial operations
hangup
else
user = verify_uid # Checks the User Id
end
if to_check_out?(user.uid)
check_out(user.uid)
else
update_shift(user.uid)
#play 'thank_you.wav'
end
end
def verify_uid
count = 1 # Generic count variable
input = ask 'enter_uid.wav', :limit => 5, :timeout => 5.seconds #takes user ID as DTMF
while (count <= 3 ) # Tracks the number of attempts
if User.find_by_uid(input.response) == nil # If user.find doesn't return anything
logger.info "Invalid user ID. ID entered = #{input.response} Attepmt = #{count}"
input = ask "please_try_again.wav", :limit => 5, :timeout => 10.seconds
count += 1 # Get user to try again
elsif count == 3
play "try_again_later.wav" #"Please try you again later"
logger.info "Tries exceeded, played try again later."
hangup
else
#user = User.find_by_uid(input) # Assigns the user variable to return.
logger.info "User ID: #{#user.uid} is valid"
return #user
end
end
end
def verify_phone(caller_id)
if Sites.find_by_site_phone(caller_id) != nil
return true
else
return false
end
end
def update_shift (user_id)
#user = User.find_by_uid(user_id) # Grabs the user object
if (#user.checked_in?) # If the user is already checked in assign the relevant attributes
#user.time_olc = time.now
#user.num_ci += 1
#user.check_in.create_check_in(:site_id => #user.site_id, uid => #user.uid, :time => time.now)
logger.info "#{#user.uid} checked in at #{#user.time_olc}"
else # Otherwise set all the attributes for the shift
#user.update_attributes(:checked_in? => true, :time_in => time.now, :time_olc => time.now, :num_ci => 1, :site_id => #user.site.find_by_site_phone(call.from))
#user.check_in.create_check_in(:site_id => #user.site_id, uid => #user.uid, :time => time.now)
logger.info "#{#user.uid} punched in at #{#user.time_in}"
end
end
def to_check_out?(user_id) # Method to see if the user has reached check out
#user = User.find_by_uid(user_id)
num_of_ci = #user.num_ci + #user.num_notifications
if (num_of_ci >= #user.site.shift_len)
return true
else
return false
end
end
def check_out!(user_id)
#user = User.find_by_uid(user_id)
input = ask 'check_out?.wav', :limit => 1, :timeout => 5.seconds
if (input == 1)
update(#user.uid)
else
#user.time_out = time.now
#user.time_olc = time.now
#user.update_attributes(:checked_in => false)
end
report_for_user(uid)
end
def secure
count = 1
play 'secure.wav'
sec = 5.times.map {Random.rand(0..9)
result = ask %w"#{sec[0]}, #{sec[1]}, #{sec[2]}, #{sec[3]}, #{sec[4]}", :limit => 5, :timeout => 5.seconds
while (count <= 3)
if (sec.join == result.response)
logger.info "Check in valid"
return true
elsif (count < 3)
result = ask 'please_try_again.wav', :limit => 5, :timeout => 5.seconds
else
play 'try_again_later.wav'
hangup
end
end
end
def report_for_user(user_id)
#user = Users.find_by_uid(user_id)
report = file.open("/report/#{user_id}-#{#user.site_id}-#{date.current}",w)
user_check_ins = #user.check_in.all
report.write("Time, Site ID, User ID")
user_check_ins.each do |check_in|
report.write("#{user.check_in.time}, #{user.check_in.site_id}, #{user.checkin.uid}")
check_in.destroy
end
end
def notify
foo = Users.all
foo.each do |user|
if user.checked_in?
t1 = Time.now #sets a time place holder
t2 = user.time_olc
convert_time = (t1.hour * 60 * 60) + (t1.min * 60) + t1.sec
convert_tolc = (t2.hour * 60 * 60) + (t2.min * 60) + t1.sec
if ((convert_time - convert_tolc)/60 > 75)
user.num_notification += 1
a = user.uid.to_s
logger.info "#{user.uid} hasn't checked in this hour"
# Adhearsion::OutboundCall.originate '+tel: Mobile' :from => ' ' do
# answer
# play a[0], a[1], a[2], a[3], a[4], 'has_not_checked_in.wav'
# hangup
# end
end
end
end
end
end
In this method you have opened one curly bracket '{' but not closed it.
def secure
count = 1
play 'secure.wav'
sec = 5.times.map {Random.rand(0..9)
result = ask %w"#{sec[0]}, #{sec[1]}, #{sec[2]}, #{sec[3]}, #{sec[4]}", :limit => 5, :timeout => 5.seconds
while (count <= 3)
if (sec.join == result.response)
logger.info "Check in valid"
return true
elsif (count < 3)
result = ask 'please_try_again.wav', :limit => 5, :timeout => 5.seconds
else
play 'try_again_later.wav'
hangup
end
end
end
you are missing a closing brace in secure method at the following line
sec = 5.times.map {Random.rand(0..9)
Hey, all. I'm working on making a GUI for a Ruby project using Shoes.
I've got a class called Manager (as in memory manager) that loads a 'process list' from a file, splits it up and assigns things to different 'pages' in memory when a certain execution call is made. I really don't think this part matters too much, though. It all works as a terminal application just fine.
However, Shoes is just baffling me. Here's what I've got so far:
Shoes.app(:title => "Paging Simulator", :width => 800, :height => 450) do
#manager = Manager.new
stack(:width => 200) do
#exec_list = stack {
title "Execution Queue", :size => 14
#exec_lines = para "click button to load", :size => 9
#file_button = button "Load Process List"
#file_button.click {
filename = ask_open_file
# #manager.set_exec_list filename
# alert "this makes no sense"
#exec_lines.text = #manager.exec_list.join "\n"
# exec_lines.text = File.read filename
}
}
end
end
What happens when I run this:
The program view loads as expected. I get a header, a paragraph that says "click button....", and a button. I click the button and I select the file. But this is where things get weird.
If I run the last commented line exec_lines.text = File.read filename it does as I would like, but my manager doesn't get any of the information it needs.
If I run the #manager.set_exec_list filename line, nothing from that line on in the block gets run, including the alert, or any other code I try to put in there.
if I run as shown above, however, I get the output I expect, but I don't get to set my data from the file that I select.
I've tried to figure this out from the Shoes Rules page, but this doesn't seem to be an issue that they address, and their "it changes/doesn't change self" I think I grasp, but it's confusing and I don't think it's exactly related to this problem.
Does anyone have any idea how to get this to work? I'm kind of down to crunch time on this project and I can't seem to get any other Ruby GUI toolkit to even run, so I think I'm pretty stuck with Shoes.
Thanks.
Update
I've tried running ruby-debug on the code when I make the call to #manager.set_exec_list filename, and stepping through it shows that this call is made, but the code never actually (from what I can tell) jumps into that method, and acts like it's the last line of code in the block. Do I need to include these classes inside the Shoes.app block?
Update Nope. That does nothing different.
update fullsource code follows:
#!/usr/bin/env shoes
require 'rubygems'
require 'ruby-debug'
class MemSegment
attr_accessor :filled, :pid, :seg, :seg_id
def initialize(filled=false, pid=nil, seg=nil, seg_id=0)
#filled = filled
#pid = pid.to_i
#seg = seg.to_s
#seg_id = seg_id.to_i
self
end
def fill(pid, seg, seg_id)
#filled = true; #pid = pid; #seg = seg; #seg_id = seg_id;
self
end
def clear
self.filled = false; self.pid = nil; self.seg = nil;
self
end
def filled?
#filled
end
def to_s
filled? ? "#{seg} #{seg_id} for pid #{pid}" : "Free"
end
end
class SimProc
include Enumerable
attr_accessor :pid, :code, :data
def initialize(pid, code, data)
#pid = pid.to_i
#code = code.to_i
#data = data.to_i
end
def each
yield :code, code
yield :data, data
end
def to_s
"[SimProc :pid => #{pid}, :code => #{code}, :data => #{data}]"
end
def to_a
[#pid, #code, #data]
end
end
class Manager
attr_reader :segments, :processes, :exec_list, :exec_object
def initialize
#exec_list = [[1, 2], [3, 4], [5, 6]]
#processes = {}
#segments = Array.new(8) { MemSegment.new }
end
def print_activity
#segments.each_with_index {|s, index| puts "Seg #{index} => #{s}" }
#processes.each_value {|s| puts s }
end
def load_process(pcb, exec_index)
if pcb.size == 3
p = SimProc.new(*pcb)
bad_load = false
#processes.store p.pid, p
#processes[p.pid].each do |proc_seg, bsize|
(bsize / 512.0).ceil.times do |seg_id|
#segments.each_with_index do |s, index|
if !s.filled
#find the first empty memory segment
s.fill p.pid, proc_seg, seg_id
break
# if all slots are filled and we couldn't place a proc block
elsif index == #segments.size - 1
bad_load = true
puts "Cannot find a place for #{proc_seg} segment of size #{bsize}. Requeueing..."
break;
end
end
break if bad_load
end
end
# recover pages and queue the process for later
if bad_load
#segments.each_with_index do |seg, seg_index|
# clear any segments that didn't get loaded properly
if seg.pid == p.pid
seg.clear
puts "Seg #{seg_index} => segment cleared: #{seg}"
end
end
# reinsert this process after the next in the execution list
# it will attempt to load and run after the next process is performed
#exec_list.insert(exec_index + 2, p.to_a)
end
print_activity
elsif pcb.size == 2 and pcb[1] == -1
# a process is exiting
puts "removing pid #{pcb[0]}"
#segments.each { |s| s.clear if s.pid == pcb[0] }
#processes.delete pcb[0]
print_activity
end
end
def set_exec_list(filename)
file = File.open filename
file.each { |pcb| #exec_list << pcb.split.map(&:to_i) } unless file.nil?
filename
end
def main
exseq = File.open('exseq2.txt')
set_exec_list exseq
# this is the object that will be used to run each process with .next
#exec_object = #exec_list.each_with_index
# #exec_list.each_with_index { |pcb, exec_index| load_process(pcb, exec_index) }
(#exec_list.size + 1).times do
load_process(*#exec_object.next)
end
end
end
=begin
manager = Manager.new
manager.main
=end
#=begin
Shoes.app(:title => "Paging Simulator", :width => 800, :height => 450) do
#manager = Manager.new
stack(:width => 200) do
#exec_list = stack {
title "Execution Queue", :size => 14
#exec_lines = para "click button to load", :size => 9
#file_button = button "Load Process List"
debugger
#file_button.click {
filename = ask_open_file
#manager.set_exec_list filename
# alert "this makes no sense"
# #exec_lines.text = #manager.exec_list
# #exec_lines.text = File.read filename
#exec_lines.text = #manager.exec_list.join "\n"
}
}
end
end
#=end
So, a few things:
#1, I don't have the implementation of Manager, so I can't tell you why it breaks. Did you try checking the Shoes console for any errors? Hit control-/ to bring that up. If 'nothing runs after it hits that line,' that's probably the issue.
#2, this does work for me, as long as you change exec_lines to #exec_lines on the last line. Here's what I tried:
class Manager;end
Shoes.app(:title => "Paging Simulator", :width => 800, :height => 450) do
#manager = Manager.new
stack(:width => 200) do
#exec_list = stack {
title "Execution Queue", :size => 14
#exec_lines = para "click button to load", :size => 9
#file_button = button "Load Process List"
#file_button.click {
filename = ask_open_file
#alert "this makes no sense"
#exec_lines.text = File.read filename
}
}
end
end
Hope that helps!
I have a working TCP/IP socket server that loads 3-flash files in succession. How can I unload previous files and eliminate the garbage build up?
2-Flash clients are active, 1-the loader, 2-the next Flash file being loaded, however "the Flash files don't unload." Maybe there's a "put - kill" method or something similar to addChild removeChild in as3. Any resource would help, since I'm not very familiar with Ruby.
Files involved
POLICY SERVER "server lets Flash files play"
policyserver_little.rb
RUBY TCP/IP SOCKET SERVER "server plays loader, that loads 3-Flash files"
server_little#.rb
FLASH LOADER "client"
loader_little.swf
FLASH MOVIES "numbers_odom.swf, numbers_fruits.swf, $mil.swf"
"msg1, msg2, msg3"
WHAT I SEE
def worker==>end
There's no method to unload.
RUBY SERVER server_little#.rb
require 'socket'
require 'rexml/document'
include Socket::Constants
def create_xml_msg(msg, parent)
el = nil
msg.each do |key, value|
if parent
el = parent.add_element(key)
else
el = REXML::Element.new(key)
end
if value.instance_of?(Hash)
create_xml_msg(value, el)
else
el.text = value.to_s
end
end
return el
end
def worker(client, client_sockaddr, worker_number)
$tid << Thread.new([client, client_sockaddr, worker_number]) do |cl|
Thread.current[:number] = cl[2]
puts("\nThread #{cl[2]} servicing #{Socket.unpack_sockaddr_in(cl[1]).join(':')}")
#2
seq_no = cl[2] * 10000000
loop do
begin
msg1 = {"msg" => {"head" => {"type" => "frctl", "seq_no" => seq_no, "version" => 1.0},
"body" => {"file" => "numbers_odom.swf", "start" => 5,
"end" => 3000, "rate" => 40, "duration" => 60}}}
msg2 = {"msg" => {"head" => {"type" => "frctl", "seq_no" => seq_no, "version" => 1.0},
"body" => {"file" => "numbers_fruits.swf", "start" => 5, "end" => 3000, "rate" => 40, "duration" => 60}}}
msg3 = {"msg" => {"head" => {"type" => "frctl", "seq_no" => seq_no, "version" => 1.0},
"body" => {"file" => "$mil.swf", "start" => 5,
"end" => 3000, "rate" => 40, "duration" => 60}}}
[ msg1, msg2, msg3 ].each do |m|
seq_no += 1
m["msg"]["head"]["seq_no"] = seq_no
xml_msg = create_xml_msg(m, nil)
xml_msg.write(cl[0], 0)
cl[0].putc 0
sleep 10
end
rescue
cl[0].close
puts "\nThread #{Thread.current[:number]} exiting..."
Thread.exit
end
end
end
end
$tid = [] # array of active worker thread ids
$wno = [] # array of active worker numbers
$worker_count = 0
$max_workers = 3
$wlist = Array(1..$max_workers) #array of all possible worker numbers
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 1999, '0.0.0.0' )
begin
socket.bind( sockaddr )
socket.listen( 5 )
rescue
print $!.class, " ", $!
sleep 3
retry
end
loop do
begin
$tid.each do |t|
if (t.status == false || t.status == "aborting" )
t.join
$wno.delete(t[:number])
$tid.delete(t)
$worker_count -= 1
puts("\nWorker count #{$worker_count}")
end
end
client, client_sockaddr = socket.accept_nonblock
if (client)
if ($worker_count >= $max_workers)
puts "\n too many clients...\n"
client.puts("<msg>error: too many clients; closing connection...</msg>")
client.close
else
$worker_count += 1
$wlist.each do |w| #find a hole in worker number list
if (!$wno.include?(w))
$wno << w #add new worker number to the active worker num array
worker(client, client_sockaddr, w)
break
end
end
puts("\nWorker count #{$worker_count}")
end
end
rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EINTR, Errno::EWOULDBLOCK
IO.select([socket], nil, nil, 1)
retry
end
end
REFERENCE
Ruby version 186-25
http://rubylearning.com/satishtalim/ruby_threads.html
A very good resource for socket programming in ruby:
Ruby Sockets: IBM
It was tough for me to get a feel for your code by reading through it, so I can't give you a direct answer. However I can tell you that if you want a good resource for sockets in ruby that pdf is it.
In general i think the only way to eliminate garbage is to fork off a process, let it do the garbage-y stuff then die. If that's your question. NB that jruby, macruby, and rubinius have more advanced GC's.
-r