how to monkey patch the puts method in ruby - ruby

I want to wrap the puts method with new lines before and after.
I searched the docs and didn't find where this function comes from. Ideas?

You could override the puts method like so:
def puts(object)
super('')
super(object)
super('')
end
If you really wanted to you could monkey patch the method like below. Although your changes would probably have unintended effects.
module Kernel
def puts(object)
# code
end
end
You can read about monkey patching in the Ruby docs here.

Related

Could someone help me understand what alias_method does in this code

I'm having trouble understanding the purpose of alias_method in this code
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
alias_method :configure_connection_without_interval, :configure_connection
define_method :configure_connection do
configure_connection_without_interval
execute('SET intervalstyle = iso_8601', 'SCHEMA')
end
end
What is the purpose of line 4 where they call configure_connection_without_interval -- doesn't that just call itself?
The code below works for me but I don't fully know what I'm doing and I'm worried it'll create bugs later
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
define_method :configure_connection do
execute('SET intervalstyle = iso_8601', 'SCHEMA')
end
end
alias_method operates immediately on that line before the method is redefined, preserving the old method under that given name.
That's a common Ruby technique to capture a version of a particular method and wrap it in another. When you're patching a class directly instead of subclassing you'll often be forced to do this.

ActiveRecord: override attribute writers by using a class method

I don't know how to correctly phrase the title, I think the best way to explain this issue is just with code samples.
My goal
I want to define a meta method like this (in Rails 5):
class Post < ApplicationRecord
override_this_attribute_writer :some_attribute
end
The override_this_attribute_writer follows a common pattern, it overrides the original writer by doing some filtering on top of it. I find this way of overriding very convenient and clear.
First approach
module MyCommonModule
extend ActiveSupport::Concern
module ClassMethods
def override_this_attribute_writer(attribute_name)
alias_method :"#{attribute_name}_old=", :"#{attribute_name}="
define_method :"#{attribute_name}=" do |a_value|
# Do my stuff
send(:"#{attribute_name}_old=", a_value)
end
end
end
When doing this, I was getting an exception at the call of alias_method, because, apparently, the method I was trying to copy didn't exist (yet).
Second approach
module MyCommonModule
extend ActiveSupport::Concern
module ClassMethods
def override_this_attribute_writer(attribute_name)
define_method :"#{attribute_name}=" do |a_value|
# Do my stuff
send(:write_attribute, attribute_name, a_value)
end
end
end
I was expecting this not to work: if, when running the meta method, ActiveRecord hasn't created the attribute writer yet, this means that it will do it later and override the method that I just defined.
But surprisingly it worked! So I put my hands inside ActiveRecord (5.1.5) to find out more.
Dig into ActiveRecord 5.1.5
I wanted to ensure that what I did was safe and it wasn't just working by accident: I looked into the definition of method writer, and put binding.pry around the method.
This is the result of the experiment:
For attributes that I did not override,
This line is called
Then the method is defined inside this module eval call
Finally, the newly created writer method is correctly called when performing object.attribute=
For attributes that I DID override,
My own method is defined before anything else (when the ActiveRecord writers aren't there yet
Then ActiveRecord calls the same line that handles writer creation, as in the previous example
The method gets (apparently) correctly created by ActiveRecord, since it passes again by this point
But now, surprisingly, when calling object.attribute= my own method is still called in place of the ActiveRecord one
So, this is what I don't understand: if ActiveRecord seems to be overriding my method but it doesn't, what prevents it from doing it?
My questions
What in the end I need to know is whether the fix I have done is actually a good practice (and robust) or it's at risk and it might break if in the future we do upgrades.
If you think that my fix is dangerous, would you be able to suggest a different way to achieve the same goal?
Calling super is even more idiomatic:
module MyCommonModule
extend ActiveSupport::Concern
module ClassMethods
def override_this_attribute_writer(attribute_name)
define_method :"#{attribute_name}=" do |value|
# do some stuff
super value
end
end
end
end

How to wrap methods with before and after in Ruby?

Is there a better way to achieve the following? It seems a little clunky to list the methods as symbols...
This code runs an init before and draw after for each of the 4 methods. The following code works, but is there a more readable or idiomatic way of doing it?
Class DrawMap
def draw_method_1
...
end
def draw_method_2
...
end
def draw_all
[:draw_method_1, :draw_method_2, :draw_method_3, :draw_method_4].each do |method|
init_draw
send method
#draw.draw
end
end
...
The Rails before and after filters would do the same thing, but this is not a Rails app.
Ruby 1.9.3
If you just want to make the code above a little cleaner, you could try this:
def draw_all
(1..4).each do |n|
init_draw
send "draw_method_#{n}"
#draw.draw
end
end
Otherwise, there is a pretty good SO question right here that would really help you out. It involves a little metaprogramming that basically redefines methods and wraps them with some additional code. In your case, you would wrap with init_draw and draw.

When do you use method aliasing?

Do you use the alias method in order to add more ways to call methods (like length and size) or is there another use for it?
The alias_method call is also useful for re-implementing something but preserving the original version. There's also alias_method_chain from Rails which makes that kind of thing even easier.
alias_method also comes in handy when you have a number of behaviors that are initially identical but might diverge in the future, where you can at least rough them in to start.
def handle_default_situation
nil
end
%w[ poll push foo ].each do |type|
alias_method :"handle_#{type}_situation", :handle_default_situation
end
Yes.
It is often used to preserve a handle to existing methods before overriding them. (contrived example)
Given a class like this:
class Foo
def do_something
puts "something"
end
end
You could see code that adds new behaviour like so:
class Foo
def do_something_with_logging
puts "started doing something"
do_something_without_logging # call original implementation
puts "stopped doing something"
end
alias_method :do_something_without_logging, :do_something
alias_method :do_something, :do_something_with_logging
end
(this is exactly how alias_method_chain works)
However, for this use case it't often more appropriate to use inheritance and modules to your advantage.
Still, alias_method is a useful tool to have, if you absolutely need to redefine behaviour in an existing class (or if you wanted to implement something like alias_method_chain)

How do you detect that monkey patching has occurred in Ruby?

How do you check that monkey patching has been done to a specific class in Ruby? If that is possible, is it also possible to get the previous implementation(s) of the attribute that's been patched?
There are the hooks method_added and method_undefined. Garry Dolley has written an Immutable module that prevents monkey patching.
I found this blog posting that touches on how to use method_added to track monkey patching. It's not too hard to extend it to track the methods that were patched.
http://hedonismbot.wordpress.com/2008/11/27/monkey-business-2/:
By using open classes, we can re-define method_added for instances of Class and do some custom stuff every time a method is defined for any class. In this example, we’re re-defining method_added so that it tracks where the method was last defined.
#!/usr/bin/env ruby
class Class
##method_history = {}
def self.method_history
return ##method_history
end
def method_added(method_name)
puts "#{method_name} added to #{self}"
##method_history[self] ||= {}
##method_history[self][method_name] = caller
end
def method_defined_in(method_name)
return ##method_history[self][method_name]
end
end

Resources