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.
Related
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
There has got to be a more efficient way to do this in Ruby. I have a list of methods that scrape the same things (title, price) across multiple sites but in slightly different ways based on the code in each store. For example:
def store1_get_title
def store1_get_price
def store2_get_title
def store2_get_price
def store3_get_title
def store3_get_price
When calling all of these functions, I would just like a generic call with say a "namespace" parameter to do invoke any of these methods without having to type out all of them, something like:
for get_all_stores().each do |store|
store::get_title
store::get_price
end
...which would invoke store1_get_title, store1_get_price, store2_get_title, store2_get_price like I want. Is there something like this or a better way to do this?
Hope that makes sense. Thanks for any input!
Edit: these tasks are in rake task code.
This is a perfect use for classes. If you find two stores with the same software powering them (maybe Yahoo commerce or EBay stores) you can make instances of the classes with different parameters.
class Amazon
def get_price; end
def get_title; end
end
class Ebay
def initialize seller; end
def get_price; end
def get_title; end
end
[Amazon.new, Ebay.new("seller1"), Ebay.new("seller2")] each do |store|
store.get_price
store.get_title
end
And you can do this in any other object-oriented language by defining a base class or interface that all of the stores implement/inherit.
I don't understand the logic of your application. Perhaps you should think about a class definition (see Ken Blooms answer).
Nevertheless you could try a dynamic call with send:
def store1_get_title
p __method__
end
def store1_get_price
p __method__
end
def store2_get_title
p __method__
end
def store2_get_price
p __method__
end
def store3_get_title
p __method__
end
def store3_get_price
p __method__
end
all_stores = ['store1', 'store2', 'store3']
all_stores.each do |store|
send("#{store}_get_title")
send("#{store}_get_price")
end
You didn't define what get_all_stores returns. In my example I used Strings. You could add some syntactical sugar and extend String (I don't recommend this)
class String
def get_title()
send("#{self}_get_title")
end
def get_price()
send("#{self}_get_price")
end
end
all_stores.each do |store|
store.get_title
store.get_price
end
One last remark. You wrote
for get_all_stores().each do |store|
each alone should be enough. for is not ruby-like and in combination with each it doen't look reasonable to me.
HI
I try to build some dynamic defined methods and chain some scope methods something like:
define_method "#{instance_name_method}" do
Kernel.const_get(model_name).___some_chaining methods basd on condition
end
One idea for that is something like:
method_action = model_name #ex Post
['latest', 'old', 'deleted','latest_deleted','archived'].each do |prefix|
method_action << ".deleted" if prefix.match('deleted')
method_action << ".latest" if prefix.match('latest')
method_action << ".old" if prefix.match('old')
define_method "#{prefix}_#{instance_name_method}" do
eval( method_action)
end
end
in post we have defiend scopes latest,old ...
Now we can call methods like:
Post.latest or Post.old_archived etc...
My questions are:
Is there a better approach for doing this? (similar to active record find but without method_missing) this is kind ugly...
How can I chain methods dynamically ?
I already know for send('method',var) but i don't know how to join those methods from strings based on condition...
Thanks
I'm sorry but it's difficult for me to understand exactly what you're asking. And I'm not sure you're using some terms correctly for instance what do you mean by "scope methods?" Do you mean a class method vs. an instance method? That would pertain to scope.
And when you say chain do you mean to call one method after the other? Like so?
f = Foo.new
puts f.method1(some_value).method2(some_other_value)
I will just comment that your not so dynamic part above could be written:
method_action << ".#{prefix}"
I don't see any actual chaining in your question so I'm not sure if you just mean to concatenate stings to build names dynamically. If you do in fact mean to chain methods you need to remember that you need to always return self at the end of a method you want to make chainable back to that class.
For instance:
class Foo
def method1(value)
puts "method1 called with #{value}"
self
end
def method2(value)
puts "method2 called with #{value}"
self
end
end
f = Foo.new
puts f.method1("Hello").method2("World").method1("I can").method2("do this").method2("all").method1("day!")
Would output:
method1 called with Hello
method2 called with World
method1 called with I can
method2 called with do this
method2 called with all
method1 called with day!
#<Foo:0x0000010084de50>
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. :-)