Undefined method "each" in Ruby 2.0 - ruby

The new Mac OS update moved the system Ruby up to 2.0, which is great, but now I'm seeing errors in a lot of my scripts that I don't know how to fix. Specifically, I had code that called for files using mdfind and then read them, like this:
files = %x{mdfind -onlyin /Users/Username/Dropbox/Tasks 'kMDItemContentModificationDate >= "$time.today(-1)"'}
files.each do |file|
Now I'm getting an error that says
undefined method `each' for #<String:0x007f83521865c8> (NoMethodError)"
It seems as if each now needs a qualifier. I tried each_line but that yielded additional errors down the line. Is there a simple replacement for this that I'm overlooking?

Ruby 1.8 used to have String#each which was doing implicit splitting.
each(separator=$/) {|substr| block } => str
Splits str using the supplied parameter as the record separator ($/ by default), passing each substring in turn to the supplied block. If a zero-length record separator is supplied, the string is split into paragraphs delimited by multiple successive newlines.
Explicit splitting should work in modern rubies, I believe.
files.split($/).each do |file|
Where $/ is newline char. You can use explicit char, since your script is not portable anyway.
files.split("\n").each do |file|
Update
or you can just use an alias of now-extinct each
files.each_line do |file|

Related

Is special variable $. gone from Ruby?

When processing a file, I used to use the special variable $. to get the last line number being read. For instance, the following program
require 'csv'
IFS=';'
CSV_OPTIONS = { col_sep: IFS, external_encoding: Encoding::ISO_8859_1, internal_encoding: Encoding::UTF_8 }
CSV.new($stdin, CSV_OPTIONS).each do |row|
puts "::::line #{$.} row=#{row}"
end
is supposed to dump a CSV file (where the fields are delimited by semicolon instead of comma, as is the case in our project) and prepend each output line by the line number.
After updating Ruby to
_ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-cygwin]_
the lines are still dumped, but the line number is always displayed as zero.
What strikes me, is that this Ruby Wiki on special Ruby variables, while still having $. in its list, doesn't have a description for this variable anymore. So I wonder: Is this variable gone, or was it never supposed to work with the csv class and just worked for me by accident in the earlier versions?
I'm not sure why $. isn't working for you, but it's also not the best solution here. When it works, $. gives you the number of lines read from input, but since quoted fields in a CSV file can span multiple lines the number you get from $. won't always be the number of rows that have been read.
As mentioned above, each_with_index is a good alternative:
CSV.new($stdin, CSV_OPTIONS).each_with_index do |row, i|
puts "::::row #{i} row=#{row}"
end
Another alternative is CSV#lineno:
lineno()
The line number of the last row read from this file. Fields with nested line-end characters will not affect this count.
You would use it like this:
csv = CSV.new($stdin, CSV_OPTIONS)
csv.each do |row|
puts "::::row #{csv.lineno} row=#{row}"
end
Note that each_with_index will start counting at 0, whereas lineno starts at 1.
You can see both approaches in action on repl.it: https://repl.it/#jrunning/LoudBlushingCharactercode

Ruby: Why do I get warning "regex literal in condition" here?

A simple Ruby program, which works well (using Ruby 2.0.0):
#!/usr/bin/ruby
while gets
print if /foo/../bar/
end
However, Ruby also outputs the warning warning: regex literal in condition. It seems that Ruby considers my flip-flop-expression /foo/../bar/ as dangerous.
My question: Where lies the danger in this program? And: Can I turn off this warning (ideally only for this statement, keeping other warnings active)?
BTW, I found on the net several discussions of this kind of code, also mentioning the warning, but never found a good explanation why we get warned.
You can avoid the warning by using an explicit match:
while gets
print if ~/foo/..~/bar/
end
Regexp#~ matches against $_.
I don't know why the warning is shown (to be honest, I wasn't even aware that Ruby matches regexp literals against $_ implicitly), but according to the parser's source code, it is shown unless you provide the -e option when invoking Ruby, i.e. passing the script as an argument:
$ ruby -e "while gets; print if /foo/../bar/ end"
I would avoid using $_ as an implicit parameter and instead use something like:
while line = gets
print line if line=~/foo/..line=~/bar/
end
I think Neil Slater is right: It looks like a bug in a parser. If I change the code to
#!/usr/bin/ruby
while gets
print if $_=~/foo/..$_=~/bar/
end
the warning disappears.
I'll file a bug report.

Reading contents from UTF-16 encoded file in Ruby

I want to read the contents of a file and save it into a variable. Normally I would do something like:
text = File.read(filepath)
Unfortunately there's a file I'm working with that is encoded with UTF-16LE. I've been doing some research and it looks like I need to use File.Open instead and define the encoding. I read a suggestion somewhere that said to open the file and read in the data line by line:
text = File.open(filepath,"rb:UTF-16LE") { |file| file.lines }
However if I run:
puts text
I get:
#<Enumerator:0x23f76a8>
How can I read in the content of the UTF-16LE file into a variable?
Note: I am using Ruby 1.9.3 and a Windows OS
The lines method is deprecated. If you expect text to be an array with lines, then use readlines.
text = File.open(filepath,"rb:UTF-16LE"){ |file| file.readlines }
As the Tin Man says, it's better practise to process each line seperately, if possible:
File.open("test.csv", "rb:UTF-16LE") do |file|
file.each do |line|
p line
end
end
First, don't make it a practice to read a file directly into a variable unless you absolutely have to. That's called "slurping", and is not scalable. Instead, read it line by line.
Ruby's IO class, which File inherits from, supports a parameter they call open_args, which is a hash, on the majority of "read" type calls. For example, here are some method signatures:
read(name, [length [, offset]], open_args)
readlines(name, sep=$/ [, open_args])
The documentation says this about open_args:
If the last argument is a hash, it specifies option for internal open(). The
key would be the following. open_args: is exclusive to others.
encoding:
string or encoding
specifies encoding of the read string. encoding will be ignored if length
is specified.
mode:
string
specifies mode argument for open(). It should start with "r" otherwise it
will cause an error.
open_args:
array of strings
specifies arguments for open() as an array.

Illegal Quoting error with Ruby CSV parsing

I know there's lots of similar questions but I haven't found a solution yet. I'm trying to use the CSV parsing library with Ruby 1.9.1 but I keep getting:
/usr/lib/ruby/1.9.1/csv.rb:1925:in `block (2 levels) in shift': Illegal quoting in line 1. (CSV::MalformedCSVError)
My CSV files were created in Windows 7 but it's Ubuntu 12.04 that I'm using to run the Ruby script, which looks like this:
require 'csv'
CSV.foreach('out.csv', :col_sep => ';') do |row|
puts row
end
Nothing complicated, just a test, so I assumed it must be the Windows control characters causing problems. Vim shows up this:
"Part 1";;;;^M
;;;;^M
;;;;^M
Failure to Lodge Income Tax Return(s);;;;^M
NAME;ADDRESS;OCCUPATION;"NO OF CHARGES";"FINE/PENALTY £"^M
some name;"some,address";Bookkeeper;3;1,250.00^M
some name;"some,address";Haulier;1;600.00^M
some name;"some,address";Scaffolding Hire;1;250.00^M
some name;"some,address";Farmer;2;500.00^M
some name;"some,address";Builder;2;3000.00
I've tried removing those control characters for carraige returns that Windows added (^M), but %s/^V^M//g and %s/^M//g result in no pattern found. If I run %s/\r//g then the ^M characters are removed, but the same error still persists when I run the Ruby script. I've also tried running set ffs=unix,dos but it has no effect. Thanks.
Update:
If I remove the double quotes around the Part 1 on the first line, then the script prints out what it should and then throws a new error: Unquoted fields do not allow \r or \n (line 10). If I then remove the \r characters, the script runs fine.
I understand that I would have to remove the \r characters, but why will it only work if I unquote the first value?
The problem causing the Illegal quoting error was due to a Byte-Order-Mark (BOM) at the very beginning of the file. It didn't show up in editors, but the Ruby CSV lib was choking on it unless :encoding => 'bom|utf-8' was set.
Once that was fixed, I still needed to remove all the '^M' characters by running %s/\r//g in vim. And everything was working fine after that.

Shellwords.shellescape implementation for Ruby 1.8

While the build of 1.8.7 I have seems to have a backported version of Shellwords::shellescape, I know that method is a 1.9 feature and definitely isn't supported in earlier versions of 1.8. Does anyone know where I can find, either in Gem form or just as a snippet, a robust standalone implementation of Bourne-shell command escaping for Ruby?
You might as well just copy what you want from shellwords.rb in the trunk of Ruby's subversion repository (which is GPLv2'd):
def shellescape(str)
# An empty argument will be skipped, so return empty quotes.
return "''" if str.empty?
str = str.dup
# Process as a single byte sequence because not all shell
# implementations are multibyte aware.
str.gsub!(/([^A-Za-z0-9_\-.,:\/#\n])/n, "\\\\\\1")
# A LF cannot be escaped with a backslash because a backslash + LF
# combo is regarded as line continuation and simply ignored.
str.gsub!(/\n/, "'\n'")
return str
end
I wound up going with the Escape gem, which has the additional feature of using quotes by default, and only backslash-escaping when necessary.

Resources