How to implement to_str or to_s - ruby

I have a class that should be used as a string and will always be a string (even if empty). The object will always have a string representation. The following is an example of my class:
class Something
def initialize
#real_string_value = "hello"
end
def to_s
return #real_string_value
end
def to_str
return #real_string_value
end
end
text = Something.new
puts("#{text}") #=> #<Something:0x83e008c>
I ran this test on minitest:
assert_equal(
"",
"#{#text}",
"Unchanged class should convert to empty by default"
)
The test above fails. Why isn't my class converted to a string?

The code print hello as expected if it's run as a script.
If you run the code in irb or similar interactive shell, it uses inspect instead of to_s method for the following line:
text = Something.new
You need to define inspect:
class Something
...
def inspect
to_s
end
end

Related

How to print an object

How do you change what is printed with puts when an object is referenced?
Consiser the following code:
class MyClass
attr_accessor :var
def initialize(var)
#var = var
end
# ...
end
obj = MyClass.new("content")
puts obj # Prints #<MyClass:0x0000011fce07b4a0> but I want it to print "content"
I imagine that there is an operator that you can overload (or something similar), but I don't know what it's called so I have no clue what to search for to find the answer.
Quote from the documentation of puts:
puts(*objects) → nil
Writes the given objects to the stream, which must be open for writing; returns nil. Writes a newline after each that does not already end with a newline sequence. [...]
Treatment for each object:
String: writes the string.
Neither string nor array: writes object.to_s.
Array: writes each element of the array; arrays may be nested.
That means: The object you pass to puts is not a string, therefore, Ruby will call to_s on that object before outputting the string to IO. Because your object has no to_s method implemented, the default implementation from Object#to_s.
To return a customize output, just add your own to_s method to your class like this:
class MyClass
attr_accessor :var
def initialize(var)
#var = var
end
def to_s
var
end
end
class MyClass
attr_accessor :var
def initialize(var)
#var = var
end
def to_s
#var
end
end
obj = MyClass.new("content")
puts obj # Prints "content"

rails 3 override model setter

I'm making a plugin and I need it to override a setter/getter for my model. Here's the code I have so far:
module Iplong
extend ActiveSupport::Concern
module ClassMethods
...
def override_setter
self.class_eval %(
def #{attribute}=(raw_value)
self[:#{attribute}] = #{ip2long('raw_value')}
end
)
end
end
end
ActiveRecord::Base.send :include, Iplong
Notice the raw_value param. If I print it in the evalued code it prints the correct value that comes when the attribute is set but if I print it inside the ip2long function where it is sent it returns a string: raw_value so how do I pass this parameter without it being interpreted as string?
Your problem is in this particular piece of code:
"#{ip2long('raw_value')}"
Translating this from a string to Ruby code you'd get:
ip2long('raw_value')
So you're actually sending the 'raw_value' string instead of the actual value of that variable.
Replace the code with:
"#{ip2long(raw_value)}"
And you should be fine.
Edit: This sample code shows how it'd work:
class A
attr_accessor :ip
def ip2num(ip)
ip.gsub(".", "")
end
def override(attr)
code = "def #{attr}=(value); #ip = ip2num(value); end"
self.class.class_eval(code)
end
end
a = A.new
a.ip = "0.0.0.0"
puts a.ip
a.override("ip")
a.ip = "0.0.0.0"
puts a.ip

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"

Open classes in a module

I know I can execute the following to add methods to the String class
class String
def do_something
puts self.size
end
end
var = "test"
var.do_something
and this will return 4
I want to be able to have a module with a function that takes in a String, but be able to call the do_something method on this string (see below for example) - is it possible?
EDIT: Added sample code that is not working
module TestModule
class String
def do_something
puts self.size
end
end
def self.test(str)
str.do_something
end
end
This gives the error: undefined method 'do_something' for "hello":String (NoMethodError)
The way your code is written, you're defining a new class called TestModule::String. If you want to modify the built-in Ruby String class, you need to use the fully-qualified name of String (with the ""::") if you want to keep the declaration inside the module.
module TestModule
class ::String
def do_something
puts self.size
end
end
def self.test(str)
str.do_something
end
end
Adding the "::" tells Ruby that the String class that you want is not part of the TestModule.
It's probably cleaner to just declare String outside of TestModule in the same file.
If you don't want to pollute the global String class, you could just modify the specific String instance that you want to add the method to.
module TestModule
def self.test(str)
do_somethingify!(str)
str.do_something
end
def self.do_somethingify!(str)
unless str.respond_to? :do_something
str.instance_eval do
def do_something
puts size
end
end
end
end
end
Maybe this?
module TestModule
module_function
def test(str)
str.instance_eval{doSomething}
end
end
Test.test(str)
Edit Changed due to the change in the question
Just put the definition of doSomething outside out the TestModule class.
class String
def doSomething
puts size
end
end
module TestModule
module_function
def test(str)
str.doSomething
end
end

Ruby - How to use the method parameter as the name of the variable?

How would I use the parameter value as the instance variable name of an object?
This is the object
Class MyClass
def initialize(ex,ey)
#myvar = ex
#myothervar = ey
end
end
I have the following method
def test(element)
instanceofMyClass.element #this obviously doesnt work
end
How can I have the test method return either myvar or myothervar value depending on the element parameter. I don't want to write an if condition though, I want to pass myvar or myother var via element to the object instance if possible.
def test(element)
instanceofMyClass.send(element.to_sym)
end
You'll get a missing method error if instanceofMyClass doesn't respond to element.
def test(element)
instanceofmyclass.instance_variable_get element
end
test :#myvar # => ex
test :#myothervar # => ey
I like the simplicity of send(), though one bad thing with it is that it can be used to access privates. The issue is still remains solution below, but at least then it's explicitly specified, and reader can see which methods are to be forwarded. The first one just uses delegation, while the second one uses more dynamic way to define methods on the fly.
require 'forwardable'
class A
extend Forwardable
def_delegators :#myinstance, :foo, :bar
class B
def foo
puts 'foo called'
end
def bar
puts 'bar called'
end
def quux
puts 'quux called'
end
def bif
puts 'bif called'
end
end
def initialize
#myinstance = B.new
end
%i(quux bif).each do |meth| # note that only A#quux and A#bif are defined dynamically
define_method meth do |*args_but_we_do_not_have_any|
#myinstance.send(meth)
end
end
end
a = A.new
a.foo
a.bar
a.quux
a.bif

Resources