How does refinement scope work? - ruby

This is essentially a snippet from Ruby Metaprogramming 2. In the section they gloss over this example but there isn't really an explanation.
module MyRefinement
refine MyClass do
def my_method
"refined"
end
end
end
class MyClass
def my_method
"original"
end
def another_method
my_method
end
end
using MyRefinement
obj = MyClass.new
puts obj.my_method #=> "refined"
puts obj.another_method #=> "original"
Why doesn't the refinement apply when you call my_method from another method?

It avoids "leaky" refinements, e.g., the refinement applies specifically to the method you refine.
http://yehudakatz.com/2010/11/30/ruby-2-0-refinements-in-practice/
Very near the bottom this functionality is explained; nutshell:
[the] refinement should not leak [...]. If it did, it would mean that any call into any method could leak a refinement into that method, which is the opposite of the purpose of the feature.

refine keyword use to Refinements of the class locally. It's mean we can monkey patch any method by refinement of the class.
In your case, the process of refine/redfined/monkey patch only active when the method get call directly. Also Refinements are lexical in scope. When control is transferred outside the scope the refinement is deactivated.
To get better insight, read the scope part of refinements from here: Refinements

Related

Ruby hook for method defined?

I've been googling around for this and haven't been able to find an answer, which makes me think the answer is no, but I figured I'd ask here in case anyone knows for sure.
Does Ruby have a hook for when methods are defined (ie on a module or class)?
If not, is anyone familiar enough with the implementation of the main object to know how exactly it copies methods to Object when they're defined at the top level?
Really curious about this. Thanks for any info :)
It does. Module#method_added https://ruby-doc.org/core-2.2.2/Module.html#method-i-method_added
module Thing
def self.method_added(method_name)
puts "Thing added #{method_name}"
end
def self.a_class_method; end
def do_something; end
end
class Person
def self.method_added(method_name)
puts "I added #{method_name}"
end
attr_accessor :name
end
Thing
Person.new
# Thing added do_something
# I added name
# I added name=
If not, is anyone familiar enough with the implementation of the main object to know how exactly it copies methods to Object when they're defined at the top level?
It doesn't "copy methods". The language specification simply says that methods defined at the top-level become methods of Object. This is exactly the same mechanism as the one that says that methods defined inside class Foo become methods of class Foo. The language spec says it, therefore the implementors implement it that way. main doesn't need to do anything.
If you want to get real technical, then this is about the default definee, which is the implicit scope in which methods get defined when you don't explicitly specify the definee (as in def foo.bar; end). Usually, the default definee is the self of the closest lexically enclosing class or module definition body, and when there is no lexically enclosing class or module definition, it is Object. But some reflective methods, such as instance_eval or class_eval etc. may or may not change it.

refinement using a previously defined refinement in the same file: possible?

Sadly, the below does not work. Time is not able to respond_to(:format_to_my_datetime). So my assumption is that refinements in the same file can't use each other. Instead where I define my ActiveSupport refinement, I would've needed within that file to use using TimeFormatter to refine Time in the context of that method.
But am I missing anything? Is there any way I can do this all in one file, besides just reimplementing format_to_my_datetime(timezone) on ActiveSupport?
module TimeFormatter
include MyFormats
refine String do
def format_to_my_name
self.gsub(MY_NAME_FORMAT,"")
end
end
refine Time do
def format_to_my_datetime(timezone)
self.getlocal(timezone).strftime(MY_DATETIME_FORMAT)
end
end
refine ActiveSupport::TimeWithZone do
def method_missing(method, *args)
if Time.respond_to?(method)
self.to_time.send(:method, *args)
end
end
end
end
Found my answer here: Why does `send` fail with Ruby 2.0 refinement?
Turns out that indirect method access is specifically revoked for Refinements, which is a huge bummer to me:
== Indirect method accesses
Any indirect method access such as Kernel#send, Kernel#method, and
Kernel#respond_to? shall not honor refinements in the caller context
during method lookup.
NOTE: This behavior will be changed in the future.
=end

In how many ways can methods be added to a ruby object?

When it comes to run time introspection and dynamic code generation I don't think ruby has any rivals except possibly for some lisp dialects. The other day I was doing some code exercise to explore ruby's dynamic facilities and I started to wonder about ways of adding methods to existing objects. Here are 3 ways I could think of:
obj = Object.new
# add a method directly
def obj.new_method
...
end
# add a method indirectly with the singleton class
class << obj
def new_method
...
end
end
# add a method by opening up the class
obj.class.class_eval do
def new_method
...
end
end
This is just the tip of the iceberg because I still haven't explored various combinations of instance_eval, module_eval and define_method. Is there an online/offline resource where I can find out more about such dynamic tricks?
Ruby Metaprogramming seems to be a good resource. (And, linked from there, The Book of Ruby.)
If obj has a superclass, you can add methods to obj from the superclass using define_method (API) as you mentioned. If you ever look at the Rails source code, you'll notice that they do this quite a bit.
Also while this isn't exactly what you're asking for, you can easily give the impression of creating an almost infinite number of methods dynamically by using method_missing:
def method_missing(name, *args)
string_name = name.to_s
return super unless string_name =~ /^expected_\w+/
# otherwise do something as if you have a method called expected_name
end
Adding that to your class will allow it to respond to any method call which looks like
#instance.expected_something
I like the book Metaprogramming Ruby which is published by the publishers of the pickaxe book.

How do you prefer to define class methods in Ruby?

John Nunemaker recently blogged about the various ways to define class methods in Ruby, giving these three alternatives:
# Way 1
class Foo
def self.bar
puts 'class method'
end
end
# Way 2
class Foo
class << self
def bar
puts 'class method'
end
end
end
# Way 3
class Foo; end
def Foo.bar
puts 'class method'
end
What's your preferred way to do this?
Do you prefer something other than those above?
If you use more than one way, under what circumstances do you use them?
I consistently use Way 1:
class Foo
def self.bar
puts 'class method'
end
end
It's not verbose, and it keeps the method in the same context of the class.
I generally prefer def self.foo for single methods, and class << self for long stretches of class methods. I feel it makes the distinction between the class method part and the instance method part of the class definition.
I prefer Way 1 as it isn't context sensitive. I dislike jumping into the middle of a file and then having to scroll up or down to see if the indentation means I'm in a class << self block or if it's just a nested module.
Agree with most of the users. I tend to use primarily the
# Way 1
class Foo
def self.bar
puts 'class method'
end
end
There are some small differences, if I recall correctly, that are shown on the Pragmatic Programmers Metaprogramming talks (which I recommend), which relate to how the class code is called and executed.
They were quite small, though and mostly things we won't have to deal with on a normal basis. Will see if I can check them out and post it.
I view << for adding a method as too unusual (though I happily use << with strings and IO).
I avoid Foo.bar because it means repeating yourself.
I use Way #3, but I think Way #1 is great also. It depends on your usage. If you want your code to be "cut/pastable" into other modules and classes, then Way #1 is better. I use Way #3 to actually make it more of pain to cut/paste code, b/c Ruby's mantra is "don't repeat yourself" so you shouldn't cut/paste code very often..

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