Dynamic Method Naming - ruby

I want to be able to name methods dynamically (I would not leave it up to user input to do this, but as an example):
puts ""
foo = gets
def (whatever the user inputted for foo)
end
How can I do this?

You can do this using the send method to send a message to the class, using the parameter :define_method to tell it you are going to define a new method for that class.
For example, having a class Car
class Car
end
c = Car.new
A call to c.sound brings about the error
NoMethodError: undefined method `sound' for #<Car:0x29d9048>
But after defining the name of the method and sending it to the class:
input = "sound"
Car.send(:define_method, input) do
puts "vroom!"
end
The call to c.sound now brings the output
vroom!

The most commonly used approaches are: define_method, class_eval and instance_eval. Define method_missing method is also used a lot.
#An example of class_eval
class Foo
end
foo = gets.chomp
#suppose you input bar here
Foo.class_eval %Q{
def #{foo}
puts "This is #{foo} method you defined!"
end
}
Foo.new.bar
#output: This is the bar method you defined!
instance_eval is used in a similar way but defined on a instance of a class.
define_method is also similar:
#An example of define_method
klass = Class.new
foo = gets.chomp
#suppose you typed bar
klass.send(:define_method,foo) do
puts "This is #{foo} method you defined!"
end
klass.new.bar
#output: This is bar method you defined!
Search "Ruby Metaprogramming" and there are many tutorials out there.

Related

Determining method's visibility on the fly

I am writing a method that will define an instance method inside a class; something similar to attr_accessor:
class Foo
custom_method(:foo)
end
I have implemented that by adding custom_method function to the Module module, and defining the method with define_method, which works fine. But I cannot figure out how to take into account visibility attributes from the class. For example, in the following class
class Foo
custom_method(:foo)
private
custom_method(:bar)
end
the first generated method (foo) must be public, and the second one (bar) must be private. How do I do that? Or, how do I find the context in which my custom_method is called: private, public, or protected?
Thanks!
After experimenting with this for a bit, I'm completely baffled. Initially, I thought that Ruby took the default visibility (public, private, or protected) into account when you call Module#define_method. It turns out though that on Ruby versions <= 2.0, that's not the case:
class Foo
private
define_method :foo do
puts "Foo called!"
end
end
Foo.new.foo # Prints "Foo called!"
On Ruby 2.1+, it's even more confusing. Module#define_method seems to take default method visibility into account:
class Foo
private
define_method :foo do
puts "Foo called!"
end
end
Foo.new.foo # NoMethodError: private method `foo' called for #<Foo:0x8cb75ac>
But it only works when you are calling define_method from directly inside the class. Calling a method which then calls define_method doesn't work:
class Foo
def self.hello_on name
define_method name do
puts "Hello, #{name}!"
end
end
private
hello_on :foo
end
Foo.new.foo # Prints "Hello, foo!"
Dang it Ruby! Why?
Okay, this calls for desperate measures...
module DefaultMethodVisibilityAccessor
attr_reader :current_default_method_visibility
def public(*args)
#current_default_method_visibility = :public if args.empty?
super
end
def protected(*args)
#current_default_method_visibility = :protected if args.empty?
super
end
def private(*args)
#current_default_method_visibility = :private if args.empty?
super
end
end
class Module
prepend DefaultMethodVisibilityAccessor
end
module MethodDefiner
def hello_on name
define_method name do
puts "Hello, #{name}!"
end
case current_default_method_visibility
when :public
public name
when :protected
protected name
when :private
private name
end
end
end
Usage:
class Foo
extend MethodDefiner
hello_on :foo
private
hello_on :bar
end
Foo.new.foo # Prints "Hello, foo!"
Foo.new.bar # NoMethodError: private method `bar' called for #<Foo:0x8ec18fc>
There, fixed!
I think this is impossible, because the scope visibility level set by Module.private is managed at the C virtual machine level and not exposed to Ruby.
EDIT: and it's only available in the same syntactical scope that it is called, so when you call custom_method it loses the visibility level set inside the class declaration.
It's set in set_visibility(), and used in vm_define_method(), but I can't find any reference to the corresponding variable being available from Ruby.
I suggest using some kind of custom parameter to specify the visibility level of your methods.
You can use Module#private_method_defined? to verify if a method is defined as private

