Extending Array hides initializer? - ruby

I'm using ruby 1.8.7
my_array = Array.new(5, "A")
works fine. However if I extend the Array class like this:
class Array
def my_function
self
end
end
then the initializer no longer works and returns
wrong number of arguments (2 for 0)
Why is this and how do I fix it?

Are you doing this inside a Module? If so, you are defining a separate Array class, rather than extending Array from the standard library.
Make sure your extension is at the top level, or else write:
class ::Array
(By the way, is there a good reason why you want to use Ruby 1.8.7? If not, I would recommend using an up-to-date version -- Ruby has improved over the years.)

Related

Calculating a map of instance variables

My module method should return a Hash, where the keys are symbols denoting the instance variables of self and the Hash values are their respective values. I came up with the following solution:
module M
def self.set
#foo = 5
end
def self.ivar_map
Hash[instance_variables.map{|iv| [iv, instance_variable_get(iv)]}]
end
end
M.set
M.ivar_map # => {:#foo=>5}
This works of course, but I wonder whether there isn't a simpler way to achieve the same result. After all, Ruby somehow has to maintain such a symbol table already, mapping the variables to their content.
This works of course, but I wonder whether there isn't a simpler way to achieve the same result.
There are some slight simplifications that you could do:
Use Array#to_h instead of Hash::[].
Use Array#zip.
Use point-free style using Object#method or something like the method reference operator if that is ever added to Ruby.
module M
def self.ivar_map
instance_variables.zip(instance_variables.map(&method(:instance_variable_get))).to_h
end
end
or with the experimental (and removed again in 2.7.0-preview3) method reference operator:
module M
def self.ivar_map
instance_variables.zip(instance_variables.map(&self.:instance_variable_get)).to_h
end
end
After all, Ruby somehow has to maintain such a symbol table already, mapping the variables to their content.
Actually, that is not true. Several Ruby Implementations (including YARV, the most widely-used one) optimize instance variables to some extent. E.g. I believe YARV stores up to three instance variables in the object header directly, without using an instance variable table.
And YARV is on the more simple side when it comes to optimizations. Other implementations like Rubinius, JRuby, TruffleRuby, and RubyOMR perform much more sophisticated optimizations.

How to override Ruby + method?

Just for learning purpose I am trying to override the Ruby + method, but I am not getting the desired output.
class Integer
def +(my_num)
"Plus method overridden"
end
end
puts 5.+(9)
Please let me know what I am doing wrong here.
It seems you use ruby < 2.4. If so you want to patch Fixnum and not Integer. Be careful as the system itself uses numbers as well.
class Fixnum
alias_method :add, :+
def +(other)
puts 'plus method overridden'
add(other)
end
end
puts 5 + 9
The Ruby Language Specification allows Integer to have implementation-specific subclasses. See Section 15.2.8.1, lines 27-33.
It looks like your implementation does have such subclasses. In that case, the + method may be overridden in a subclass.
My best guess is that you have an implementation which distinguishes between Fixnums and Bignums, and that our Integer#+ gets overridden by Fixnum#+.
By the way, even if what you were trying to do were working, it wouldn't be overriding, it would be overwriting.
Also note that if what you were trying to do were working, you would have most likely broken your Ruby process, since Integers are fundamental and widely-used all over the place in Ruby.

How do I have Ruby YAML dump a Hash subclass as a simple Hash?

