Monkey-patching ruby method with bang - ruby

Is it possible to monkey-patch a method with a bang at the end?
I want to monkey-patch String.upcase!, but I don't know how to achieve that.
The problem is that I want to change the original string object.
Here's an example:
class String
def upcase!
self.mb_chars.upcase.to_s
end
end
Now if I type that in console and try it out, it doesn't work:
[1] pry(main)> asd="asd"
=> "asd"
[2] pry(main)> asd.upcase
=> "ASD"
[3] pry(main)> asd
=> "asd"
[4] pry(main)> asd.upcase!
=> "ASD"
[5] pry(main)> asd
=> "asd"

You should avoid monkey patching top-level class like String. If you want to know why, I strongly recommend you to read Monkeypatching is Destroying Ruby by Avdi Grimm.
Now to answer your question, you could do something like this:
class String
def upcase!
replace(upcase) # self is not mandatory here
end
end
> s = "foo"
=> "foo"
> s.upcase
=> "FOO"
> s
=> "foo"
> s.upcase!
=> "FOO"
> s
=> "FOO"

Your issue is independent of the method having a bang. If you want to replace the receiver string, use the method String#replace.
class String
def foo
replace(whatever_string_you_want_to_replace_the_receiver_with)
end
end
You can perhaps put mb_chars.upcase as the argument to replace.

The bang is just part of the method name. It has absolutely no special meaning whatsoever. You write a method with a bang at the end the exact same way you write a method with an 'o' or a 'z' at the end.

How about:
class String
def upcase!
#Your logic here
end
end

Related

Kernel#Array - How does Ruby interpret methods that have the same name as a constant?

