I'm interesting how to pass method with arguments in ruby. I need to implement something like command pattern with flexible function setting. Example => lambda functions in C#.
Ruby lambda functions are defined as follows:
a.lambda{ puts "Hello"}
a.call #=> Hello
a = lambda{|str| puts str }
a.call("Hello world !!!") #=> Hello world !!!
a = lambda{|*args| puts args.join(' ')}
a.call("Hello", "World") #=> Hello World
You could do the command pattern the way you do most things in Ruby: with a block.
class Soldier
def initialize(&block)
#command = block
end
def action
#command.call if #command
end
end
s = Soldier.new do #the block
line = "We are drill machines, drill machines feel no pain"
2.times{ puts line }
puts line.upcase
end
puts "Action:"
s.action
You can dynamically invoke methods along with their argument lists. Below is only one of the ways
class Foo
def foo(what)
puts what
end
end
Foo.new.send(:what, "something") # ==> "something"
Related
I've got a simple code for a class. It's:
def greeting
greeting = ARGV.shift
ARGV.each do |arg|
p "#{greeting}, #{arg}!"
end
end
My desire is for it to simply output "Hello, Charlie!" and "Hello, Sam!" based on the names stored in the array. However when I try to run the program in ruby it looks like I used a return statement.
address_bloc :> ls
address_bloc.rb greeting.rb ruby
argv_test.rb models spec
address_bloc :> greeting.rb Yo Tommy Bob Sally
-bash: greeting.rb: command not found
address_bloc :> ruby greeting.rb Yo Sam Sean Bill
address_bloc :> ruby greeting.rb Yo Sam Sean Bill
Remove the function definition logic and just include the contents at the top-level if this is all you want your script to do:
greeting = ARGV.shift
ARGV.each do |arg|
puts "#{greeting}, #{arg}!"
end
If you want to continue with defining a general-purpose function for greeting a list of names, you may also do so by calling greeting and passing it the contents of ARGV, as in the following:
def greeting(args)
greeting = args.shift
args.each do |arg|
puts "#{greeting}, #{arg}!"
end
end
greeting ARGV
But in general, it's a nicer function if its arguments are more meaningful, so consider doing something like this:
def greet_list(greeting, name_list)
name_list.each do |arg|
puts "#{greeting}, #{arg}!"
end
end
greet_list ARGV.shift, ARGV
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
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"
This is hard to explain as a question but here is a code fragment:
n = "Bob"
class A
end
def A.greet
puts "Hello #{n}"
end
A.greet
This piece of code does not work because n is only evaluated inside A.greet when it is called, rather than when I add the method.
Is there a way to pass the value of a local variable into A.greet?
What about if n was a function?
Use metaprogramming, specifically the define_singleton_method method. This allows you to use a block to define the method and so captures the current variables.
n = "Bob"
class A
end
A.define_singleton_method(:greet) do
puts "Hello #{n}"
end
A.greet
You can use a global ($n = "Bob")...
Although I prefer Nemo157's way you can also do this:
n = "Bob"
class A
end
#Class.instance_eval "method"
A.instance_eval "def greet; puts 'Hello #{n}' end"
#eval Class.method
eval "def A.greet2; puts 'Hi #{n}' end"
A.greet
A.greet2
define_method could be used to define methods:
define_method(:m) do |a|
end
which is equivalent to the following:
def m(a)
end
However, what is the equivalent form of the following using define_method:
def m(a=false)
end
Note that I'd need to be able to call m() without giving any argument.
This actually just works like you would expect in Ruby 1.9!
define_method :m do |a = false|
end
If you need 1.8 compatibility, but you don't necessarily need a closure to define your method with, consider using class_eval with a string argument and a regular call to def:
class_eval <<-EVAL
def #{"m"}(a = false)
end
EVAL
Otherwise follow the suggestion in the thread that philippe linked to. Example:
define_method :m do |*args|
a = args.first
end
This is currently not possible due to the yacc parser.
This thread on Ruby-forum proposes several solutions.
class A
define_method(:hello) do | name, *opt_greeting|
option = opt_greeting.first || Hash.new
greeting = option[:greeting] || "hello"
puts greeting+" "+name
end
end
a = A.new
a.hello "barbara"
a.hello "Mrs Jones", :greeting => "Good Morning"