Creating temp file in Ruby? - ruby

I am using Ruby 1.8.7 (2010-12-23 patchlevel 330) [x86_64-linux].
When trying to creating a temp file using the tempfile gem, it's getting stuck, and I can't exit from it:
1.8.7 :003 > require 'rubygems'
1.8.7 :004 > require 'tempfile'
1.8.7 :005 > tmp_file = Tempfile.new("new_file")
After the last line, it's stuck and not responsive.
I think it works with newer Ruby versions, but does anyone know what can cause this issue?

It' not gone be easy to find an issue without some deeper debugging. Try running:
class Tempfile
def initialize(basename, tmpdir=Dir::tmpdir)
if $SAFE > 0 and tmpdir.tainted?
tmpdir = '/tmp'
end
lock = nil
n = failure = 0
begin
puts 'Entering critcal stage'
Thread.critical = true
begin
tmpname = File.join(tmpdir, make_tmpname(basename, n))
lock = tmpname + '.lock'
n += 1
puts n
end while ##cleanlist.include?(tmpname) or
File.exist?(lock) or File.exist?(tmpname)
Dir.mkdir(lock)
#rescue
# failure += 1
# retry if failure < MAX_TRY
# raise "cannot generate tempfile `%s'" % tmpname
ensure
Thread.critical = false
puts 'critical stage left'
end
#data = [tmpname]
#clean_proc = Tempfile.callback(#data)
ObjectSpace.define_finalizer(self, #clean_proc)
#tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600)
#tmpname = tmpname
##cleanlist << #tmpname
#data[1] = #tmpfile
#data[2] = ##cleanlist
super(#tmpfile)
Dir.rmdir(lock)
end
end
This should be identical to your Tempfile initialize method, with some debugging bits. Could you run this, and retry creating a new tempfile. I'll update answer when you comment with the console output.

Related

detect/set immutable on a file in Ruby

I am in need of a ruby way to detect if immutable is set on a file. If it is I need to remove it. Make my change to the file and then return it to immutability. I have looked at File and Fileutils, I have searched but all I can find with immutable and ruby is how to make ruby objects and threads immutable, which is of course a different thing then what I am looking for. I am trying to avoid using the shell, I want to stick to ruby code if at all possible. I am imagining code something like this:
file_name = '/boot/grub2/grub.cfg'
was_immutable = false
if File.immutable?(file_name)
FileUtils.chattr '-i', file_name
was_immutable = true
end
#my changes
if was_immutable
FileUtils.chattr '+i', file_name
end
This might seem like an over-simplification, but you can probably see where it is going:
file_name = '/boot/grub2/grub.cfg'
a = %x[lsattr #{file_name}].chomp
puts a if a[/i/]
Ok, so based on the link in the comments below, here is what I could come up with as a ruby version (if a guru around, happy to see how this should look):
#!/usr/bin/env ruby
require 'fcntl'
FS_IOC_GETFLAGS = 0x80086601
EXT3_IMMUTABLE_FL = 0x00000010
$count = 0
def check(filename)
fd = IO.sysopen(filename, Fcntl::O_RDONLY)
f = IO.new(fd)
a = [0].pack("L_")
f.ioctl(FS_IOC_GETFLAGS, a)
unless a.unpack('L_')[0] & EXT3_IMMUTABLE_FL == 0
puts "#{filename} is immutable :- a[0] = #{a[0].to_s}"
$count += 1
end
f.close
end
ARGV.each do |arg|
Dir[arg + '/*'].each do |item|
check(item) if File.file?(item)
end
end
exit(1) unless $count == 0

Download files asynchronously

I was trying to make a script that downloads all images or videos from a thread in my favourite imageboard: 2ch.hk
I was successful until I wanted to download these files asynchronously (for example, to improve performance)
Here is the code http://ideone.com/k2l4Hm
file = http.get(source).body
require 'net/http'
multithreading = false
Net::HTTP.start("2ch.hk", :use_ssl => true) do |http|
thread = http.get("/b/res/133467978.html").body
sources = []
thread.scan(/<a class="desktop" target="_blank" href=".+">.+<\/a>/).each do |a|
source = "/b#{/<a class="desktop" target="_blank" href="\.\.(.+)">.+<\/a>/.match(a).to_a[1]}"
sources << source
end
i = 0
start = Time.now
if multithreading
threads = []
sources.each do |source|
threads << Thread.new(i) do |j|
file = http.get(source).body #breaks everything
# type = /.+\.(.+)/.match(source)[1]
# open("#{j}.#{type}","wb") { |new_file|
# new_file.write(file)
# }
end
i += 1
end
threads.each do |thr|
thr.join
end
# until downloade=sources.size
#
# end
else
sources.each do |source|
file = http.get(source).body
type = /.+\.(.+)/.match(source)[1]
open("#{i}.#{type}","wb") { |new_file|
new_file.write(file)
}
i += 1
print "#{(((i).to_f / sources.size) * 100).round(2)}% "
end
puts
end
puts "Done. #{i} files were downloaded. It took #{Time.now - start} seconds"
end
I suppose that this line crashes everything.
file = http.get(source).body
Or maybe that's the problem.
threads.each do |thr|
thr.join
end
Error messages are always different, from Bad File Descriptor and IO errors to "You may have encountered a bug in the Ruby interpreter or extension libraries."
If you want to try and run my code, please substitute a link to thread in 4th line with a new thread (from 2ch.hk/b), because the one in my code may be deleted by the time you run my code
Version of ruby: 2.3.1, OS Xubuntu 16.10
You'll probably have much better performance using a ruby http lib that supports parallel requests:
https://github.com/typhoeus/typhoeus
e.g.
hydra = Typhoeus::Hydra.new
10.times.map{ hydra.queue(Typhoeus::Request.new("www.example.com", followlocation: true)) }
hydra.run
The problem with my code is that I can't make multiple requests on a Net::HTTP instance at the same time.
The solution is to open an HTTP connection for each thread.

Ruby: Undefined Method `<'

As a way to learn the ins and outs of ruby, I decided to make a (relatively simple) text-based RPG. Everything so far has gone well, except recently I've hit a roadblock that I haven't seen before.
My goal is: if any stat (str, def, agi, man) is < 0, I want to make it = 0. For some reason though, ruby doesn't seem to like the `<'.
Here's the code ruby hangs up on:
def self.compile
#str = ProfileData.load['g_str']
#def = ProfileData.load['g_def']
#agi = ProfileData.load['g_agi']
#man = ProfileData.load['g_man']
#smin = 1
#dmin = 1
#amin = 1
#mmin = 1
if #str < #smin
#str = 0
end
if #def < #dmin
#def = 0
end
if #agi < #amin
#agi = 0
end
if #man < #mmin
#man = 0
end
#str.round!
#def.round!
#agi.round!
#man.round!
d = YAML::load_file('./profile')
d['mstr'] = #str
File.open('./profile', 'w') {|f| f.write d.to_yaml}
d = YAML::load_file('./profile')
d['mdef'] = #def
File.open('./profile', 'w') {|f| f.write d.to_yaml}
d = YAML::load_file('./profile')
d['magi'] = #agi
File.open('./profile', 'w') {|f| f.write d.to_yaml}
d = YAML::load_file('./profile')
d['mman'] = #man
File.open('./profile', 'w') {|f| f.write d.to_yaml}
end
Now when I run through my program, I get this error code when it finally runs "compile":
start.rb:734:in `compile': undefined method `<' for []:Array (NoMethodError)
And that's it. Have any clue what's happened or how I can fix it? Any help is very much appreciated!
It means your variables (at leas one per compared pair) is of Array type.
Check what each of these
#str = ProfileData.load['g_str']
#ddef = ProfileData.load['g_def']
#agi = ProfileData.load['g_agi']
#man = ProfileData.load['g_man']
returns and make sure it is Comparable (integers, for example).
The load method seems to assume that would be a collection of variables, even you having just one.
If you grant to be always one, select just the first and as it may be a string you should make it a int.
#str = ProfileData.load['g_str'].first.to_i

Ruby Threads: progress bar during copy

I'm pretty new to Ruby Threads, so could someone let me know what I'm doing wrong here?
require 'fileutils'
require 'zip'
require 'rubygems'
require 'progressbar'
oraclePath = "\\\\server\\Oracle Client\\Oracle_11gR2\\win64_11gR2_client.zip"
begin
tmpDir = Dir.mktmpdir("ora-")
progress = Thread.new(){
Thread.current[:name] = "FileProgress"
sourceFileSize = File.size("#{oraclePath}")
batch_bytes = ( in_size / 100 ).ceil
total = 0
p_bar = ProgressBar.new('Copying', 100)
buffer = "#{oraclePath}".sysread(batch_bytes)
while total < sourceFileSize do
"#{tmpDir}".syswrite(buffer)
p_bar.inc
total += batch_bytes
if (sourceFileSize - total) < batch_bytes
batch_bytes = (sourceFileSize - total)
end
buffer = "#{oraclePath}".sysread(batch_bytes)
end
p_bar.finish
}
progress.run
puts "#{tmpDir}"
FileUtils.cp_r("#{oraclePath}","#{tmpDir}")
Zip::File.open("#{tmpDir}/win64_11gR2_client.zip") do |zipfile|
`unzip -j #{zipfile} -d #{dir}`
#zipfile.each do |file|
#zipfile.extract(file, "#{tmpDir}")
#end
end
ensure
# remove the temp directories
FileUtils.remove_entry_secure tmpDir
end
The copying works, but the thread doesn't - I can't even step into it; it just skips it entirely.
A Ruby Thread will start running the moment it's instantiated with Thread.new, so in your example the copy begins immediately and the line progress.run isn't necessary.
You would only need to call run if the thread itself had stopped (i.e. called stop on itself while waiting for further instructions).
As reference, you can find more information here: http://ruby-doc.org/core-2.0.0/Thread.html

ruby cgi wont return method calls, but will return parameters

my environment: ruby 1.9.3p392 (2013-02-22 revision 39386) [x86_64-linux]
The thing is, I can make ruby return the parameters sent over GET. but when i'm trying to use them as arguements to my methods in if/else, ruby wont return anything and I end up with a blank page.
ph and pm return correctly:
http://127.0.0.1/cgi-bin/test.rb?hostname=node00.abit.dk&macadd=23:14:41:51:63
returns:
node00.abit.dk 23:14:41:51:63
Connection to the database (MySQL) works fine
When I test the method newHostName it outputs correctly:
puts newHostName
returns (which is correct)
node25.abit.dk
the code:
#!/usr/bin/ruby
require 'cgi'
require 'sequel'
require 'socket'
require 'timeout'
DB = Sequel.connect(:adapter=>'mysql', :host=>'localhost', :database=>'nodes', :user=>'nodeuser', :password=>'...')
#cgi-part to work
#takes 2 parameters:
#hostname & macadd
cgi = CGI.new
puts cgi.header
p = cgi.params
ph = p['hostname']
pm = p['macadd']
def nodeLookup(hostnameargv)
hostname = DB[:basenode]
h = hostname[:hostname => hostnameargv]
h1 = h[:hostname]
h2 = h[:macadd]
ary = [h1, h2]
return ary
end
def lastHostName()
#TODO: replace with correct sequel-code and NOT raw SQL
DB.fetch("SELECT hostname FROM basenode ORDER BY id DESC LIMIT 1") do |row|
return row[:hostname]
end
end
def newHostName()
org = lastHostName
#Need this 'hack' to make ruby grep for the number
#nodename e.g 'node01.abit.dk'
var1 = org[4]
var2 = org[5]
var3 = var1 + var2
sum = var3.to_i + 1
#puts sum
sum = "node" + sum.to_s + ".abit.dk"
return sum
end
def insertNewNode(newhost, newmac)
newnode = DB[:basenode]
newnode.insert(:hostname => newhost, :macadd => newmac)
return "#{newnode.count}"
end
#puts ph
#puts pm
#puts newHostName
cgi.out() do
cgi.html do
begin
if ph == "node00.abit.dk"
puts newHostName
else
puts nodeLookup(ph)
end
end
end
end
I feel like im missing something here. Any help is very much appreciated!
//M00kaw
What about modify last lines of your code as followed? CGI HTML generation methods take a block and yield the return value of the block as their content. So you should make newHostName or nodeLookup(ph) as the return value of the block passed to cgi.html(), rather than puts sth, which prints the content to your terminal and return nil. That's why cgi.html() got an empty string (nil.to_s).
#puts newHostName
cgi.out() do
cgi.html do
if ph == "node00.abit.dk"
newHostName
else
nodeLookup(ph)
end
end
end
p.s. It's conventional to indent your ruby code with 2 spaces :-)

Resources