How do I pass the parameter name in the following case..the name is being is evaluated before being passed to class_eval
class Foo
end
Foo.class_eval %Q{
def hello(name)
p "hello #{name}"
end
}
Sorry about not giving the entire scenario...
I just wanted to add a instance method dynamically to a class and that method should be able to take arguments...
the above code would not compile complaining that the name is not defined as local variable when executing in irb..
Thanks
The other answers are the "right" answer, but you could also just skip interpolating inside the p call:
Foo.class_eval %Q{
def hello(name)
p "hello \#{name}"
end
}
I thought you wanted to change the actual parameter name (possibly useful for completion or when using Pry on dynamic methods), here assuming it's in a global, but could also be passed into a method doing the class_eval:
Foo.class_eval %Q{
def hello(#{$argname})
p "hello \#{$argname}"
end
}
Really simple:
Foo.class_eval do
def hello(name)
p "hello #{name}"
end
end
Try passing a block to class_eval instead of an array (from this link):
class Foo
end
Foo.class_eval {
def hello(name)
p "hello #{name}"
end
}
You then can call the instance method hello in the usual fashion:
boo = Foo.new
boo.hello("you")
which produces:
>> boo.hello("you")
"hello you"
=> nil
class Foo
end
Foo.class_eval do
define_method :hello do |name|
p "hello #{name}"
end
end
Foo.new.hello("coool") # => "hello coool"
Related
I extended the String Class with an Method. Let's say 'foo'.
class String
def foo
puts "Hello World."
end
end
Why is it not possible to call either String.foo or String.method("foo")?
I am getting an NoMethodError when I try.
My ultimate Goal is to pass 'foo' to another Method. Something like bar(String.method('foo'))
Thanks in advance
foo is an instance method. You can use Module#instance_method to get the method, then bind it to a String object using .bind(string) and then call that using .call(args).
class String
def foo
puts "Hello #{self}."
end
end
p String.instance_method(:foo)
p String.instance_method(:foo).bind("World")
String.instance_method(:foo).bind("World").call
Output:
#<UnboundMethod: String#foo() a.rb:2>
#<Method: String#foo() a.rb:2>
Hello World.
If you truly want it to be a class method, you can define it so using self.methodname:
class String
def self.foo
puts "Hello"
end
end
String.foo # => Hello
You could also make it return a proc so it's passable elsewhere:
class String
def self.foo = -> { puts "Hello" }
end
String.foo # => #<Proc:0x0000000102a05ea8 (irb):14 (lambda)>
String.foo[] # => Hello
With arguments:
class String
def self.foo = ->(name){ puts "Hello #{name}" }
end
String.foo["Malte"] # => Hello Malte
You are defining an instance method, but tried to call a static method.
It works as expected when you call it the correct way.
class String
def foo
puts "Hello World."
end
end
String.new("test").foo # output: Hello World.
Your first question is, "If I execute
class String
def foo
puts "Hello World."
end
end
why is it not possible to call either String.foo or String.method("foo")?
When you execute
String.foo
#=> NoMethodError: undefined method 'foo' for String:Class
the error message1 is very specific: the class String does not have a method foo. That is consistent with the following.
String.methods.include?(:foo)
#=> false
What you have done is create an instance method foo of the class string. Let's take "cat", an instance of the class String:
"cat".methods.include?(:foo)
#=> true
"cat".foo
#=> "Hello World."
We can also look at
String.instance_methods
#=> [:unicode_normalize, :unicode_normalize!, :ascii_only?,
# :to_r,..., :foo, :count,..., :instance_exec]
or to find :foo more easily among String's 187 or so instance methods, we could examine
String.instance_methods.sort
#=> [:!, :!=,..., :extend, :foo, :force_encoding,..., :yield_self]
See Kernel#methods and Module#instance_methods
If you want to invoke foo on String you need to write
class String
def self.foo
puts "Hello World."
end
end
String.foo
#=> "Hello World."
When the method is constructed self equals String, so the above is the same as
class String
def String.foo
puts "Hello World."
end
end
The reason self is generally used in place of the literal class name is that it allows the class name to be changed without having to change the method definition. (We obviously wouldn't rename String, but we might want to change a user-defined class name (e.g., change Amount to Quantity.)
Actually, Ruby only has instance methods, so let's look at what def String.foo means in terms of an instance method.
We see that
String.class
#=> Class
meaning that String is an instance of Class. We therefore could write
class Class
def foo
puts "Hello World."
end
end
String.foo
#=> "Hello World."
but this has the undesirable effect of making this instance method Class available to all classes:
Array.foo
#=> "Hello World."
Hash.foo
#=> "Hello World."
Instead, we want is to limit the availability of foo to just one of Class' instances, String.
Every Ruby object has a special class called a "Singleton class" (one of several names used). I say "special" because it differs from regular classes in that it cannot be subclassed and one cannot create instances of it.
We can write the following to create a method foo that we can invoke on String.
string_single = String.singleton_class
#=> #<Class:String>
string_single.define_method(:foo) { puts "Hello World." }
#=> :foo
string_single.instance_methods.include?(:foo)
#=> true
String.foo
#=> Hello World.
See Module#define_method.
In fact,
def String.foo
puts "Hello World."
end
is just shorthand for the use of define_method above. Similarly, the familiar
class String
def foo
puts "Hello World."
end
end
#=> :foo
is just shorthand for
String.define_method(:foo) { puts "Hello World." }
#=> :foo
"cat".foo
#=> "Hello World."
The second part of your questions asks how to pass one singleton method to another. That's easy. We have already defined foo on String's singleton class so let's define another, bar, that calls foo.
String.singleton_class.define_method(:bar) { foo }
#=> :bar
String.bar
#=> Hello World.
1. Pay careful attention to error messages in their entirety. Often they will pinpoint the problem.
Is there a way to call multiple methods on the same instance without having to prefix each method call with the instance?
logger = Logger.new(STDERR)
table_name = ENV['ec2_information'].split('/')[1]
discovery = Ec2_ddb_discovery.new(logger:, table:)
discovery.scan_ddb_table
discovery.collect_stale_items.each { |item|
As max pleaner mentions in his comment, you can use instance_eval or if you need to pass arguments, you can use instance_exec.
These are typically used to create DSLs, but can be us like this as well.
class Foo
def bar_one
puts "hello from bar_one"
end
def bar_two
puts "hello from bar_two"
end
def bar_three(arg)
puts "hello from bar_three with #{arg}"
end
end
Foo.new.instance_eval do
bar_one
bar_two
bar_three("local_argument")
end
Foo.new.instance_exec("passed_argument") do |arg|
bar_one
bar_two
bar_three(arg)
end
My functions are:
def hello(str)
puts "hello #{str}"
end
def hello_scope(scope, &block)
# ???
end
I would like to temporarily augment a function within a block of my method.
In hello_scope, I simply want to prepend the scope string to the str before passing it to the original hello method. Here's an example:
hello 'world' #=> hello world
hello_scope "welcome!" do
hello 'bob' #=> welcome!hello bob
hello 'alice' #=> welcome!hello alice
end
I'm kind of a noob when it comes to this kind of thing in Ruby. Can someone help me solve this in an elegant way?
Edit:
If it makes things easier, it's OK if we pass the method in as an argument to the block, such as:
hello_scope "welcome!" do |h|
h "bob" #=> welcome!hello bob
h "alice" #=> welcome!hello alice
end
One way is to create a "evaluation context object" on which the block is going to be instance-eval'd. This object has to provide all the methods that are specific to the block. In the example below, I did not use the same name as I don't remember how to explicitly referring to the global method "hello" (to avoid infinite recursion). In a proper library, "hello" would be defined as a class method somewhere, so that would not be an issue.
For instance
def hello(str)
puts "hello #{str}"
end
class HelloScope
def h(str)
print scope
hello(str)
end
end
def hello_scope(scope, &block)
HelloScope.new(scope).instance_eval(&block)
end
Just modify your "hello" method to take into account current scope:
class Greeter
def initialize
#scope = nil
end
def hello(str)
puts "#{#scope}hello #{str}"
end
def with_scope(scope)
#scope = scope
yield
#scope = nil
end
end
Greeter.new.instance_eval do
hello 'world' #=> hello world
with_scope "welcome!" do
hello 'bob' #=> welcome!hello bob
hello 'alice' #=> welcome!hello alice
end
end
I know this works:
proc = Proc.new do
puts self.hi + ' world'
end
class Usa
def hi
"Hello!"
end
end
Usa.new.instance_eval &proc
However I want to pass arguments to proc, so I tried this which does not work:
proc = Proc.new do |greeting|
puts self.hi + greeting
end
class Usa
def hi
"Hello!"
end
end
Usa.new.instance_eval &proc, 'world' # does not work
Usa.new.instance_eval &proc('world') # does not work
Can anyone help me make it work?
Use instance_exec instead of instance_eval when you need to pass arguments.
proc = Proc.new do |greeting|
puts self.hi + greeting
end
class Usa
def hi
"Hello, "
end
end
Usa.new.instance_exec 'world!', &proc # => "Hello, world!"
Note: it's new to Ruby 1.8.7, so upgrade or require 'backports' if needed.
Is it possible to execute a proc within the context of another object?
I know that normally you'd do proc.call(foo), and then the block should define a parameter. I was wondering though whether I could get "self" to bind to foo so that it's not necessary to have a block parameter.
proc = Proc.new { self.hello }
class Foo
def hello
puts "Hello!"
end
end
foo = Foo.new
# How can proc be executed within the context of foo
# such that it outputs the string "Hello"?
proc.call
foo.instance_eval &proc
instance_eval can take a block instead of a string, and the & operator turns the proc into a block for use with the method call.
This is for ruby 1.9:
class MyCat
def initialize(start, &block)
#elsewhere = start
define_singleton_method(:run_elsewhere, block) if block_given?
end
end
MyCat.new('Hello'){ #elsewehere << ' world' }.run_elsewhere