So I've got a Ruby method like this:
def something(variable, &block)
....
end
And I want to call it like this:
something 'hello' { do_it }
Except that isn't working for me, I'm getting a syntax error. If I do this instead, it works:
something 'hello' do
do_it
end
Except there I'm kind of missing the nice look of it being on one line.
I can see why this is happening, as it could look like it's a hash being passed as a second variable, but without a comma in between the variables...but I assume that there must be a way to deal with this that I'm missing. Is there?
You need to parenthesize your argument:
something('hello') { do_it }
That should work.
If you want "def something" to to accept a block, you need to yield data to that block. For example:
#to uppercase string
def something(my_input)
yield my_input.upcase
end
# => "HELLO WORLD"
something("hello world") { |i| puts i}
Uh, what about:
>> def something(arg1 , &block)
>> yield block
>> end
=> nil
>> def do_it
>> puts "Doing it!"
>> end
=> nil
>> something('hello') { do_it }
"Doing it!"
=> nil
Related
I have a set of contexts with given before clauses that I'd like to wrap around various examples. Here's my attempt:
# The "multi-context" wrapper
def with_foo_and_bar(&block)
before { p 'hello world' }
context 'foo' do
before { p 'baz' }
yield
end
context 'bar' do
before { p 'qux' }
yield
end
end
# The example
describe do
with_foo_and_bar do
it 'prints some stuff' do
# Example runs twice, but only 'hello world' is printed
end
end
end
In this spec, I would expect all before clauses to run and print out "baz" and "qux" once each and "hello world" twice, but only "hello world" is printed (twice, as expected). I feel like there's some problem with the yield ignoring the before blocks, but I'm not sure how to tweak the code to get what I want. I'd appreciate any suggestions!
I found the answer hinted at here:
https://www.jorgemanrubia.com/2010/01/16/using-macros-to-create-custom-example-groups-in-rspec/
The solution is to do the following:
def with_foo_and_bar(&block)
foo = context 'foo' do
before { p 'baz' }
end
foo.class_eval &block
bar = context 'bar' do
before { p 'qux' }
end
bar.class_eval &block
end
describe do
with_foo_and_bar do
it 'prints some stuff' do
# Prints 'baz' once and 'qux' once
end
end
end
I want to write a method which takes a block and if no block given it should use a default block. So I want to have something like this:
def say_hello(name, &block = ->(name) { puts "Hi, #{name}" })
# do something
end
But when I'm trying to do so I'm getting the syntax error.
I know I can deal with my problem using block_given?. But I am interested in first approach.
Am I missing something or this is just not possible?
Some answers suggest using block_given?, but since there is no possibility that a block would be nil or false when it is given, you can simply use ||=.
def say_hello(name, &block)
block ||= ->(name){puts "Hi, #{name}"}
# do something
end
You cannot declare a default block in the method definition, however you can use a little trick to use a custom block if none is given.
def say_hello(name)
block = block_given? ? Proc.new : ->(name) { puts "Hi, #{name}" }
block.call(name)
end
# This example uses a custom block
say_hello('weppos') { |name| puts "Hello, #{name}!" }
# => Hello, weppos!
# This example fallbacks to the default
say_hello('weppos')
# => Hi, weppos!
Let me explain it a little bit. Let's start from a more readable version.
def say_hello(name, &block)
block = block ? block : ->(name) { puts "Hi, #{name}" }
block.call(name)
end
You define the method to accept a block, then you check if block is defined. If not, you assign a custom block. Finally, you execute the block.
Let's enhance it a little bit. You can use block_given? to check if a block is passed
def say_hello(name, &block)
block = block_given? ? block : ->(name) { puts "Hi, #{name}" }
block.call(name)
end
This also allows you to skip the declaration of the block (&block) in the method definition.
def say_hello(name)
if block_given?
yield name
else
# This is rendundant, but it's for clarity
block = ->(name) { puts "Hi, #{name}" }
block.call(name)
end
end
But, at this point, you can also use the Proc.new to assign the block to a variable.
def say_hello(name)
block = block_given? ? Proc.new : ->(name) { puts "Hi, #{name}" }
block.call(name)
end
As a final word, I'm trying to understand when this approach would make sense. In most cases, you can probably wrap the code in a class or module and pass it as argument. It's probably better.
You can do it with regular lambdas.
def say_hello(name, block = ->(name) { puts "Hi, #{name}" })
block.call(name)
end
say_hello("Sergio")
say_hello("Ivan", ->(name) { puts "Where are you from, #{name}?"})
# >> Hi, Sergio
# >> Where are you from, Ivan?
Not sure if you can do this with blocks, though. A block is not an ordinary parameter.
No, you can't provide a default block value in a method definition. You can, however, achieve the equivalent behavior through the use of block_given? within the body of the method, as follows:
def say_hello(name, &block)
block = ->(name) { puts "Hi, #{name}" } unless block_given?
# do something
end
However, in this scenario you can't utilize yield to invoke any block that is passed in, since it won't be there in the default case. You'll have to invoke the block Proc object, as in block.(name).
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"
What is difference in below usage
a = proc { puts 'hii' }
def abc(&a)
a.call
end
abc(&a)
def xyz(c)
c.call
end
xyz(a)
In below implementation more than one blocks can be passed as arguments -
def pqr(c, &t)
c.call
yield
xyz(c)
abc(&t)
end
pqr(a) { puts 'block to method'}
In the first of the two, the &a parameter will also capture a block passed like this:
abc {puts "Hello world"}
This is the same as:
xyz(proc {puts "Hello world"})
The other of the two only allows the last of the two.
I can't figure out how to assign a function call to a ruby hash.
What I want to do is to assign a function to a hash key, and later
call this function using the classic hash lookout syntax.
def Foo()
puts "bar"
end
puts "Assigning"
test = { "foo" => Foo() }
puts "Executing"
test["foo"]
This code fails, function Foo is called after puts "Assign", during hash creation, and nothing happens after puts "Executing"
def Foo()
puts "bar"
end
puts "Assigning"
test = { "foo" => Foo }
puts "Executing"
test["foo"]
with this code I receive an uninitialized constant Foo (NameError).
Finally with
def Foo()
puts "bar"
end
puts "Assigning"
test = { "foo" => :Foo }
puts "Executing"
test["foo"]
I get not outputs.
Any suggestions?
Thanks to all for answres and suggestions.
What I'm going to do is to test
if a hash based approach to call function is faster than
an equivalent code based on if / case statements.
funcs["foo"].call
fatser than
if func_name == "foo" then
Foo()
elsif ...
...
end
or
case func_name
when "foo"
Foo()
when ...
...
end
Obviously for a big number of functions (~150) and hundreds of
calling cycles
you could use lambda's instead of methods. Two options here:
hash = {:foo => lambda { puts 'bar } }
hash[:foo].call
the second (more complicated) is this:
irb(main):001:0> class Hash
irb(main):002:1> alias :orig_anc :'[]'
irb(main):003:1>
irb(main):004:1* def [](key)
irb(main):005:2> if orig_anc(key).is_a? Proc
irb(main):006:3> orig_anc(key).call
irb(main):007:3> else
irb(main):008:3* orig_anc(key)
irb(main):009:3> end
irb(main):010:2> end
irb(main):011:1> end
=> nil
irb(main):012:0> h = {:hello => 'world', :foo => lambda { puts 'bar' }}
=> {:hello=>"world", :foo=>#<Proc:0x843224c#(irb):12 (lambda)>}
irb(main):013:0> h[:hello]
=> "world"
irb(main):014:0> h[:foo]
bar
=> nil
irb(main):015:0>
The second one just allows you to skip using 'call' method
There is no easy possibility to make your function execute simply by retrieving the hash key withput overriding Hash's [] method, as Vlad pointed out, i.e.
def foo
puts "hi"
end
... # magic
test["foo"] # outputs hi
won't work. What you can do, though, is assign the method reference using Object#method and then invoke it using call:
def foo
puts "hi"
end
test = { "foo" => method(:foo) }
test["foo"].call # => hi
First things first, start method names with lower case letters. Then, moving to first example, Ruby is eager so test = { "foo" => Foo() } Foo is called.
In the second example, variables starting with an uppercase letter are considered constants, so Ruby looks for one and do not bother looking for a method. Take into account that if foo where a method then foo would be called as in the first example.
Third example: test["foo"] returns :Foo, a symbol. Nowhere in your code Foo() is invoked.
*Suggestions
1.- Take a look at identifiers nomenclature in Ruby.
2.- You may took a look at lambda, Proc and Object#method
3.- If you have some money to spare and don't mind buying a pair of books, give Programming Ruby and Metaprogramming in Ruby a chance (both can be bought in PragProg.
You could try using the hash constructor with a block, so
def foo
puts "hi"
end
test = Hash.new do |hash, key|
send(key) if respond_to?(key)
end
test["foo"] # prints "hi", returns nil