I've created a class called SpecialArray and I'd like to customize what sort of output irb shows. Currently, when I create a new instance of the class, irb returns the entire object. This is what I currently see:
1.9.3p194 :022 > SpecialArray.new([1,2,0,6,2,11])
=> #<UniqueArray:0x007ff05b026ec8 #input=[1, 2, 0, 6, 2, 11], #output=[1, 2, 0, 6, 11]>
But I'd like to only show what I've defined as output. In other words, I'd like to see this.
1.9.3p194 :022 > SpecialArray.new([1,2,0,6,2,11])
=> [1, 2, 0, 6, 11]
What do I need to do specify that irb should only display the output?
SOLUTION:
This is the method that I ended up creating.
def inspect
output.inspect
end
IRB calls Object#inspect method to get string representation of your object. All you need is to override this method like that:
class Foo
def inspect
"foo:#{object_id}"
end
end
Then in the IRB you'll get:
>> Foo.new
=> foo:70250368430260
In your particular case just make SpecialArray#inspect return string representation of the underlying array, e.g.:
SpecialArray
def inspect
#output.inspect
end
end
Related
In Ruby I'm trying to understand between the to_enum and enum_for methods. Before I my question, I've provided some sample code and two examples to help w/ context.
Sample code:
# replicates group_by method on Array class
class Array
def group_by2(&input_block)
return self.enum_for(:group_by2) unless block_given?
hash = Hash.new {|h, k| h[k] = [] }
self.each { |e| hash[ input_block.call(e) ] << e }
hash
end
end
Example # 1:
irb (main)> puts [1,2,3].group_by2.inspect
=> #<Enumerator: [1, 2, 3]:group_by2>
In example #1: Calling group_by on the array [1,2,3], without passing in a block, returns an enumerator generated with the command self.enum_for(:group_by_2).
Example #2
irb (main)> puts [1,2,3].to_enum.inspect
=> #<Enumerator: [1, 2, 3]:each>
In example #2, the enumerator is generated by calling the to_enum method on the array [1,2,3]
Question:
Do the enumerators generates in examples 1 and 2, behave differently in any way? I can see from the inspected outputs that they show slightly different labels, but I can find any difference in the enumerators' behavior.
# Output for example #1
#<Enumerator: [1, 2, 3]:each> # label reads ":each"
# Output for example #2
#<Enumerator: [1, 2, 3]:group_by2> # label reads ":group_by2"
p [1, 2, 3].to_enum
p [1, 2, 3].enum_for
--output:--
#<Enumerator: [1, 2, 3]:each>
#<Enumerator: [1, 2, 3]:each>
From the docs:
to_enum
Creates a new Enumerator which will enumerate by calling method on
obj, passing args if any.
...
enum_for
Creates a new Enumerator which will enumerate by calling method on
obj, passing args if any.
ruby is a language that often has method names that are synonyms.
Followup question:
Does the symbol in the command [1,2,3].to_enum(:foo) serve a purpose,
other than replacing :each with :foo in the output?
Yes. By default, ruby hooks up the enumerator to the receiver's each() method. Some classes do not have an each() method, for instance String:
str = "hello\world"
e = str.to_enum
puts e.next
--output:--
1.rb:3:in `next': undefined method `each' for "helloworld":String (NoMethodError)
from 1.rb:3:in `<main>
to_enum() allows you to specify the method you would like the enumerator to use:
str = "hello\nworld"
e = str.to_enum(:each_line)
puts e.next
--output:--
hello
Now, suppose you have the array [1, 2, 3], and you want to to create an enumerator for your array. An array has an each() method, but instead of creating an enumerator with each(), which will return each of the elements in the array, then end; you want to create an enumerator that starts over from the beginning of the array once it reaches the end?
e = [1, 2, 3].to_enum(:cycle)
10.times do
puts e.next()
end
--output:--
1
2
3
1
2
3
1
2
3
1
Given this irb session:
[2.0.0p195]> arr = [{count: 5}, {count: 6}, {count: 7}]
=> [{:count=>5}, {:count=>6}, {:count=>7}]
[2.0.0p195]> arr.collect(&:count)
=> [1, 1, 1]
wat
[2.0.0p195]> arr.collect(&:count).reduce(:+)
=> 3
[2.0.0p195]> arr.collect {|e| e[:count]}.reduce(:+)
=> 18
Can I exclude methods on Hash when collecting or is using a block the only way around this problem?
& means call #to_proc on its argument, and the Symbol class implements this by creating a Proc that calls the method name based on the symbol - so &:symbol means "Call the #symbol method on the passed in object". Essentially, what you've got is the equivalent of this:
arr.collect{|obj| obj.send(:count)}
Since Hash won't respond to the "count" method at all to get the value of the :count key - that is, Hash#count is not the same as Hash#[](:count), (though OpenStruct does do this for you), you're stuck with the block method.
Another alternative is to create a lambda, useful if you are writing the same block many times:
fetch_count = -> x{x[:count]}
arr.collect(&fetch_count) #=> [5, 6, 7]
# If hash only has one value as in example:
arr.collect(&values).flatten #=> [5, 6, 7]
The implementation of calling & on a symbol is as follows (more or less):
class Symbol
def to_proc
Proc.new { |obj| obj.send self }
end
end
You can see that all it is doing (when combined with a #map) is calling the method corresponding to the provided symbol on each member of the enumerable.
You could fix this if you really wanted by using OpenStructs instead of hashes, they have method-style access of elements:
[{test: 1}].map { |h| OpenStruct.new(h) }.map &:test
#=> [1]
Or invent an operator that does what you want for hash access in addition to &, I may revisit this challenge if I have a spare moment later!
EDIT: I have returned
This is hacky but you could monkey-patch symbol to provide the functionality that you wish for by augmenting with unary ~:
# Patch
class Symbol
def ~#
->(obj){ obj[self] }
end
end
# Example usage:
[{count: 5}, {count: 6}, {count: 7}].map &~:count
#=> [5, 6, 7]
If a free-for-all language such as Ruby doesn't have a feature that you wish for, you can always build it in :-)
Disclaimer: This is probably a terrible idea.
I have a class which inherits from Array (in practice, it's just a mask for a multidimensional array).
I want to override its to_a method:
def to_a
self.each.with_index { |el, i| el.map {|j| j} }
end
but this messes things up: when I try to test my function:
it 'should be non destructive' do
a_board = Representation.new(#a_size)
a_clean_board = Representation.new(#a_size)
expect(a_board).to eq(a_clean_board)
# Try to modify a_board
arr = a_board.to_a
arr.pop
a_board.to_a.pop
# Check that it stayed equal to a_clean_board
expect(a_board).to eq(a_clean_board)
end
both calls to pop have side effects on the original board.
How can I avoid this?
Probably because it returns reference to the same object. To avoid this use map instead of each or use .dup at the end.
UPD
As I said, just use map. Like it's in functional programming where intentionally are no side-effects. Example:
class WrappedArr < Array
def to_a
map { |el| el.map {|el2| el2 } }
end
end
w_arr = WrappedArr.new([[1,2], [2,3]])
# => [[1, 2], [2, 3]]
2.0.0p247 :012 > w_arr.to_a.object_id # always different as it is different object
#=> 70318090081080
2.0.0p247 :013 > w_arr.to_a.object_id
# => 70318095088040
2.0.0p247 :014 > w_arr.to_a.object_id
# => 70318095081540
# final test
2.0.0p247 :015 > w_arr.to_a.pop
# => [2, 3]
2.0.0p247 :016 > w_arr
# => [[1, 2], [2, 3]]
For methods in Ruby, is there anything similar to javascript's apply?
That is, if some method were defined to take a few parameters, say, some_method(a, b, c) and I have an array of three items, can I call some_method.apply(the_context, my_array_of_three_items)?
EDIT: (to clear up some confusion):
I don't care so much about the context of the call, I just want to avoid this:
my_params = [1, 2, 3]
some_method(my_params[0], my_params[1], my_params[2])
instead, I'm curious to know if there is something like this
my_params = [1, 2, 3]
some_method.apply(my_params)
There are bindings which are closest counterpart of javascript context, and there are unbound methods which can be later bound to object to be called in it's scope.
Bindings must be earlier captured, and they allow evaluating code from string in it's context.
Unbinding method captured from any object with method extractor allows you to later bind it to an object (note that it must share enough of interface for method to work) and call it in it's scope.
Unless you want to hack some very low-level things in Ruby, I would discourage use of both of above in favor of object-oriented solution.
EDIT:
If you simply want to call method, while it's arguments are contained in array use splat operator:
a = [1, 2, 3]
method(*a)
You can invoke a method whose name is only known at run-time by the send method on Class.
Update
To pass the arguments as an array:
$ irb
2.0.0p195 :001 > class Foo
2.0.0p195 :002?> def bar( a, b, c)
2.0.0p195 :003?> puts "bar called with #{a} #{b} #{c}"
2.0.0p195 :004?> end
2.0.0p195 :005?> end
=> nil
2.0.0p195 :006 > foo = Foo.new
=> #<Foo:0x000000022206a8>
2.0.0p195 :007 > foo.bar( 1, 2, "fred" )
bar called with 1 2 fred
=> nil
2.0.0p195 :009 > foo.send( :bar, *[1, 2, "jane"] )
bar called with 1 2 jane
=> nil
2.0.0p195 :010 >
Following is the code I tried to run from the Ruby Programming Book
http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_modules.html
Why doesn't the product method give the right output?
I ran it with irb test.rb. And I am running Ruby 1.9.3p194.
module Inject
def inject(n)
each do |value|
n = yield(n, value)
end
n
end
def sum(initial = 0)
inject(initial) { |n, value| n + value }
end
def product(initial = 1)
inject(initial) { |n, value| n * value }
end
end
class Array
include Inject
end
[1, 2, 3, 4, 5].sum ## 15
[1, 2, 3, 4, 5].product ## [[1], [2], [3], [4], [5]]
Since that code example was written, Array has gained a #product method and you're seeing the output of that particular method. Rename your module's method to something like product_new.
Add this line at the end of your code :
p Array.ancestors
and you get (in Ruby 1.9.3) :
[Array, Inject, Enumerable, Object, Kernel, BasicObject]
Array is a subclass of Object and has a superclass pointer to Object. As Enumerable is mixed in (included) by Array, the superclass pointer of Array points to Enumerable, and from there to Object. When you include Inject, the superclass pointer of Array points to Inject, and from there to Enumerable. When you write
[1, 2, 3, 4, 5].product
the method search mechanism starts at the instance object [1, 2, 3, 4, 5], goes to its class Array, and finds product (new in 1.9) there. If you run the same code in Ruby 1.8, the method search mechanism starts at the instance object [1, 2, 3, 4, 5], goes to its class Array, does not find product, goes up the superclass chain, and finds product in Inject, and you get the result 120 as expected.
You find a good explanation of Modules and Mixins with graphic pictures in the Pickaxe http://pragprog.com/book/ruby3/programming-ruby-1-9
I knew I had seen that some are asking for a prepend method to include a module before, between the instance and its class, so that the search mechanism finds included methods before the ones of the class. I made a seach in SO with "[ruby]prepend module instead of include" and found among others this :
Why does including this module not override a dynamically-generated method?
By the way: in Ruby 2.0, there are two features which help you with both your problems.
Module#prepend prepends a mixin to the inheritance chain, so that methods defined in the mixin override methods defined in the module/class it is being mixed into.
Refinements allow lexically scoped monkeypatching.
Here they are in action (you can get a current build of YARV 2.0 via RVM or ruby-build easily):
module Sum
def sum(initial=0)
inject(initial, :+)
end
end
module ArrayWithSum
refine Array do
prepend Sum
end
end
class Foo
using ArrayWithSum
p [1, 2, 3].sum
# 6
end
p [1, 2, 3].sum
# NoMethodError: undefined method `sum' for [1, 2, 3]:Array
using ArrayWithSum
p [1, 2, 3].sum
# 6
In response to #zeronone "How can we avoid such namespace clashes?"
Avoid monkeypatching core classes wherever possible is the first rule. A better way to do this (IMO) would be to subclass Array:
class MyArray < Array
include Inject
# or you could just dispense with the module and define this directly.
end
xs = MyArray.new([1, 2, 3, 4, 5])
# => [1, 2, 3, 4, 5]
xs.sum
# => 15
xs.product
# => 120
[1, 2, 3, 4, 5].product
# => [[1], [2], [3], [4], [5]]
Ruby may be an OO language, but because it is so dynamic sometimes (I find) subclassing gets forgotten as a useful way to do things, and hence there is an over reliance on the basic data structures of Array, Hash and String, which then leads to far too much re-opening of these classes.
The following code is not very elaborated. Just to show you that today you already have means, like the hooks called by Ruby when certain events occur, to check which method (from the including class or the included module) will be used/not used.
module Inject
def self.append_features(p_host) # don't use included, it's too late
puts "#{self} included into #{p_host}"
methods_of_this_module = self.instance_methods(false).sort
print "methods of #{self} : "; p methods_of_this_module
first_letter = []
methods_of_this_module.each do |m|
first_letter << m[0, 2]
end
print 'selection to reduce the display : '; p first_letter
methods_of_host_class = p_host.instance_methods(true).sort
subset = methods_of_host_class.select { |m| m if first_letter.include?(m[0, 2]) }
print "methods of #{p_host} we are interested in: "; p subset
methods_of_this_module.each do |m|
puts "#{self.name}##{m} will not be used" if methods_of_host_class.include? m
end
super # <-- don't forget it !
end
Rest as in your post. Execution :
$ ruby -v
ruby 1.8.6 (2010-09-02 patchlevel 420) [i686-darwin12.2.0]
$ ruby -w tinject.rb
Inject included into Array
methods of Inject : ["inject", "product", "sum"]
selection to reduce the display : ["in", "pr", "su"]
methods of Array we are interested in: ["include?", "index",
..., "inject", "insert", ..., "instance_variables", "private_methods", "protected_methods"]
Inject#inject will not be used
$ rvm use 1.9.2
...
$ ruby -v
ruby 1.9.2p320 (2012-04-20 revision 35421) [x86_64-darwin12.2.0]
$ ruby -w tinject.rb
Inject included into Array
methods of Inject : [:inject, :product, :sum]
selection to reduce the display : ["in", "pr", "su"]
methods of Array we are interested in: [:include?, :index, ..., :inject, :insert,
..., :private_methods, :product, :protected_methods]
Inject#inject will not be used
Inject#product will not be used