Im working with a class, containing large instance arrays.
Whenever I initialize a class like this, e.g. i = Image.new, I get a lot of junk output from my arrays (#r, #g, #b - ~300k values each).
class Image
def initialize(width=640, height=480, brightness=64)
#width, #height, #brightness = width, height, brightness
self.load('usart.dat')
end
def load(file='usart.dat')
self.reset
f = IO.read(file, #height * #width * 2, 0)
# Parsing the datafile, saving data in #r, #g, #b, #gray etc
end
return self
end
# ... More methods
end
Question is, how can I either silence the output (all results are saved to a file, never viewed in the console) or make an initializer NOT inspect self. I want to return self, since I want to stack methods, e.g. image.load('file').binary.grayscale.save(:bin).
A common workaround for this type of problem is to just add ; nil in IRB, e.g. i = Image.new ; nil. This way the last evaluated expression is nil which will also be returned.
>> Foo.new
=> #<Foo:0x00000000e3b9d0 #a=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]>
>> Foo.new ; nil
=> nil
The output you are seeing is the result of your object's to_s (or inspect). You can define/overwrite to_s (or inspect) for your classes to produce less output. E.g.
irb(main):001:0> class Foo
irb(main):002:1> def initialize
irb(main):003:2> #a = #b = #c = #d = 42
irb(main):004:2> end
irb(main):005:1>
irb(main):006:1* def to_s
irb(main):007:2> 'a Foo object'
irb(main):008:2> end
irb(main):009:1> end
=> nil
irb(main):010:0> h = Foo.new
=> a Foo object
irb(main):011:0>
Regarding to_s/inspect: irb calls inspect which normally just calls to_s (see ruby-doc). So, defining to_s should normally work. However, if there is an inspect not calling to_s in the class (or its ancestors), this inspect has to be overwritten.
Disable irb echoing:
conf.echo = false
The gem utility_belt will give you commands quiet and verbose to disable/enable the output (and more).
If you changed the return value of Foo.new to not return the new Foo object, you would never be able to assign the new Foo to any variable. If the only concern is occasionally scrolling the console view, use the ";nil" approach.
Related
I'm learning ruby and my opinion about attr_reader is bad. Because you can change the value of particular instance variable from outside of class. For example:
class X
attr_reader :some_string
def initialize
#some_string = "abc"
end
end
tmp = X.new
puts tmp.some_string
tmp.some_string[0] = "aaaaaaaaa"
puts tmp.some_string
When you run this code you can see that the instance variable some_string has changed. So to avoid this I'm always making my own getter, and returning frozen object. For example:
class X
def initialize
#some_string = "abc"
end
def some_string
#some_string.freeze
end
end
tmp = X.new
puts tmp.some_string
tmp.some_string[0] = "aaaaaaaaa"
puts tmp.some_string
Now when you run the code, it throws an error saying can't modify frozen String: "abc", which is what I wanted. So my question is should I use attr_reader and is always returning frozen objects from getters bad practice?
attr_reader is not "bad", it simply does what it does, which is to return the value of the instance variable, which is a reference. Unlike attr_writer or attr_accessor it will not allow you to change the value of the instance variable (ie you can't change what the instance variable refers to)
The question is do you really want or need the reference. For example say you want to convert the value of some_string to upper case. You could use attr_reader to get the reference to some_string and then call upcase! on it like below. But having the reference allows you to call any method on the object including the []= method which maybe you don't want to allow. Note: []= is a method that manipulates the content of what some_string references it does not set #some_string to a new value, it still points to the same object, but the object it points to was manipulated.
class Foo
attr_reader :some_string
def initialize()
#some_string = "abc"
end
end
puts "Foo"
foo = Foo.new
some_string = foo.some_string
puts some_string #=> abc
some_string.upcase!
p foo # => #<Foo:0x0000563c38391ac8 #some_string="ABC">
puts some_string.object_id # => 60
some_string[0] = "x"
p foo # => #<Foo:0x0000563c38391ac8 #some_string="xBC">
puts some_string.object_id # => 60 ... ie same object different content
# foo.some_string = "ABC" is a runtime error
If you don't want to allow arbitrary methods to be called on an instance variable then you should not expose it using attr_reader, rather you should manipulate the instance variable via methods in your class. For example below I "delegate" the upcase! method to the instance variable #some_string, and I provide a string_value method to return a new string with the same value as the instance variable.
class Bar
def initialize()
#some_string = "abc"
end
def upcase!()
#some_string.upcase!
end
def string_value()
"#{#some_string}"
end
end
puts "Bar"
bar = Bar.new
p bar # => #<Bar:0x0000563c383915a0 #some_string="abc">
bar.upcase!
p bar # => #<Bar:0x0000563c383915a0 #some_string="ABC">
some_string = bar.string_value
p some_string # => "ABC"
some_string[0] = "x"
p bar # => #<Bar:0x0000563c383915a0 #some_string="ABC">
p some_string # => "xBC"
So I would say attr_reader is not bad, you might argue it is over used, I know I will often use it to "get" an instance variable when all I really need is some property on the instance variable.
A lot of developers try to use private attr_reader and use it inside the class or avoid using it at all
A good conversations about attr_reader are here and here
I'm trying to add an instance method foo to Ruby's Array class
so when it's invoked, the array's string elements are changed to string "foo".
This can be done easily by monkey patching Ruby's String and Array classes.
class String
def foo
replace "foo"
end
end
class Array
def foo
self.each {|x| x.foo if x.respond_to? :foo }
end
end
a = ['a', 1, 'b']
a.foo
puts a.join(", ") # you get 'foo, 1, foo' as expected
Now I'm trying to rewrite the above using Ruby 2's refinements feature.
I'm using Ruby version 2.2.2.
The following works (in a file, eg. ruby test.rb, but not in irb for some reason)
module M
refine String do
def foo
replace "foo"
end
end
end
using M
s = ''
s.foo
puts s # you get 'foo'
However, I can't get it to work when adding foo onto the Array class.
module M
refine String do
def foo
replace "foo"
end
end
end
using M
module N
refine Array do
def foo
self.each {|x| x.foo if x.respond_to? :foo }
end
end
end
using N
a = ['a', 1, 'b']
a.foo
puts a.join(", ") # you get 'a, 1, b', not 'foo, 1, foo' as expected
There're two issues:
After you refine a class with a new method, respond_to? does not work even when you can invoke
the method on an object. Try adding puts 'yes' if s.respond_to? :foo
as the last line in the second code snippet, you'll see 'yes' is not printed.
In my Array refinement, the String#foo is out of scope. If you remove if x.respond_to? :foo from
the Array#foo, you'll get the error undefined method 'foo' for "a":String (NoMethodError). So the question is: how do you make the String#foo refinement visible inside the Array#foo refinement?
How do I overcome these two issues so I can get this to work?
(Please don't offer alternative solutions that don't involve refinement, because this is a theoretical exercise so I can learn how to use refinement).
Thank you.
The respond_to? method does not work and this is documented
here.
The problem is that you can only activate a refinement at top-level
and they are lexical in scope.
One solution would be:
module N
refine String do
def foo
replace 'foobar'
end
end
refine Array do
def foo
self.each do |x|
x.foo rescue x
end
end
end
end
using N
a = ['a', 1, 'b']
p a.foo
puts a.join(", ") # foo, 1, foo
Taking up your example again, a simple solution could be to override the respond_to? method in refinement block :
module M
refine String do
def foo
replace "foo"
end
def respond_to?(name,all=false)
list_methods = self.methods.concat [:foo]
list_methods.include? name
end
end
refine Array do
def foo
self.each {|x| x.foo if x.respond_to? :foo }
end
end
end
using M
a = ['a', 1, 'b']
a.foo
puts a.join(", ") # you get 'foo, 1, foo'
I have a class called Test. I've created an instance:
example = Test.new
I want to return a value when I type this:
example
I know how to return a value when typing:
example.give_me_some_stuff
But I can't figure out how to override just the instance name and return some value, preferably the result of a method inside of the class.
EDIT:
This works for a method that returns a string, but I can't return an instance variable from the instance of the class. For example here is the test I'm trying to make work:
def test_push
stack = Stack.new
stack.push(10)
binding.pry
refute stack.empty?
end
And here is my class:
class Stack
attr_accessor :contents
def inspect
#contents
end
def initialize
#contents = []
end
def push(n)
#contents << n
end
end
In the test my object comes back as:
#<Stack:0x007f8cd9051938 #contents=[10]>
When I want it to return just the value of #contents.
example always returns your object. If it would return another object, example.give_me_some_stuff would not work anymore.
Maybe you're looking for inspect. IRB calls inspect when printing your object:
class Test
def inspect
"I am Test"
end
end
IRB session:
irb(main):001:0> example = Test.new
=> I am Test
irb(main):002:0> example
=> I am Test
Did you mean this?:
Test = Struct.new('Test', :a, :b)
example = Test.new(1, 2)
example.instance_eval do
def give_me_some_stuff
a + b
end
end
example.give_me_some_stuff #=> 3
another_example = Test.new(1, 2)
# => #<struct Struct::Test a=1, b=2>
another_example.give_me_some_stuff
#=> NoMethodError: undefined method `give_me_some_stuff' for #<struct Struct::Test a=1, b=2>
I noticed some weird behavior with instance variables in Ruby the other day. I was trying to add an instance variable array, containing other instance variable "attributes" of the class. The class is initialized without any parameters, but I still wanted to create this array at initialization. Here's an example of a (stripped-down) class:
class Foo
attr_accessor :bar, :baz
attr_reader :attrs
def initialize
#attrs = [#bar, #baz]
end
end
Here's where it gets weird:
f = Foo.new #=><Foo.0x[object_id] #attrs=[nil, nil]>
f.bar = "bar" #=>"bar"
f.baz = "baz" #=>"baz"
f.attrs #=>[nil, nil]
At initialization, I can see that Foo.attrs is [nil, nil]. But after updating Foo.bar and Foo.baz, why is Foo.attrs still returning [nil, nil]? Why aren't their new values reflected?
I figured this wasn't the best way to do this, and found a way around it, but I'm still curious about this behavior.
Because that's how variables work, here and in virtually every other programming language.
Your array contains the values of #bar and #baz at the time the array was created. It does not contain references to the variables themselves. Modifying one does not modify the other.
Effectively you've done this:
x = 3;
y = x;
x = 4;
# Why doesn't y equal 4?
y is not 4 because x and y share a value but are otherwise unrelated. Reassigning x to a new value does not modify the value that y contains.
If you want this to work, you need to make an accessor that builds the array on-demand, using the current values of your member variables:
class Foo
attr_accessor :bar, :baz
def attrs
[#bar, #baz]
end
end
You can simply add a puts and see what happens
class Foo
attr_accessor :bar, :baz
attr_reader :attrs
def initialize
#attrs = [#bar, #baz]
puts "inside initialize"
end
end
Now you can see initialize gets executed when you create an instance of Foo
f = Foo.new
#=> inside initialize
#=> #<Foo:0x2bc1bb0 #attrs=[nil, nil]>
f.bar = "bar" #=>"bar" , "inside initialize" not printed
If you do want to get them assigned then create a setter
class Foo
attr_accessor :bar, :baz
attr_reader :attrs
def initialize
#attrs = [#bar, #baz]
puts "inside initialize"
end
def bar=(v)
#bar = v
#attrs = [#bar,#baz]
end
def baz=(v)
#baz = v
#attrs = [#bar,#baz]
end
end
f = Foo.new
#=> inside initialize
#=> #<Foo:0x2bc1bb0 #attrs=[nil, nil]>
f.bar = "bar"
f.attrs #=> ["bar", nil]
f.baz = "baz"
f.attrs #=> ["bar", "baz"]
Why can you chain this:
"Test".upcase.reverse.next.swapcase
but not this:
x = My_Class.new
x.a.b.c
where
class My_Class
def a
#b = 1
end
def b
#b = #b + 2
end
def c
#b = #b -72
end
end
The upcase, reverse, next and swapcase methods all return String objects and all those methods are for... you guessed it, String objects!
When you call a method (more often than not, like 99.9999% of the time) it returns an object. This object has methods defined on it which can then be called which explains why you can do this:
"Test".upcase.reverse.next.swapcase
You can even call reverse as many times as you like:
"Test".reverse.reverse.reverse.reverse.reverse.reverse.reverse.reverse
All because it returns the same kind of object, a String object!
But you can't do this with your MyClass:
x = My_Class.new
x.a.b.c
For that to work, the a method would have to return an object which has the b method defined on it. Right now, that seems like only instances of MyClass would have that. To get this to work you could make the return value of a the object itself, like this:
def a
#b += 2
self
end
Extrapolating this, the b method would also need to return self as the c method is available only on instances of the MyClass class. It's not important what c returns in this example, because it's the end of the chain. It could return self, it could not. Schrödinger's cat method. Nobody knows until we open the box.
As support for other answers, this code:
"Test".upcase.reverse.next.swapcase
...is almost exactly the same as...
a = "Test"
b = a.upcase
c = b.reverse
d = c.next
e = d.swapcase
....except that my code above has extra variables left over pointing to the intermediary results, whereas the original leaves no extra references around. If we do this with your code:
x = MyClass.new # x is an instance of MyClass
y = x.a # y is 1, the last expression in the a method
z = y.b # Error: Fixnums have no method named 'b'
Using Ruby 1.9's tap method, we can even make this more explicit:
irb> "Test".upcase.tap{|o| p o}.reverse.tap{|o| p o}.next.tap{|o| p o}.swapcase
#=> "TEST"
#=> "TSET"
#=> "TSEU"
=> "tseu"
irb> class MyClass
irb> def a
irb> #b = 1
irb> end
irb> def b
irb> #b += 2
irb> end
irb> end
=> nil
irb(main):011:0> x = MyClass.new
=> #<MyClass:0x000001010202e0>
irb> x.a.tap{|o| p o}.b.tap{|o| p o}.c
#=> 1
NoMethodError: undefined method `b' for 1:Fixnum
from (irb):12
from /usr/local/bin/irb:12:in `<main>'
The last expression in a function is its implicit return value. You need to return self if you want to chain methods like that.
For example, your a method is currently returning 1. b is not a method for numbers. You'll want to modify it like so:
def a
#b = 1
self
end