When I'm coding in Ruby, to avoid type checks and respond_to?s and nil checks I often coerce objects into the type I expect. If I expect an Array, instead of foo.nil? ? handle_nil(foo) : handle_array(foo) I prefer to just handle_foo(foo.to_a) since nil.to_a # => [].
This doesn't work for everything, 1.to_a raises NoMethodError: undefined method 'to_a' for 1:Integer. 1.to_ary raises the same error. Array() seems to solve the problem, handling what seems like any object and either making a sensible conversion to an Array or wrapping it in [].
Array(nil) # => []
Array('') # => [""]
Array(Class) # => [Class]
Array(1) # => [1]
Array(Object.new) # => [#<Object:0x007fdbd40df4c8>]
Array({}) # => []
Array(foo: :bar) # => [[:foo, :bar]]
You can do the same thing with other classes too. String(), Float()... I'd used it a lot without thinking about the unusual syntax. I wanted to learn more about the implementation. Is it syntactic sugar for a special method, similar to #[]?
Both of these raise a SyntaxError
def self.(foo)
puts foo
end
def self.()(foo)
puts foo
end
You use Array() like you would use a method. Looking at the docs for Kernel it becomes clear that that's what's going on. The following works as you'd expect:
class Thing
attr_accessor :name
def initialize(name)
#name = name
end
end
module Kernel
def Thing(name)
Thing.new(name)
end
end
thing = Thing('name') # => #<Thing:0x007fefef89d8e8 #name="name">
thing.name # => "name"
You can also do this
HI = 'hi'.freeze
def HI(word)
word
end
HI # => "hi"
HI("ho") # => "ho"
My question is: How does Ruby interpret this? How does it distinguish between the method and the constant? Is this a silly question? Given "normal" behavior, one might expect Ruby to complain with a name warning when you define the method, or raise a SyntaxError. Does anyone know how Ruby handles this under the hood?
Thanks!
How does Ruby interpret this? How does it distinguish between the method and the constant?
The rules are pretty much the same as with local variables: Ruby will assume it is a constant unless it's obvious that it isn't: constants can't have arguments, and constants can't have receivers.
FOO # constant
self.FOO # message send
FOO() # message send
FOO :bar # message send
Given "normal" behavior, one might expect Ruby to complain with a name warning when you define the method, or raise a SyntaxError.
Why? Defining a method is perfectly legal syntactically (after all, that's what OO is all about), and Array is a perfectly legal Ruby identifier.

Ruby Error When Trying to define methods

irb(main):001:0> class Fixnum
irb(main):002:1> define_method(:gimme_five) do
irb(main):003:2* 5
irb(main):004:2> end
irb(main):005:1> end
=> #<Proc:0x007ff4ed01cd10#(irb):2 (lambda)>
irb(main):006:0> class String
irb(main):007:1> define_method(:scramble) do
irb(main):008:2* new_string = self.reverse()
irb(main):009:2> new_string = new_string.upcase()
irb(main):010:2> new_string
irb(main):011:2> end
irb(main):012:1> end
=> #<Proc:0x007ff4ed02d868#(irb):7 (lambda)>
irb(main):013:0>
I'm am not sure what I am doing wrong but my methods are not being defined???
In ruby, it is much more common to define methods as follows (using your output as an example:
def gimme_five
5
end
I believe that the syntax you are using to define your methods 'works', but what it is doing is creating 'lambdas' instead of regular methods. Lambdas and Procs compose Ruby's support for functional programming, and are special Ruby objects that represent blocks of code -- think of them as mini bundled up methods you can pass around to other objects.
So yeah, just define methods normally and should be fine -- the output in your irb REPL is just returning the lambda for no reason in particular -- just like how if you were to set an array in irb, you would get that array returned back:
>> a = [5]
=> [5]

Monkey patching Ruby's Hash class

What could be the repercussions of monkey patching Hash to act like this:
class Hash
def method_missing(method,*args, &block)
if self.has_key?(method)
return self[method]
elsif self.has_key?(method.to_s)
return self[method.to_s]
else
return nil
end
end
end
My justification is the following:
Basically, when I add objects to hashes, I make sure their keys.to_s are unique.
Should I be concerned, Am I missing something?
h = { :foo => "bar", "hello" => "bar" }
h.foo => "bar"
h.hello => "bar"
There is a native ruby object for this: OpenStruct.
You can instantiate an OpenStruct with a hash.
o = OpenStruct.new(hello: 'world')
Once it is initialized, you can use all the keys as methods.
o.hello #=> "world"
Therefore you don't need to monkey-patch your hash (which can lead to weird behaviors indeed).
And OpenStruct manages internally the duplicates between string and symbol entries.
OpenStruct.new(:hello => 'world', 'hello' => 'world2') #=> #<OpenStruct hello="world2">
BAD IDEA!!
If you misspell any of the valid methods of hash, you will get a nil instead of the real method missing. Eg: {}.count => 0, {}.counts => nil instead of NoMethodError: undefined method `counts' for {}:Hash.

Variable name from Self in Ruby

I have a variable lets call it #foo. I expect it to be a string so I call #foo.downcase. Sometimes it's nil and i'll get this error:
NoMethodError: undefined method `downcase' for nil:NilClass
from (irb):4
from /Users/schneems/.rvm/rubies/ruby-1.9.2-p290/bin/irb:16:in `<main>
What I want to do is write some code to tell me that nil is actually #foo
NoMethodError: undefined method `downcase' on variable #foo, variable is a nil:NilClass
from (irb):4
from /Users/schneems/.rvm/rubies/ruby-1.9.2-p290/bin/irb:16:in `<main>
To do something like this, I would need to get the name of the variable programatically.
Question: Is it possible to get the name of a variable programmatically ruby?
I'm looking for something that produces an output like this:
#foo.magical_variable_method #=> '#foo'
bar.magical_variable_method #=> 'bar'
AweSome.magical_variable_method #=> 'AweSome'
$wat.magical_variable_method #=> '$wat'
I don't want the value of the variable, nor do i care if it is nil. I want the human readable name of the variable. Is it possible to get the name of a variable programmatically ruby?
What you want is kinda mess.
Here's proper way for this task:
#foo && #foo.downcase
Or you could use rails library:
require 'active_support'
#foo.try(:downcase)
There isn't currently any way to do this.
Variables are just references, not objects themselves. If you use dot notation to call a method, like downcase, that method is operating on an object, not a variable. And the object in your example is the singleton nil; if nil in one place were to have a property assigned to it naming the variable it was assigned to, that same property would apply to all nils.
Even more generally, one object may have many variables/references pointing to it, so there would be no good way of determining which variable name should be saved in the object. However, there is some sort of special treatment in Ruby for assigning a class to a constant; in that case, the class object does remember the name of the first constant it's assigned to, e.g.:
$ irb
1.9.3p194 :001 > Foo = Class.new do
1.9.3p194 :002 > attr_accessor :foo
1.9.3p194 :003?> end
=> Foo
1.9.3p194 :004 > Bar = Foo
=> Foo
1.9.3p194 :005 > Foo.name
=> "Foo"
1.9.3p194 :006 > Bar.name
=> "Foo"
You can get the instance variable from the symbol with instance_variable_get(:#foo)
which would just give you the value.
But you could do this:
puts "I'm gonna call #foo now, people"
some_obj.instance_variable_get(:#foo)
You could also do a method_missing hook for the attrs you wanted to watch. Don't create accessor methods for #foo and catch the call in method_missing and forward it to some generic implementation such as instance_variable_get
Untested attempt:
def method_missing(method_name, *args)
super(method_name, *args) unless watched_attributes.include?(method_name)
attr = ":##{method_name.to_str}"
log.debug "Calling watched attribute #{attr}"
val = instance_variable_get(method_name)
log.debug "#{attr} was nil omg!" unless val
val
end
Edit: Probably the best way (see mu's comment) is to just make sure you're working with a string:
#foo.to_s.downcase
You don't need to get the "name" to do this. You can just use a conditional:
# Downcased if #foo's a string, empty string otherwise:
(String === #foo)? #foo.downcase : ''
Or, if you're going to be doing this in lots of places, and don't want to have to wrap each one in a conditional, monkey-patch NilClass and add a dummy downcase method:
class NilClass
def downcase
return ''
end
end
Hope that helps!

Adding an instance variable to a class in Ruby

How can I add an instance variable to a defined class at runtime, and later get and set its value from outside of the class?
I'm looking for a metaprogramming solution that allows me to modify the class instance at runtime instead of modifying the source code that originally defined the class. A few of the solutions explain how to declare instance variables in the class definitions, but that is not what I am asking about.
Ruby provides methods for this, instance_variable_get and instance_variable_set. (docs)
You can create and assign a new instance variables like this:
>> foo = Object.new
=> #<Object:0x2aaaaaacc400>
>> foo.instance_variable_set(:#bar, "baz")
=> "baz"
>> foo.inspect
=> #<Object:0x2aaaaaacc400 #bar=\"baz\">
You can use attribute accessors:
class Array
attr_accessor :var
end
Now you can access it via:
array = []
array.var = 123
puts array.var
Note that you can also use attr_reader or attr_writer to define just getters or setters or you can define them manually as such:
class Array
attr_reader :getter_only_method
attr_writer :setter_only_method
# Manual definitions equivalent to using attr_reader/writer/accessor
def var
#var
end
def var=(value)
#var = value
end
end
You can also use singleton methods if you just want it defined on a single instance:
array = []
def array.var
#var
end
def array.var=(value)
#var = value
end
array.var = 123
puts array.var
FYI, in response to the comment on this answer, the singleton method works fine, and the following is proof:
irb(main):001:0> class A
irb(main):002:1> attr_accessor :b
irb(main):003:1> end
=> nil
irb(main):004:0> a = A.new
=> #<A:0x7fbb4b0efe58>
irb(main):005:0> a.b = 1
=> 1
irb(main):006:0> a.b
=> 1
irb(main):007:0> def a.setit=(value)
irb(main):008:1> #b = value
irb(main):009:1> end
=> nil
irb(main):010:0> a.setit = 2
=> 2
irb(main):011:0> a.b
=> 2
irb(main):012:0>
As you can see, the singleton method setit will set the same field, #b, as the one defined using the attr_accessor... so a singleton method is a perfectly valid approach to this question.
#Readonly
If your usage of "class MyObject" is a usage of an open class, then please note you are redefining the initialize method.
In Ruby, there is no such thing as overloading... only overriding, or redefinition... in other words there can only be 1 instance of any given method, so if you redefine it, it is redefined... and the initialize method is no different (even though it is what the new method of Class objects use).
Thus, never redefine an existing method without aliasing it first... at least if you want access to the original definition. And redefining the initialize method of an unknown class may be quite risky.
At any rate, I think I have a much simpler solution for you, which uses the actual metaclass to define singleton methods:
m = MyObject.new
metaclass = class << m; self; end
metaclass.send :attr_accessor, :first, :second
m.first = "first"
m.second = "second"
puts m.first, m.second
You can use both the metaclass and open classes to get even trickier and do something like:
class MyObject
def metaclass
class << self
self
end
end
def define_attributes(hash)
hash.each_pair { |key, value|
metaclass.send :attr_accessor, key
send "#{key}=".to_sym, value
}
end
end
m = MyObject.new
m.define_attributes({ :first => "first", :second => "second" })
The above is basically exposing the metaclass via the "metaclass" method, then using it in define_attributes to dynamically define a bunch of attributes with attr_accessor, and then invoking the attribute setter afterwards with the associated value in the hash.
With Ruby you can get creative and do the same thing many different ways ;-)
FYI, in case you didn't know, using the metaclass as I have done means you are only acting on the given instance of the object. Thus, invoking define_attributes will only define those attributes for that particular instance.
Example:
m1 = MyObject.new
m2 = MyObject.new
m1.define_attributes({:a => 123, :b => 321})
m2.define_attributes({:c => "abc", :d => "zxy"})
puts m1.a, m1.b, m2.c, m2.d # this will work
m1.c = 5 # this will fail because c= is not defined on m1!
m2.a = 5 # this will fail because a= is not defined on m2!
Mike Stone's answer is already quite comprehensive, but I'd like to add a little detail.
You can modify your class at any moment, even after some instance have been created, and get the results you desire. You can try it out in your console:
s1 = 'string 1'
s2 = 'string 2'
class String
attr_accessor :my_var
end
s1.my_var = 'comment #1'
s2.my_var = 'comment 2'
puts s1.my_var, s2.my_var
The other solutions will work perfectly too, but here is an example using define_method, if you are hell bent on not using open classes... it will define the "var" variable for the array class... but note that it is EQUIVALENT to using an open class... the benefit is you can do it for an unknown class (so any object's class, rather than opening a specific class)... also define_method will work inside a method, whereas you cannot open a class within a method.
array = []
array.class.send(:define_method, :var) { #var }
array.class.send(:define_method, :var=) { |value| #var = value }
And here is an example of it's use... note that array2, a DIFFERENT array also has the methods, so if this is not what you want, you probably want singleton methods which I explained in another post.
irb(main):001:0> array = []
=> []
irb(main):002:0> array.class.send(:define_method, :var) { #var }
=> #<Proc:0x00007f289ccb62b0#(irb):2>
irb(main):003:0> array.class.send(:define_method, :var=) { |value| #var = value }
=> #<Proc:0x00007f289cc9fa88#(irb):3>
irb(main):004:0> array.var = 123
=> 123
irb(main):005:0> array.var
=> 123
irb(main):006:0> array2 = []
=> []
irb(main):007:0> array2.var = 321
=> 321
irb(main):008:0> array2.var
=> 321
irb(main):009:0> array.var
=> 123
Readonly, in response to your edit:
Edit: It looks like I need to clarify
that I'm looking for a metaprogramming
solution that allows me to modify the
class instance at runtime instead of
modifying the source code that
originally defined the class. A few of
the solutions explain how to declare
instance variables in the class
definitions, but that is not what I am
asking about. Sorry for the confusion.
I think you don't quite understand the concept of "open classes", which means you can open up a class at any time. For example:
class A
def hello
print "hello "
end
end
class A
def world
puts "world!"
end
end
a = A.new
a.hello
a.world
The above is perfectly valid Ruby code, and the 2 class definitions can be spread across multiple Ruby files. You could use the "define_method" method in the Module object to define a new method on a class instance, but it is equivalent to using open classes.
"Open classes" in Ruby means you can redefine ANY class at ANY point in time... which means add new methods, redefine existing methods, or whatever you want really. It sounds like the "open class" solution really is what you are looking for...
I wrote a gem for this some time ago. It's called "Flexible" and not available via rubygems, but was available via github until yesterday. I deleted it because it was useless for me.
You can do
class Foo
include Flexible
end
f = Foo.new
f.bar = 1
with it without getting any error. So you can set and get instance variables from an object on the fly.
If you are interessted... I could upload the source code to github again. It needs some modification to enable
f.bar?
#=> true
as method for asking the object if a instance variable "bar" is defined or not, but anything else is running.
Kind regards, musicmatze
It looks like all of the previous answers assume that you know what the name of the class that you want to tweak is when you are writing your code. Well, that isn't always true (at least, not for me). I might be iterating over a pile of classes that I want to bestow some variable on (say, to hold some metadata or something). In that case something like this will do the job,
# example classes that we want to tweak
class Foo;end
class Bar;end
klasses = [Foo, Bar]
# iterating over a collection of klasses
klasses.each do |klass|
# #class_eval gets it done
klass.class_eval do
attr_accessor :baz
end
end
# it works
f = Foo.new
f.baz # => nil
f.baz = 'it works' # => "it works"
b = Bar.new
b.baz # => nil
b.baz = 'it still works' # => "it still works"

Resources