Ruby: NoMethodError, but why?

I was working on a simple Pi Generator while learning Ruby, but I kept getting NoMethodError on RubyMine 6.3.3, so I decided to make a new project and new class with as simple as possible, and I STILL get NoMethodError. Any reason?
class Methods
def hello (player)
print "Hello, " << player
end
hello ("Annie")
end
And the error I get is:
C:/Users/Annie the Eagle/Documents/Coding/Ruby/Learning Environment/methods.rb:5:in `<class:Methods>': undefined method `hello' for Methods:Class (NoMethodError)
You have defined an instance method and are trying to call it as a method of a class. Thus you need to make the method hello a class method, not an instance method of the class Methods.
class Methods
def self.hello(player)
print "Hello, " << player
end
hello("Annie")
end
Or, if you want to define it as instance method then call it as below :
class Methods
def hello(player)
print "Hello, " << player
end
end
Methods.new.hello("Annie")
You're trying to call an instance method as a class method.
Here's some code that illustrates the difference between the two in ruby:
class Person
# This is a class method - note it's prefixed by self
# (which in this context refers to the Person class)
def self.species
puts 'Human'
# Note: species is OK as a class method because it's the same
# for all instances of the person class - ie, 'Bob', 'Mary',
# 'Peggy-Sue', and whoever else, are ALL Human.
end
# The methods below aren't prefixed with self., and are
# therefore instance methods
# This is the construct, called automatically when
# a new object is created
def initialize(name)
# #name is an instance variable
#name = name
end
def say_hello
puts "Hello from #{#name}"
end
end
And now try it out, calling the methods...
# Call a class method...
# We're not referring to any one 'instance' of Person,
Person.species #=> 'Human'
# Create an instance
bob = Person.new('Bob')
# Call a method on the 'Bob' instance
bob.say_hello #=> 'Hello from Bob'
# Call a method on the Person class, going through the bob instance
bob.class.species #=> 'Human'
# Try to call the class method directly on the instance
bob.species #=> NoMethodError
# Try to call the instance method on the class
# (this is the error you are getting)
Person.say_hello #=> NoMethodError
You've created an instance method, but you're calling a class method. In order to call hello("Annie"), you have to make an instance of Methods. For instance:
class Methods
def self.hello(player)
print "Hello, " << player
end
end
my_method = Methods.new
my_method.hello("Annie")
This would output Hello, Annie
By defining a method with def method_name args you are defining a instance method that will be included in every object of that class, but not in the class itself.
On the other hand, by def self.method_name args you will get a class method that will be directly in the class, without the need of instanciate an object from it.
So If you have this:
Class Test
def self.bar
end
def foo
end
end
You can execute the instance method this way:
a = Test.new
a.foo
And as for the class one should be:
Test.foo

Call a function and method with the same name in Ruby

Forgive me for my english.
I am a php programmer and now i want to learn Ruby.
In php if you want to call function "foo" within a class, you simply call foo(), and if you want to call method "foo" you call this->foo().
The question is, is it possible to call function and method with the same name in Ruby?
For example:
def foo
puts "In foo function"
end
class A
def call_foo
foo
#How can i call foo function, not a method?
end
def foo
puts "In foo method"
end
end
a = A.new
a.call_foo #Prints "In foo method"
There is no such thing as a function in Ruby, only methods.
If you define a method at the top-level it is an instance method of Object.
If you define a class without a superclass, it's superclass is Object.
So, your A#foo simply overrides Object#foo. And if it overrides Object#foo, it should respect its contract. You should never need to call Object#foo on an A, if A#foo implements Object#foo's contract correctly (and it should, otherwise it would be a violation of the Liskov Substitution Principle). If you want to reuse Object#foo's implementation within A#foo, you can defer to the superclass implementation using super.
Note: what you want is possible using reflection, but the correct solution would be to fix your design:
def foo
puts "In foo function"
end
class A
def call_foo
self.class.superclass.public_instance_method(:foo).bind(self).()
end
def foo
puts "In foo method"
end
end
a = A.new
a.call_foo #Prints "In foo function"
The foo method outside of your class definition is bound to Object, which is an instance of Class. So to call your method you can do:
> Object.foo­
=> "In foo function"
But as it was pointed out before, you should rather declare this method in an appropiate class.
You can also declare this method as class method with:
class A
def self.foo
puts "In foo class method"
end
end
Now you can call it without creating an A instance:
> A.foo
=> puts "In foo class method"

