Source code problem of ZenTest - ruby

Here is one method that monkey patched the Dir[] method from autotest
class Dir
class << self
alias :old_index :[]
def [](*args)
$-w, old_warn = false, $-w
old_index(*args)
ensure
$-w = old_warn
end
end
end
Could you please help by explain this line $-w, old_warn = false, $-w ?
Thanks in advance.

You can assign multiple variables to multiple values on one line in Ruby.
That line is equivalent to the following:
old_warn = $-w
$-w = false
If you were asking what the purpose was; $-w is a global variable in Ruby that points to a boolean that indicates whether or not the user passed the -w flag to the ruby executable when they ran the script. In other words, the variable indicates whether or not the script/program is currently supposed to be printing "warnings".
Essentially, the purpose of that entire block of code is to ensure that warnings are turned off before executing it's core. The old value of the warn flag is saved into a new variable; the warn flag is turned off; and then when the execution is done, the warn flag is re-set back to whatever it used to be.

Related

How to get file mode in ruby?

I'm new to ruby file IO. I have a function that takes a File parameter, and I need to make sure that the file is in read-only mode.
def myfunction(file)
raise ArgumentError.new() unless file.kind_of?(File)
#Assert that file is in read-only mode
end
Any help would be appreciated!
If you don't need to raise an error, you can use reopen, I think something like:
file = file.reopen(file.path, "r")
I can't find a way to otherwise verify that there isn't a write stream, but here's a bit of a hack that will work. Although I don't like exception throwing being used in the expected path, you could use close_write:
begin
file.close_write
# you could actually raise an exception here if you want
# since getting here means the file was originally opened for writing
rescue IOError
# This error will be raised if the file was not opened for
# writing, so this is actually the path we want
end
So all you need is 'make sure make sure that the file is in read-only mode', why not just set it as readonly with FileUtils.chmod.
Or if actually you just want to test if it is readonly, use File.writeable?
You can use file.readable?, which returns true or false.
Please check this link.

How do I share object from main file to supporting file in ruby?

I have something similar.
# MAIN.RB
require 'sockets'
require_relative 'replies.rb'
hostname = 'localhost'
port = 6500
s = TCPSocket.open(hostname, port)
$connected = 0
while line = s.gets # Read lines from the socket
#DO A BUNCH OF STUFF
if line == "Hi"
reply line
end
end
s.close
Then I have the reply function in a secondary file.
# REPLIES.RB
def reply(input)
if input == "Hi"
s.write("Hello my friend.\n"
end
end
However calling on the object s from the second file does not seem to work. How would I go about making this work. I'm new to Ruby. I've searched google for the answer, but the only results I have found is with sharing variables across files. I could always do a return "Hello my friend.\n", but I rather be able to write to the socket object directly from the function in REPLIES.rb
Remember that variables are strictly local unless you expressly pass them in. This means s only exists in the main context. You can fix this by passing it in:
reply(s, line)
And on the receiving side:
def reply(s, input)
# ...
end
I'd strongly encourage you to try and indent things consistently here, this code is really out of sorts, and avoid using global variables like $connected. Using a simple self-contained class you could clean up this code considerably.
Also, don't add .rb extensions when calling require. It's implied.

Chef - get variable from ruby block

I try to get a variable from my ruby block, but Chef doesn't recognise my variable outside of this block. How can I retrieve any variable out of ruby block? Thanks in advance.
ruby_block 'fetch_path' do
block do
current_path = `sudo cat /var/chef/cache/revision`
new_path = current_path.to_s.split(',').last.split('"').drop(1).first
Chef::Log.info("### Your Current Directory: '#{new_path}' ###")
end
end
Chef::Log.info("### Your Current Directory: '#{new_path}' ###")
Within the block I can get a value, but, out of block - no.
There's two thing here.
First, your second Chef::Log.info will be run at compilation phase, at this time your ruby_block has not been converged. See here about it. You can prefix your logs with 1) and 2) to witch one runs first.
Second, there's a scoping problem, when you define a variable in a block, it is available only within this block.
In chef you can use node.run_state['variable'] as a global variable usable in all recipes, without an use case it's hard to showcase this.
Side note: you should not use the backticks `` construction to execute commands and prefer using shell_out from the recipe DSL.

Ruby gets method throws an exception when arguments are passed from the console

