As a Ruby newbie, it's confusing to me why it would ever be beneficial to yield self within a method definition. I've come across this functionality in a number of tutorials as being something that is helpful -- it makes perfect sense to me how this works, but I don't understand why you'd ever use it.
Let's say I have the following code:
class Dog
attr_accessor :breed
def initialize
#breed = "Westie"
end
def bark
puts "Woof!"
yield self if block_given?
end
end
fido = Dog.new
fido.bark do |d|
puts "Bark, bark, bark!"
puts d.breed
end
So yeah, via yield self, I now have access to the instance of the Dog class within the block that I am yielding too.
But, even if I don't yield self, I would still have access to that instance, right? In other words, wouldn't the code below work exactly the same as the code above?
class Dog
attr_accessor :breed
def initialize
#breed = "Westie"
end
def bark
puts "Woof!"
yield
end
end
fido = Dog.new
fido.bark do
puts "Bark, bark, bark!"
puts fido.breed
end
Note that in the second code sample, I'm not calling yield self.
I'm clearly missing the utility here.
The second example works because of the specific circumstances where you have a local variable referring to the the Dog. As an alternative consider what happens if using an anonymous instance:
Dog.new.bark do
puts "Bark, bark, bark!"
# what goes here?? puts ????.breed
end
or maybe you want to declare your block somewhere else and pass it in e.g.
loud = lambda { |dog| puts "A #{dog.breed} doing some LOUD YAPPING" }
puts d.bark(&loud)
so essentially setting up with yield self gives flexibility in how your code can be used.
Your examples are pretty simple, and there's no obvious use for yield self in them.
There are cases when it is useful though, the same way that tap is useful: It allows you to define and use a value without introducing a variable in the local scope.
A (admittedly very contrived) example would be:
Dog.new.bark do |d|
puts d.breed
end
Related
Define a class as follows. I want to call one_method dynamically. By default, wow.one_method calls the first one_method. If I want to change the behavior of the method, just call redefine.
I can implement the method as a function type property, but that is not what I want.
If I use the following code directly, it would report errors. Could you modify it slightly.
class Wow
def one_method
puts "hello Ruby"
end
def redefine(what="none")
define_method :one_method do
puts what
end
end
end
wow = Wow.new
wow.redefine("Whatever I want.")
You can achieve that via class_eval or instance_eval:
class Wow
def one_method
puts "hello Ruby"
end
def redefine(what="none")
self.class.class_eval do
define_method :one_method do
puts what
end
end
end
end
wow = Wow.new
wow.one_method #=> hello Ruby
wow.redefine("Whatever I want.")
wow.one_method #=> Whatever I want.
Reason is that define_method defines instance method on the receiver and is a class's instance method so you'll have to call it on the eigen class of the object that you want to redefine a method on.
I would recommend achieving your goal in a more canonical way, just redefine the method on the instance itself:
class Wow
def one
:one
end
end
w = Wow.new
w.one
#=> :one
def w.one
:two
end
w.one
#=> :two
Drawbacks are:
your methods lookup table caches will be dropped
the code is becoming more obscure and hard to debug
Alternatives:
I don't know your real problem, but for your particular question it is better to parameterize your one_method method just to receive an argument for puts. Also, you can pass a block, so you will receive more grained control over the behavior.
I would like to access a class' name in its superclass MySuperclass' self.inherited method. It works fine for concrete classes as defined by class Foo < MySuperclass; end but it fails when using anonymous classes. I tend to avoid creating (class-)constants in tests; I would like it to work with anonymous classes.
Given the following code:
class MySuperclass
def self.inherited(subclass)
super
# work with subclass' name
end
end
klass = Class.new(MySuperclass) do
def self.name
'FooBar'
end
end
klass#name will still be nil when MySuperclass.inherited is called as that will be before Class.new yields to its block and defines its methods.
I understand a class gets its name when it's assigned to a constant, but is there a way to set Class#name "early" without creating a constant?
I prepared a more verbose code example with failing tests to illustrate what's expected.
Probably #yield has taken place after the ::inherited is called, I saw the similar behaviour with class definition. However, you can avoid it by using ::klass singleton method instead of ::inherited callback.
def self.klass
#klass ||= (self.name || self.to_s).gsub(/Builder\z/, '')
end
I am trying to understand the benefit of being able to refer to an anonymous class by a name you have assigned to it after it has been created. I thought I might be able to move the conversation along by providing some code that you could look at and then tell us what you'd like to do differently:
class MySuperclass
def self.inherited(subclass)
# Create a class method for the subclass
subclass.instance_eval do
def sub_class() puts "sub_class here" end
end
# Create an instance method for the subclass
subclass.class_eval do
def sub_instance() puts "sub_instance here" end
end
end
end
klass = Class.new(MySuperclass) do
def self.name=(name)
#name = Object.const_set(name, self)
end
def self.name
#name
end
end
klass.sub_class #=> "sub_class here"
klass.new.sub_instance #=> "sub_instance here"
klass.name = 'Fido' #=> "Fido"
kn = klass.name #=> Fido
kn.sub_class #=> "sub_class here"
kn.new.sub_instance #=> "sub_instance here"
klass.name = 'Woof' #=> "Woof"
kn = klass.name #=> Fido (cannot change)
There is no way in pure Ruby to set a class name without assigning it to a constant.
If you're using MRI and want to write yourself a very small C extension, it would look something like this:
VALUE
force_class_name (VALUE klass, VALUE symbol_name)
{
rb_name_class(klass, SYM2ID(symbol_name));
return klass;
}
void
Init_my_extension ()
{
rb_define_method(rb_cClass, "force_class_name", force_class_name, 1);
}
This is a very heavy approach to the problem. Even if it works it won't be guaranteed to work across various versions of ruby, since it relies on the non-API C function rb_name_class. I'm also not sure what the behavior will be once Ruby gets around to running its own class-naming hooks afterward.
The code snippet for your use case would look like this:
require 'my_extension'
class MySuperclass
def self.inherited(subclass)
super
subclass.force_class_name(:FooBar)
# work with subclass' name
end
end
EDIT: To be clear. This is a question about how to do something with meta programming. It's not about memoizing. Clearly there are better ways to memoize. The relevant methods have "memoize" in them just to illustrate their purpose.
I'm just toying around with meta programming, so please don't answer use a
#foo instance variable.
I have the following that tries to memoize both an instance and a class method
by overwriting the method definition from the running method..
class Obj
class << self
def meta_me; self; end
def class_memoize
puts "hard core calculating ..."
abc = "huge calculation result"
raise "broken here with infinite loop"
define_class_method "class_memoize" do
puts abc
abc
end
class_memoize
end
def define_class_method name, &blk
meta_me.instance_eval do
define_method name, &blk
end
end
end
def instance_memoize
puts "hard core calculating ..."
abc = "huge calculation result"
self.class.meta_me.send :define_method, :instance_memoize do
puts abc
abc
end
instance_memoize
end
end
o = Obj.new
o.instance_memoize
# hard core calculating ...
# huge calculation result
o.instance_memoize
# huge calculation result
The instance version works, but the class version does not.
I've left in an attempt at the class version for reference.
Ethics aside, It's much easier than you think. Your main issue is you're using the wrong thing for your meta_me method. Try this:
class Object
def metaclass
class<<self;self;end
end
end
This is a fairly common monkeypatch when doing metaprogramming in Ruby. Now it's easy to reimplement a method, dynamically:
class Obj
def self.class_memoize
puts "calculating..."
abc = "result"
metaclass.send(:define_method, :class_memoize) do
puts abc
abc
end
class_memoize
end
def instance_memoize
puts "calculating..."
abc = "result"
metaclass.send(:define_method, :instance_memoize) do
puts abc
abc
end
instance_memoize
end
end
As you can see, once you have the metaclass object, redefining the methods done the same way whether it's a class method or an instance method. Note that if you don't want to sully the whole namespace with a metaclass method, it's probably better just to use the class<<self;self;end idiom whenever you want to refer to it. You can call methods directly on it, like this:
(class<<self;self;end).send(:define_method, :foo){|bar| bar*23}
Note that the parens aren't truly needed, it just helps to contain the messiness that is the bare metaclass reference :) Hope this helps.
I am having a bit trouble to understand when "super" can be called and when not. In the below example the super method leads to a no superclass error.
class Bacterium
def eats
puts "Nam"
end
end
class Bacterium
def eats
super # -> no superclass error
puts "Yam"
end
end
b = Bacterium.new
b.eats
But this works:
class Fixnum
def times
super # -> works
puts "done"
end
end
5.times { |i| puts i.to_s }
Is 5 not just also an instance of Fixnum. And am I not redefining an existing method like in the Bacterium example above?
No, not really. Fixnum inherits from Integer class, and you are in fact overriding Integer#times, so super works, as it calls implementation from the parent.
In order to achieve something similar when monkeypatching, you should alias method before redefining it, and there call it by alias.
class Bacterium
alias_method :eats_original, :eats
def eats
eats_original # -> "Nam"
puts "Yam"
end
end
Class reopening is not a form of inheritance and super is of no use there.
Just as Mladen said, and you can check that with Class#superclass:
irb> Fixnum.superclass
=> Integer
And does Integer implement #times?:
irb> Integer.instance_methods.grep /times/
=> [:times]
Yes it does.
So, in a simplified way, we can say, that super invokes the method you are in of a superclass. In your case the superclass of a Bacterium is Object, which doesn't implement #eats.
I said this is very simplified, because look at this example:
module One
def hi
" World" << super()
end
end
module Two
def hi
"Hello" << super()
end
end
class SayHi
def hi
"!!!"
end
end
h = SayHi.new
h.extend(One)
h.extend(Two)
puts h.hi
#=> Hello World!!
Don't take to serious what I wrote here, it is actually tip of the iceberg of the Ruby object model, which is important to understand (I am still learning it) - then you will get most, or all of those concepts.
Use some Google-fu for "Ruby object model"...
EDIT: I slightly changed the spec, to better match what I imagined this to do.
Well, I don't really want to fake C# attributes, I want to one-up-them and support AOP as well.
Given the program:
class Object
def Object.profile
# magic code here
end
end
class Foo
# This is the fake attribute, it profiles a single method.
profile
def bar(b)
puts b
end
def barbar(b)
puts(b)
end
comment("this really should be fixed")
def snafu(b)
end
end
Foo.new.bar("test")
Foo.new.barbar("test")
puts Foo.get_comment(:snafu)
Desired output:
Foo.bar was called with param: b = "test"
test
Foo.bar call finished, duration was 1ms
test
This really should be fixed
Is there any way to achieve this?
I have a somewhat different approach:
class Object
def self.profile(method_name)
return_value = nil
time = Benchmark.measure do
return_value = yield
end
puts "#{method_name} finished in #{time.real}"
return_value
end
end
require "benchmark"
module Profiler
def method_added(name)
profile_method(name) if #method_profiled
super
end
def profile_method(method_name)
#method_profiled = nil
alias_method "unprofiled_#{method_name}", method_name
class_eval <<-ruby_eval
def #{method_name}(*args, &blk)
name = "\#{self.class}##{method_name}"
msg = "\#{name} was called with \#{args.inspect}"
msg << " and a block" if block_given?
puts msg
Object.profile(name) { unprofiled_#{method_name}(*args, &blk) }
end
ruby_eval
end
def profile
#method_profiled = true
end
end
module Comment
def method_added(name)
comment_method(name) if #method_commented
super
end
def comment_method(method_name)
comment = #method_commented
#method_commented = nil
alias_method "uncommented_#{method_name}", method_name
class_eval <<-ruby_eval
def #{method_name}(*args, &blk)
puts #{comment.inspect}
uncommented_#{method_name}(*args, &blk)
end
ruby_eval
end
def comment(text)
#method_commented = text
end
end
class Foo
extend Profiler
extend Comment
# This is the fake attribute, it profiles a single method.
profile
def bar(b)
puts b
end
def barbar(b)
puts(b)
end
comment("this really should be fixed")
def snafu(b)
end
end
A few points about this solution:
I provided the additional methods via modules which could be extended into new classes as needed. This avoids polluting the global namespace for all modules.
I avoided using alias_method, since module includes allow AOP-style extensions (in this case, for method_added) without the need for aliasing.
I chose to use class_eval rather than define_method to define the new method in order to be able to support methods that take blocks. This also necessitated the use of alias_method.
Because I chose to support blocks, I also added a bit of text to the output in case the method takes a block.
There are ways to get the actual parameter names, which would be closer to your original output, but they don't really fit in a response here. You can check out merb-action-args, where we wrote some code that required getting the actual parameter names. It works in JRuby, Ruby 1.8.x, Ruby 1.9.1 (with a gem), and Ruby 1.9 trunk (natively).
The basic technique here is to store a class instance variable when profile or comment is called, which is then applied when a method is added. As in the previous solution, the method_added hook is used to track when the new method is added, but instead of removing the hook each time, the hook checks for an instance variable. The instance variable is removed after the AOP is applied, so it only applies once. If this same technique was used multiple time, it could be further abstracted.
In general, I tried to stick as close to your "spec" as possible, which is why I included the Object.profile snippet instead of implementing it inline.
Great question. This is my quick attempt at an implementation (I did not try to optimise the code). I took the liberty of adding the profile method to the
Module class. In this way it will be available in every class and module definition. It would be even better
to extract it into a module and mix it into the class Module whenever you need it.
I also didn't know if the point was to make the profile method behave like Ruby's public/protected/private keywords,
but I implemented it like that anyway. All methods defined after calling profile are profiled, until noprofile is called.
class Module
def profile
require "benchmark"
#profiled_methods ||= []
class << self
# Save any original method_added callback.
alias_method :__unprofiling_method_added, :method_added
# Create new callback.
def method_added(method)
# Possible infinite loop if we do not check if we already replaced this method.
unless #profiled_methods.include?(method)
#profiled_methods << method
unbound_method = instance_method(method)
define_method(method) do |*args|
puts "#{self.class}##{method} was called with params #{args.join(", ")}"
bench = Benchmark.measure do
unbound_method.bind(self).call(*args)
end
puts "#{self.class}##{method} finished in %.5fs" % bench.real
end
# Call the original callback too.
__unprofiling_method_added(method)
end
end
end
end
def noprofile # What's the opposite of profile?
class << self
# Remove profiling callback and restore previous one.
alias_method :method_added, :__unprofiling_method_added
end
end
end
You can now use it as follows:
class Foo
def self.method_added(method) # This still works.
puts "Method '#{method}' has been added to '#{self}'."
end
profile
def foo(arg1, arg2, arg3 = nil)
puts "> body of foo"
sleep 1
end
def bar(arg)
puts "> body of bar"
end
noprofile
def baz(arg)
puts "> body of baz"
end
end
Call the methods as you would normally:
foo = Foo.new
foo.foo(1, 2, 3)
foo.bar(2)
foo.baz(3)
And get benchmarked output (and the result of the original method_added callback just to show that it still works):
Method 'foo' has been added to 'Foo'.
Method 'bar' has been added to 'Foo'.
Method 'baz' has been added to 'Foo'.
Foo#foo was called with params 1, 2, 3
> body of foo
Foo#foo finished in 1.00018s
Foo#bar was called with params 2
> body of bar
Foo#bar finished in 0.00016s
> body of baz
One thing to note is that it is impossible to dynamically get the name of the arguments with Ruby meta-programming.
You'd have to parse the original Ruby file, which is certainly possible but a little more complex. See the parse_tree and ruby_parser
gems for details.
A fun improvement would be to be able to define this kind of behaviour with a class method in the Module class. It would be cool to be able to do something like:
class Module
method_wrapper :profile do |*arguments|
# Do something before calling method.
yield *arguments # Call original method.
# Do something afterwards.
end
end
I'll leave this meta-meta-programming exercise for another time. :-)