I wrote some code:
output = File.open(text_file).collect.reverse.join("<BR>")
It seems to work okay on 1.8.7 but throws the error
NoMethodError - undefined method 'reverse' for #<Enumerator: #<File:C:\EduTester\cron\rufus.log>:collect>:
on 1.9.1 (ruby 1.9.3p194 (2012-04-20) [i386-mingw32])
Does somebody know why this happens and how to fix this? (Why is of most interest to me.)
First how to fix it - you should be doing this:
output = File.open(text_file).to_a.reverse.join("<BR>")
This will work on either version of Ruby. Basically you need to turn the file into an array of lines (with .to_a) before reversing them and adding line breaks.
In terms of the why (this gets a little technical): File mixes in the Enumerable module, which gives it methods like collect. Now in Ruby 1.87, if you called Enumberable.collect without a block it would return an Array. But in 1.9, it returns an Enumerator - which doesn't respond to the reverse method.
Here are the 2 versions of the method in question:
http://ruby-doc.org/core-1.8.7/Enumerable.html#method-i-collect
http://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-collect
So basically before 1.9 .collect was a (hacky) equivalent to .to_a. But always use .to_a to turn something into an array.
In Ruby 1.8.7 if a block is given or not given with collect method, it returns an array.
But in 1.9 , it will only return array if block is given with collect method. Otherwise it will return enumerator object.
From documentation -
Collect method-
Returns a new array with the results of running block once for every element in enum.
If no block is given, an enumerator is returned instead.
in 1.8.7, the collect method returns an array when applied on a File object whereas in 1.9.3, it returns an enumerator. reverse method can be applied on an array only.
The reason this works in 1.8.7 is that when you call Enumerable#collect without a block in 1.8.7, it uses a default block which just returns its arg, so file.collect is equivalent to file.collect {|x| x} which returns an array of the lines in the file, on which in you can call Array#reverse`.
In 1.9.x, calling Enumerable#collect without a block returns an Enumerator. Enumerator does not natively support #reverse, nor does its mixin Enumerable. So, you get NoMethodError.
If you want to write this expression in a way compatible with either version, use #to_a instead of #collect.
output = File.open(text_file).to_a.reverse.join("<BR>")
Related
I have this simple code to generate a lazy array:
lazy_arr = Enumerator.new { |y|
i = 1
loop {
y << i
i+=1
}
}
p lazy_arr.take(5)
In official Ruby 1.9.3, the output is [1,2,3,4,5], which is what I want.
But in Rubinius, it gives error and tells me cannot find Enumerator constant.
So I looked it up, and find Enumerator defined in Enumerable module instead of kernel, and when it is generated, it needs a few arguments in the brackets:
http://rubydoc.info/github/evanphx/rubinius/master/Enumerable/Enumerator
I tried to change Enumerator.new to Enumerable::Enumerator.new, or include Enumerable, neither works because it needs more arguments.
How can I do the example above in Rubinius? Is there any way around to make the code work in both official and Rubinius?
You're using Rubinius in 1.8 mode, which doesn't have Enumerator in the global namespace. Please use Rubinius in 1.9 mode and the example works fine then. You can use 1.9 by passing -X19 when starting Rubinius, or setting RBXOPT=-X19 for example.
It's also possible to make 1.9 mode the default with configure during compile time.
Sounds like a bug/missing class in Rubinius. Open up an issue on github and it will get added. Or dig in and send a pull request!
In Ruby 1.8.7 and prior, Enumerable::each_with_index did not accept any arguments. In Ruby 1.9, it will accept an arbitrary number of arguments. Documentation/code shows that it simply passes those arguments along to ::each. With the built in and standard library Enumerables, I believe passing an argument will yield an error, since the Enumerable's ::each method isn't expecting parameters.
So I would guess this is only useful in creating your own Enumerable in which you do create an ::each method that accepts arguments. What is an example where this would be useful?
Are there any other non-obvious consequences of this change?
I went through some gems code and found almost no uses of that feature. One that it does, spreadsheet:
def each skip=dimensions[0], &block
skip.upto(dimensions[1] - 1) do |idx|
block.call row(idx)
end
end
I don't really see that as an important change: #each is the base method for classes that mix-in module Enumerable, and methods added (map, select, ...) do not accept arguments.
I'm wanting to use the &method(:method_name) idiom when there's more than one object required by method_name. Can I do this under Ruby 1.9?
For example, if I've got
def move_file(old_filename, new_filename)
STDERR.puts "Moving #{old_filename.inspect} to #{new_filename.inspect}"
# Implementation for careful moving goes here
end
old_filenames = ["foo.txt", "bar.txt", "hoge.ja.txt"]
new_filenames = ["foo_20110915.txt", "bar_20110915.txt", "hoge_20110915.ja.txt"]
the code
old_filenames.zip(new_filenames).each(&method(:move_file))
works under Ruby 1.8, but not under Ruby 1.9. Under Ruby 1.9, it's trying to do move_file(["foo.txt", "foo_20110915.txt"]) instead of move_file("foo.txt", "foo_20110915.txt").
How do I splattify it so it has the correct arity?
Workarounds I'm aware of:
Replace def move_file(old_filename, new_filename) with def move_file(*arguments)
Replace each(&method(:move_file)) with
each{|old_filename, new_filename| move_file(old_filename, new_filename)}
Instead
each{|old_filename, new_filename| move_file(old_filename, new_filename)}
you should be able to do
each{|pair| move_file(*pair)}
But I don't know how you'd pull off blockless variant (I needed it couple of times as well). I guess &-shorthand was made to make the syntax simpler, and is not meant to be clogged much (whether it will be passed an array as an array, or splatted, for example). :)
How do I splattify it so it has the correct arity?
I don't think there is a way to do this while being compatible to both Ruby versions. What you could do is wrap it into a lambda
move_from_to = Proc.new {|*both| move_files(*both) }
The thing is - block and proc arity is something that got addressed in Ruby 1.9 so there might be a difference in behavior there. Also see prc.lambda? here http://www.ruby-doc.org/core/classes/Proc.html for info on what it does to the arity.
This question is also related to what you want to do (the solution there is to resplat and unsplat manually): Inconsistency of arity between Hash.each and lambdas
I find this code in Ruby to be pretty intriguing
(1..4).inject(&:+)
Ok, I know what inject does, and I know this code is basically equivalent to
(1..4).inject(0) {|a,n| a + n}
but how exactly does it work?
Why &:+ is the same as writing the block {|a,n| a + n}?
Why it doesn't need an initial value? I'm ok with the inicial value being 0, but (1..4).inject(&:*) also works, and there the initial value must be 1...
From Ruby documentation:
If you specify a symbol instead, then each element in the collection will be passed to the named method of memo
So, specifying a symbol is equivalent to passing the following block:
{|memo, a| memo.send(sym, a)}
If you do not explicitly specify an initial value for memo, then uses the first element of collection is used as the initial value of memo.
So, there is no magic, Ruby simply takes the first element as the initial value and starts injecting from the second element. You can check it by writing [].inject(:+): it returns nil as opposed to [].inject(0, :+) which returns 0.
Edit: I didn't notice the ampersand. You don't need it, inject will work with a symbol. But if you do write it, the symbol is converted to block, it can be useful with other methods
I'm just a newbie to ruby. I've seen a string method (String).hash .
For example, in irb, I've tried
>> "mgpyone".hash
returns
=> 144611910
how does this method works ?
The hash method is defined for all objects. See documentation:
Generates a Fixnum hash value for this
object. This function must have the
property that a.eql?(b) implies a.hash == b.hash.
The hash value is used by class Hash. Any hash value that
exceeds the capacity of a Fixnum will
be truncated before being used.
So the String.hash method is defined in C-Code. Basically (over-simplified) it just sums up the characters in that string.
If you need to get a consistent hashing output I would recommend NOT to use 'string.hash but instead consider using Digest::MD5 which will be safe in multi-instance cloud applications for example you can test this as mentioned in comment in previous by #BenCrowell
Run this 2x from your terminal, you will get different output each time:
ruby -e "puts 'a'.hash"
But if you run this the output will be consistent:
ruby -e "require 'digest'; puts Digest::MD5.hexdigest 'a'"