Ruby: relationship between two methods with same name, one is 'super' to the other

In the the following tea timer code, there's a 'start' method inside SleepTimer, which calls 'notify.'
def start
sleep minutes * 60
notifier.notify("Tea is ready!")
end
If you look at the code below, you'll see that there's a notify method in the class StdioUi as well as a notify method in module UiWithBeep. The start method shown above calls the notify method in module UiWithBeep, which then, via 'super,' calls the notify method in class StdioUi. (The effect is that "BEEP!" is heard before "Tea is ready".) However, I don't understand why notifier.notify calls the notify method in module UiWithBeep rather than in class StdioUi.
First Question: how does it know to go to the one 'notify' over the other.
SecondQuestion And, although I understand super, what establishes the relationship so that notify in class StdioUi is 'super' to the other notify. Can you please explain
Tea Timer
class TeaClock
attr_accessor :timer
attr_accessor :ui
def initialize(minutes)
self.ui = StdioUi.new
self.timer = SleepTimer.new(minutes, ui)
init_plugins
end
def init_plugins
puts "init plugins"
#plugins = []
::Plugins.constants.each do |name|
#plugins << ::Plugins.const_get(name).new(self)
end
end
def start
timer.start
end
end
class StdioUi
def notify(text)
puts text
end
end
SleepTimer = Struct.new(:minutes, :notifier) do
def start
sleep minutes * 60
notifier.notify("Tea is ready!")
end
end
module Plugins
class Beep
def initialize(tea_clock)
tea_clock.ui.extend(UiWithBeep)
end
module UiWithBeep
def notify(*) #gets called by notifier.notify("Tea is ready")
puts "BEEP!"
super #calls notify in class StdioUi
end
end
end
end
t = TeaClock.new(0.01).start
The Book: I keep recommending this excellent book, Metaprogramming Ruby. I was consulting it while composing this answer.
So, here you extend an object with a module. In Ruby it's called Object Extension. In simple cases it all works as expected, like this one:
module Foo
def hello
puts "foo"
end
end
class Bar
end
bar = Bar.new
bar.extend Foo
bar.hello
# >> foo
Things get complicated when there are class' own methods involved. Here's a simplified version of your snippet that exhibits the same behaviour.
module Foo
def hello
puts "foo"
super
end
end
class Bar
def hello
puts 'bar'
end
end
bar = Bar.new
bar.extend Foo
bar.hello
# >> foo
# >> bar
When you call a method in ruby, the interpreter has to first find a method to call. This is called Method Lookup. Now, when you define an instance method, in reality it's a method on class object, not that instance. So, method lookup goes like this for first snippet:
1) bar instance => method hello not found here
2) Bar class => method hello found
When you extend an object, however, methods are injected into instance's eigenclass. It's a special "hidden" class, unique for each instance. And in reality method lookup goes through it first. First snippet again:
1) bar instance => method hello not found here
2) bar's eigenclass => method hello not found here
3) Bar class => method hello found
Now it should be clear why Foo.hello is called instead of Bar.hello: because it appears earlier in the method lookup process!
1) bar instance => method hello not found here
2) bar's eigenclass => method hello found
I may have made a few mistakes but this is roughly what happens.
each class has a property called ancestors that represents the inheritance chain. ruby walks through the list of inherited behavior and looks for matching methods. if it finds one, it calls it with the given parameters. if you call super it looks for the next match.
1.9.3-p194 :003 > String.class.ancestors
=> [Class, Mocha::ClassMethods, Module, NewRelic::Agent::MethodTracer::InstanceMethods, NewRelic::Agent::MethodTracer::InstanceMethods::TraceExecutionScoped, NewRelic::Agent::MethodTracer::ClassMethods, NewRelic::Agent::MethodTracer::ClassMethods::AddMethodTracer, Mocha::ModuleMethods, ActiveSupport::Dependencies::ModuleConstMissing, Object, FactoryGirl::Syntax::Vintage, Metaclass::ObjectMethods, Mocha::ObjectMethods, PP::ObjectMixin, JSON::Ext::Generator::GeneratorMethods::Object, ActiveSupport::Dependencies::Loadable, FriendlyId::ObjectUtils, Kernel, BasicObject]

