Ruby object printing format - ruby

Consider the following:
irb(main):001:0> class A
irb(main):002:1> def initialize
irb(main):003:2> #string = "my string"
irb(main):004:2> end
irb(main):005:1> def to_s
irb(main):006:2> puts #string
irb(main):007:2> end
irb(main):008:1> end
=> nil
irb(main):009:0> a = A.new
=> #<A:0x2ea606c #string="my string">
irb(main):010:0> puts a
my string
#<A:0x2ea606c>
=> nil
When 'puts' outputs the string my does it also append the refference of the object ?
Is there anyway to get rid of this behavior? What I want outputed is justs the string #string as defined in the classes 'to_s' method
Thanks,
RM

You want your to_s method to just return #string, not do puts #string.

Related

Creating methods with class_eval

I want to write a method which takes one parameter and creates another method, named with this parameter. Here is my code
class Class
def createMethod(attr_name)
attr_name = attr_name.to_s
class_eval %Q{
def #{attr_name}
puts "bar"
end
}
end
end
p Class.new.createMethod("foo").respond_to?(:foo)
Unfortunately, respond_to?(:foo) evaluates to false. What's wrong?
This is because class_eval is a class method and you're calling it in the context of an instance. You can do this instead:
class Class
def createMethod(attr_name)
attr_name = attr_name.to_s
self.class.class_eval %Q{
def #{attr_name}
puts "bar"
end
}
self # Return yourself if you want to allow chaining methods
end
end
Here's the output from irb when doing this:
irb(main):001:0> class Class
irb(main):002:1> def createMethod(attr_name)
irb(main):003:2> attr_name = attr_name.to_s
irb(main):004:2> self.class.class_eval %Q{
irb(main):005:2" def #{attr_name}
irb(main):006:2" puts "bar"
irb(main):007:2" end
irb(main):008:2" }
irb(main):009:2> end
irb(main):010:1> end
=> nil
irb(main):011:0> clazz = Class.new
=> #<Class:0x007fd86495cd58>
irb(main):012:0> clazz.respond_to?(:foo)
=> false
irb(main):013:0> clazz.createMethod("foo")
=> nil
irb(main):014:0> clazz.respond_to?(:foo)
=> true

In a Ruby Class that Inherits from String, How do I Get "#{my_string}" with explicit .to_s

In short, I expect all three #{}s in the final line of this test program to result in the same string and they don't. It works if my class inherits from Object.
> ./test.rb
A string foobar, and MyString: , MyString.to_s: foobar
> cat test.rb
#!/usr/bin/env ruby
class MyString < String
def initialize s
#s= s.to_s
#simple = s.index(?+)
end
def to_s() #s end
end
x='foobar'
puts "A string #{x}, and MyString: #{
MyString.new(x)}, MyString.to_s: #{MyString.new(x).to_s}"
It seems like string interpolation handles objects of type String differently:
irb(main):011:0> class MyString < String
irb(main):012:1> def initialize(str)
irb(main):013:2> #str = str
irb(main):014:2> end
irb(main):015:1> def to_s
irb(main):016:2> puts "to_s called!"
irb(main):017:2> #str
irb(main):018:2> end
irb(main):019:1> end
=> nil
irb(main):020:0> "#{MyString.new('foo')}"
=> ""
As you can see, to_s is not even called. The reason for that is the invariant str.to_s == str where str.is_a? String. You broke that invariant, thus confusing the library code.
Conclusion: Don't override to_s if you're subclassing String (it doesn't make a lot of sense anyway).
As Niklas B. demonstrates, string interpolation doesn't call to_s on String objects. The default value for the argument to String#initialize is "", so instances of your subclass are all in effect empty strings.
One way to get this to work is to call super in the initialize method if your subclass, and pass the string value:
class MyString < String
def initialize s
super s.to_s # this line added
#s= s.to_s
#simple = s.index(?+)
end
def to_s() #s end
end
puts "#{MyString.new("foo")}" # => produces "foo"

Where does Ruby store method defined in instance_eval()

Here is an example:
class MyClass
end
obj = MyClass.new
obj.instance_eval do
def hello
"hello"
end
end
obj.hello
# => "hello"
obj.methods.grep "hello"
# => ["hello"]
MyClass.instance_methods.grep "hello"
# => []
MyClass's instance methods don't contain 'hello' method, so My question is where Ruby stores the method defined in instance_eval()?
Look at this:
obj = MyClass.new
def obj.hello
"hello"
end
obj.hello #=> "hello"
obj.singleton_methods #=> [:hello]
obj.methods.grep :hello #=> [:hello]
obj.instance_eval do
def hello2 ; end
end #
obj.singleton_methods #=> [:hello, :hello2]
As you can see instead of using instance_eval you can also define a method directly on an object. In both cases they end up in the object's singleton class (eigenclass), which can be accessed via obj.singleton_class in Ruby 1.9 and the class << self ; self; end idiom in Ruby 1.8.

how to write proper method?

class Mycompute
def initialize(str)
#str=str
end
def values
##result=#str
end
def up
##result.upcase
end
end
irb(main):012:0> Mycompute.new("Abc").values
=> "Abc"
irb(main):013:0>
irb(main):014:0* Mycompute.new("Abc").up
=> "ABC"
irb(main):015:0> Mycompute.new("Abc").values.up
NoMethodError: undefined method `up' for "Abc":String
from (irb):15
from :0
How can I make Mycompute.new("Abc").values.up work?
The object returned from values need have up.
class Mycompute
def initialize(str)
#str=str
end
def values
##result=#str
def ##result.up
self.upcase
end
##result
end
def up
##result.upcase
end
end
This works well.
Mycompute.new("Abc").values #=> "Abc"
Mycompute.new("Abc").up #=> "ABC"
Mycompute.new("Abc").values.up #=> "ABC"

Class returning an array

I have a class like:
class Configuration
def self.files
##files ||= Array.new
end
end
Now instead of doing this:
irb(main):001:0> Configuration.files
=> [file, file, file]
I would like to be able to do this:
irb(main):001:0> Configuration
=> [file, file, file]
But I can't figure out how, any ideas?
#glenn jackman:
I was thinking of adding extra methods to a 'Configuration' constant that's a hash. So if I had...
Configuration = Hash.new
Configuration[:foo] = 'bar'
I wanted to be able to save this Configuration hash constant to be able to dump to and load from a YAML file I wanted to be able to use...
Configuration.load
Configuration.save
I wanted the Configuration class to look like
class Configuration
def self.save
open('config.yml', 'w') {|f| YAML.dump( self , f)}
end
def self.load
open('config.yml') {|f| YAML.load(f)}
end
end
You could possibly get the effect you are looking for by adding a method to the top level object which implicitly calls Configuration.files, but you can't really make a reference to a class invoke a method on it. You can alias the method to something shorter, but you will need to call something.
irb's top level just calls 'inspect' on the result, so by overriding it you can customize what you see in irb:
$ irb
>> class Configuration
>> def self.files
>> ##files ||= Array.new
>> end
>> def self.inspect
>> ##files.inspect
>> end
>> end
=> nil
>> Configuration.files << 1 << 2 << 3
=> [1, 2, 3]
>> Configuration
=> [1, 2, 3]
You could do something like this:
class Configuration
(class << self; self; end).module_eval do
def files
['foo','bar','baz']
end
def to_s
files
end
end
end
This would define the Configuration.files method and say that the to string conversion would return the result of that method. But I am really not sure why you would want to do this. It seems quite wrong.
wpc#wpc-laptop:~$ irb
irb(main):001:0> 'a'
=> a
irb(main):002:0> def A
irb(main):003:1> end
=> nil
irb(main):004:0> A
NameError: uninitialized constant A
from (irb):4
from :0
irb(main):005:0> class A
irb(main):006:1> end
=> nil
irb(main):007:0> A
=> A
irb(main):008:0> class A
irb(main):009:1> (class ['g']
irb(main):012:3> end
irb(main):013:2> def to_s
irb(main):014:3> g
irb(main):015:3> end
irb(main):016:2> end
irb(main):017:1> end
=> nil
irb(main):018:0> A
=> g
irb(main):019:0> class B
irb(main):020:1> class def to_s
irb(main):022:3> 'g'
irb(main):023:3> end
irb(main):024:2> end
irb(main):025:1> end
=> nil
irb(main):026:0> B
=> g
irb(main):027:0> class B
irb(main):028:1> class def files
irb(main):030:3> ['a','b','c']
irb(main):031:3> end
irb(main):032:2> def to_s
irb(main):033:3> files
irb(main):034:3> end
irb(main):035:2> end
irb(main):036:1> end
=> nil
irb(main):037:0> B
=> abc
The right solution is to using the class' method of to_s, but what it return is only the string; and then you can set the inspect for using. See the follows' detail for help.
class A
class<<self
def to_s
......
end
end
end

Resources