I have a class Foo that is a subclass of Hash.
class Foo < Hash
# whatever Foo-specific methods/etc
end
When I dump it to a file with YAML, it is written with a tag indicating the class.
> f = Foo.new
> f[:bar] = "baz"
> puts YAML.dump(f)
--- !ruby/hash:Foo
:bar: baz
I would like it to just be written as a plain old hash (not !ruby/hash:Foo)
> puts YAML.dump({bar:"baz"})
---
:bar: baz
...so that consumers of my data don't need to know about Foo. Is there a magic method to add to my class to convert itself for serialization, or a magic option to pass to YAML.dump?
Of course it is easy to convert one Foo object to a hash, but they may show up nested at any level inside the actual hash I'd like to dump, and I'd rather not have to do a search and replace.
You can achieve this with the (poorly documented) encode_with and represent_map methods. To customise the YAML serialisation of an object you provide it with an encode_with method which accepts a coder object, one of the methods on which is represent_map.
class Foo < Hash
# other methods ...
def encode_with coder
coder.represent_map nil, self
end
end
Now YAML.dump will just output your object as a normal hash.
However
There is a bit of a problem because there is a bug that will cause this to fail and is only fixed in the latest Gem version of Psych. It is not fixed in the current latest Ruby version (ruby 2.0.0p247). It is fixed in Ruby trunk so later patch releases should be okay.
In order to use this you will have to make sure you are using the latest Psych Gem, rather than the version bundled with Ruby. This should be as easy as
gem 'psych', '2.0.0'
before you require Yaml, but it seems that in Ruby 2.0 this doesn’t work for some reason that I can’t figure out. Using Bundler to specify the Gem versions does work though, so you might need to create a Gemfile and specify Psych in there if you’re not already using it.
Search and replace was actually not too bad:
# Convert Hash/Array subclasses into plain hashes/arrays for YAML dump.
# Assumptions:
# Hash keys will be simple objects - don't need to clear them
# No custom objects containing Hash/Array subclass instances
def deep_clear_subclasses(obj, dedup = {})
case obj
when Hash
return dedup[obj] if dedup.has_key? obj
dedup[obj] = copy = {}
obj.each {|k,v| copy[k] = deep_clear_subclasses(v, dedup)}
copy
when Array
return dedup[obj] if dedup.has_key? obj
obj.inject(dedup[obj] = []) {|a,v| a << deep_clear_subclasses(v,dedup)}
else
obj # not going to operate on other kinds of objects
end
end

Ruby's 'is_a?' is returning false for built-in types, what's happening?

I've only been learning the deep parts of Ruby for a few months so apologies if this is a bit of a dumb question. I'm attempting to recursively iterate through an Array that may contain nested Arrays and as such I need to check the type of the current element. I have the following code as a small test:
arr = [ 1..2, [3..4, [5..6]], [7..8, [9..10]] ]
arr.each do |node|
p node.class
p node.instance_of? Array
end
When I run it, I get the following output:
Range
false
Array
false
Array
false
I expected the last two to return True, given I have an Array containing a Range and two nested Arrays.
What's even weirder, is if I write the following:
node.class.name == "Array"
It returns True, as it should.
What's happening here?
Ruby Version: MRI 1.9.3-p194
Note: I eventually realised that this was occurring due to the way I namespace my code using modules to avoid code-collision, like so, but also verify object identity in a naughty way:
module MyProg
class MyClass
attr_reader :my_array
def initialize(size)
#my_array = Array.new(size)
end
end
end
MyProg::MyClass.new
Doing this isolates your code but has the downfall of causing all class lookups to be resolved starting from under your namespace. This means that in the above example, my_array.class would actually resolve to MyProg::Array instead of the global Array class.
If you namespace like this and you still want to use this method, you can remedy it by using the double-colon global identifier before the class to force Ruby to begin lookup from the global namespace:
arr.is_a? ::Array
arr.is_a? ::String
Given Ruby's Duck Typing abilities however (and for better code maintenance later on), you should really be testing the behaviour of the object as-per Peter's suggestion below. As such I'm marking his answer as correct for some excellent help given to a learner!
I wrote another answer, but one major question is - why are you doing this? Why not, instead, just call flatten on the array so you just get the entries? Or, check for the behavior of the objects instead. You might need to give more detail about what you require.
You really mean is_a?, which is a more general test to see if the node is of type Array, rather than a specific instance of the specific Array class that you mention. See here for more details. But if you just use is_a? everything will make sense.
I ran your code and got these results.
Range
false
Array
true
Array
true
I'm running ruby 1.9.3p125

How can I splattify an anonymous object so I can use &method on it?

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

Resources