I have experienced some ODD behavior from the code below:
require 'CSV'
$DEBUG = ARGV.empty? ? false : ARGV[0] #Global debug flag.
class PhoneBook
#class code here etc etc
end
PhoneBook.start_dir = "file-io-samples/phonebooks/"
puts "Enter a phonebook!"
name = gets #This is the problem.
puts "Using #{name}.."
When I pass true to have $DEBUG set to true on execution I get an error from name = gets and I have no idea why. If I don't pass parameters via the command line everything works fine.
This is the error output:
C:\Pickaxe>ruby PhoneBook.rb
Enter a phonebook!
Hurrah! Works
Using Hurrah! Works
..
C:\Pickaxe>ruby PhoneBook.rb true
Enter a phonebook!
Exception `Errno::ENOENT' at PhoneBook.rb:62 - No such file or directory - true
PhoneBook.rb:62:in `gets': No such file or directory - true (Errno::ENOENT)
from PhoneBook.rb:62:in `gets'
from PhoneBook.rb:62:in `<main>'
C:\Pickaxe>
If I need to I can post the class definition, but I don't think it's part of the problem.
gets reads from stdin if no arguments are passed, and from the file that was passed as an argument otherwise. You are passing an argument true, ergo gets tries to read from a file named true, which apparently doesn't exist.
This is the very first sentence of the documentation of gets:
Returns (and assigns to $_) the next line from the list of files in ARGV (or $*)
This wouldn't cause a problem on *nix, but I expect Windows, or Ruby on Windows, isn't handling the additional command-line parameter the same way. On *nix, we can use -- between the script name and the parameter to tell the OS not to pass the parameter as a flag. In other words, Ruby wouldn't see true, your script would.
ruby some_script.rb -- options
But, in general, I think you're doing it wrong and recommend handling your command-line options in a standard way by using the OptionParser class:
require 'optparse'
OptionParser.new do |opt|
opt.on('-d', '--[no-]debug') { |o| $DEBUG = o }
end.parse!
puts $DEBUG
Running that several times on my Mac OS system, with different parameters, gives me:
$ ruby test.rb
false
$ ruby test.rb --no-debug
false
$ ruby test.rb -d
true
$ ruby test.rb --debug
true
You might still have to use -- to tell the OS and called app which parameters belong to what.

Can I disable the log header for ruby logger?

I'm currently running into kind of a problem.
As you might know, the ruby logger adds a logging header at the top of every newly created logfile.
"# Logfile created on %s by %s\n" % [Time.now.to_s, Logger::ProgName]
I am logging CSV files to import them in a warehouse later, usually I just skip the first line with the header. It's seems like there is a bug in the logger, because sometimes the logging header appears more than once, right in the middle of a log file.
So I decided to simply leave that header out. To my surprise I didn't find any argument one could pass at the creation of a logger. I thought of something like this:
Logger.new "info.log", :skip_header => true
But it's just not there. I searched in the ruby core sources and surprisingly there really is nothing that could prevent the logger from adding the log header:
def create_logfile(filename)
logdev = open(filename, (File::WRONLY | File::APPEND | File::CREAT))
logdev.sync = true
add_log_header(logdev)
logdev
end
def add_log_header(file)
file.write(
"# Logfile created on %s by %s\n" % [Time.now.to_s, Logger::ProgName]
)
end
Does anyone have an idea what I could do, to prevent the log header? I'm using Ruby 1.8.7 302 with Rails 2.3.5 here. Simply ignoring the comments on the warehouse side is not possible because I have no control over the code there, and it seems to be to risky to simply ignore it, if something goes wrong with a a logging line.
Does someone know a logger that allows this? Do you think it would be a good idea to use and write plain to a file?
Thanks in advance,
Tommy
Ideally the method add_log_header on the Logger instance should be overwritten, but since add_log_header is called on initialize, you're too late by the time you get your hands on it.
Well, you could just overwrite the add_log_header method on the Class.
class Logger::LogDevice
def add_log_header(file)
end
end
log1 = Logger.new('info1.log')
But if your app needs more instances of Logger after this, they will behave the same: no header. To prevent this:
# dismantle the header and save it under another name
class Logger::LogDevice
alias orig_add_log_header add_log_header
def add_log_header(file)
end
end
# Quick,create an instance
log1 = Logger.new('test_log1file.log')
# restore the old method:
class Logger::LogDevice
alias add_log_header orig_add_log_header
end
Here's a solution that involves subclassing Logger. We have to be sneaky with initialize and super to keep it from creating a standard Logger::LogDevice too early.
class HeadlessLogger < Logger
def initialize(logdev, shift_age = 0, shift_size = 1048576)
super(nil) # this prevents it from initializing a LogDevice
if logdev
#logdev = HeadlessLogger::LogDevice.new(logdev, shift_age: shift_age, shift_size: shift_size)
end
end
class LogDevice < ::Logger::LogDevice
def add_log_header(file) ; end
end
end
As an alternative to patching the logger class, simply do not let it create the log file by touching it upfront:
FileUtils.touch logfile_path
Logger.new logfile_path
In plain Ruby you will need to require 'fileutils' from stdlib obviously.
Edit: This will not work if you use the built-in logrotation though, or the file is deleted, as there is no on-rotate hook and it will then write the header yet again.

Resources