Why does this Ruby method need to be a class level method?

Here's a classic fizzbuzz in Ruby:
class PrimeChecker
def print_em
1.upto 100 do |fizzbuzz|
if (fizzbuzz % 2) == 0 && (fizzbuzz % 5) == 0
puts "fizzbuzz: " + fizzbuzz.to_s
elsif (fizzbuzz % 5) == 0
puts "fizz: "+fizzbuzz.to_s
elsif (fizzbuzz % 2) == 0
puts 'buzz: ' + fizzbuzz.to_s
else
puts "-" + fizzbuzz.to_s
end
end
end
end
PrimeChecker.print_em
When I execute this, I get this error:
undefined method 'print_em'.
I change the method to self.print_em and it works. Does this mean it's a class method (I think so)? Was the method "not found" before because I can only call such methods in a class on actual instances of the object? If I wanted it to be a instance method what is the syntax for that? I'm trying to understand Ruby, classes and methods better.
Class methods are just that: called on the class. Whereas instance methods are called on an instance of that class. An example is more useful:
class Foo
def self.bar
"This is a class method!"
end
def bar
"This is an instance method!"
end
end
Foo.bar # => "This is a class method!"
foo = Foo.new # This creates "foo" to be a new instance of Foo
foo.bar # => "This is an instance method!"
Note that "class methods" in Ruby are actually methods on the class object's singleton. This is a rather difficult concept to explain, and you can read more about it if you'd like.
It's not a class method as written; you need to run it with an instance of PrimeChecker:
pc = PrimeChecker.new
pc.print_em
Using self. turns it into a class method, runnable with the syntax you show.
It doesn't need to be a class method, it's just that that's how you're trying to execute it.
Q: When I run ruby.rb I get undefined method 'print_em'. I change the method to self.print_em and it works. Does this mean it's a class method (I think so).
A: Yes. class Bar; ... def self.foo defines a class method foo for class Bar.
Q: Was the method "not found" before because I can only call such methods in a class on actual instances of the object?
A: You were first defining it as an instance method. In that case, it is only available to instances of the class.
Q: If I wanted it to be a instance method what is the syntax for that?
A: The way you had it originally: class Bar; def foo defines instance method foo for class Bar
Yes, you are completely correct. Currently, the way you define it, you can evaluate the method with:
PrimeChecker.new.print_em
The reason def self.my_awesome_method defines it on the class side is because the stuff inside
class MyAwesomeClass
end
is being executed in the context of MyAwesomeClass. It's all Ruby code, as you can see! This enables you to do things like this:
class MyAwesomeClass
puts "Hello from innards of #{self}!" #=> Hello from the innards of MyAwesomeClass!
end
Method definitions will also only work if you call them after the definition location, for example:
class MyAwesomeClass
my_awesome_method # produces a nasty error
def self.my_awesome_method
puts "Hello world"
end
my_awesome_method # executes just fine
end
Hope this clears some things up.

Resources