Ruby if syntax (Two Expressions) - ruby

Lets say you are using the if syntax for Ruby that goes like this:
if foo == 3: puts "foo"
elsif foo == 5: puts "foobar"
else puts "bar"
end
Is there a way to do it so Ruby executes two things in the if statement, say, like this:
if foo == 3
puts "foo"
foo = 1
elsif foo = ...
How can I make it so I can put two statements in using the first syntax?

if foo == 3: puts "foo"; puts "baz"
elsif foo == 5: puts "foobar"
else puts "bar"
end
However, I advise against it.

Ruby permits semicolons to separate statements on a single line, so you can do:
if foo == 3: puts "foo"; puts "bar"
elsif foo == 5: puts "foobar"
else puts "bar"
end
To be tidy, I would probably terminate both statements with ;:
if foo == 3: puts "foo"; puts "bar";
elsif foo == 5: puts "foobar"
else puts "bar"
end
Unless you have an excellent reason though, I wouldn't do this, due to its effect on readability. A normal if block with multiple statements is much easier to read.

I think case statements look far better than the if statements:
case foo
when 3
puts "foo"
when 5
puts "foobar"
else
puts "bar"
end
And this allows for multiple statements per condition.

Related

How can I use multiple RSpec contexts across a single example?

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

Ruby: Case using object

Is there a way to implicitly call methods on the object of a case statement?
IE:
class Foo
def bar
1
end
def baz
...
end
end
What I'd like to be able to do is something like this...
foo = Foo.new
case foo
when .bar==1 then "something"
when .bar==2 then "something else"
when .baz==3 then "another thing"
end
... where the "when" statements are evaluating the return of methods on the case object. Is some structure like this possible? I haven't been able to figure out the syntax if so...
FWIW, you don't need to pass an object to a case statement in 1.8.7 at all.
foo = Foo.new()
case
when foo.bar == this then that
when foo.baz == this then that
end
I was surprised as hegg.
http://www.skorks.com/2009/08/how-a-ruby-case-statement-works-and-what-you-can-do-with-it/
What case .. when does is it calls the method === on your when values, passing your foo object as the argument to the === method. So in this code:
case foo
when 1 then "something"
when 2 then "something else"
when 3 then "another thing"
end
It will try 1 === foo, then 2 === foo, then 3 === foo, until one of them returns a truthy value.
One way of making case .. when more powerful is using Procs as the when values. I'm not sure about earlier versions of Ruby, but in 1.9, proc === x is equivalent to proc.call(x). So you can write code like this:
case foo
when Proc.new { foo.bar == 1 } then "something"
when Proc.new { foo.bar == 2 } then "something else"
when Proc.new { foo.baz == 3 } then "another thing"
end
Note that we don't even have to pass foo into the Procs, since we already have access to it. I don't think this is a very good choice of control structure for this example, a simple chain of ifs would make more sense:
if foo.bar == 1
"something"
elsif foo.bar == 2
"something else"
elsif foo.baz == 3
"another thing"
end
For different scenario where you want to test truthy method value of an object
class Foo
def approved?
false
end
def pending?
true
end
end
foo = Foo.new
case foo
when :approved?.to_proc
puts 'Green'
when :pending?.to_proc
puts 'Amber'
else
puts 'Grey'
end
# This will output: "Amber"
It looks like you're wanting to change the default receiver. This is hacky, but you could do something like:
string = Foo.new.instance_eval do
if bar==1 then "something"
elsif bar==2 then "something else"
elsif baz==3 then "another thing"
end
end
That's a big, terrible code smell, though, if you're just doing it because you're lazy. If you're doing it because you're creating a DSL, that's something else.

Ruby blocks with method_missing

Note, this is a follow up to my question here.
I'm trying to parse the following Tcl code:
foo bar {
biz buzz
}
In Tcl, foo is the method name, bar is the argument, and the rest is a "block" to be processed by eval.
Now here is my current implementation to this:
def self.foo(name, &block)
puts "Foo --> #{name}"
if block
puts "block exists"
else
puts "block does not exist"
end
end
def self.method_missing(meth, *args, &block)
p meth
p block
meth.to_s &block
end
tcl = <<-TCL.gsub(/^\s+/, "").chop
foo bar {
biz buzz
}
TCL
instance_eval(tcl)
Which outputs the following:
:bar
#<Proc:0x9e39c80#(eval):1>
Foo --> bar
block does not exist
In this example, when the block is passed up to the foo method, it does not exist. Yet in method_missing it does exist (at least it appears to exist). What's going on here?
Note, I am aware of ruby's precedence of parentheses and realize this works:
foo (bar) {
biz buzz
}
However, I want to have the parentheses omitted. So is this possible in ruby (without lexical analysis)?
You can do (I marked the lines I changed):
def self.foo args # changed
name, block = *args # changed
puts "Foo --> #{name}"
if block
puts "block exists"
else
puts "block does not exist"
end
end
def self.method_missing(meth, *args, &block)
p meth
p block
return meth.to_s, block # changed
end
That way, block will exist.
This has nothing to do with method_missing. You simply can't omit parentheses when passing block along with some parameters. In your case, Ruby will try to call bar method with block as an argument, and the result of it all will be passed to foo method as a single argument.
You can try this yourself by simplifying the method call (all the metaprogramming just obscures the real problem in your case):
# make a method which would take anything
def a *args, &block
end
# try to call it both with argument and a block:
a 3 {
4
}
#=>SyntaxError: (irb):16: syntax error, unexpected '{', expecting $end
# from /usr/bin/irb:12:in `<main>'
So the best solution I've found is to just gsub the string before processing it.
tcl = <<-TCL.gsub(/^\s+/, "").chop.gsub('{', 'do').gsub('}', 'end')
foo bar {
biz buzz
}
TCL

How to break out from a ruby block?

Here is Bar#do_things:
class Bar
def do_things
Foo.some_method(x) do |x|
y = x.do_something
return y_is_bad if y.bad? # how do i tell it to stop and return do_things?
y.do_something_else
end
keep_doing_more_things
end
end
And here is Foo#some_method:
class Foo
def self.some_method(targets, &block)
targets.each do |target|
begin
r = yield(target)
rescue
failed << target
end
end
end
end
I thought about using raise, but I am trying to make it generic, so I don't want to put anything any specific in Foo.
Use the keyword next. If you do not want to continue to the next item, use break.
When next is used within a block, it causes the block to exit immediately, returning control to the iterator method, which may then begin a new iteration by invoking the block again:
f.each do |line| # Iterate over the lines in file f
next if line[0,1] == "#" # If this line is a comment, go to the next
puts eval(line)
end
When used in a block, break transfers control out of the block, out of the iterator that invoked the block, and to the first expression following the invocation of the iterator:
f.each do |line| # Iterate over the lines in file f
break if line == "quit\n" # If this break statement is executed...
puts eval(line)
end
puts "Good bye" # ...then control is transferred here
And finally, the usage of return in a block:
return always causes the enclosing method to return, regardless of how deeply nested within blocks it is (except in the case of lambdas):
def find(array, target)
array.each_with_index do |element,index|
return index if (element == target) # return from find
end
nil # If we didn't find the element, return nil
end
I wanted to just be able to break out of a block - sort of like a forward goto, not really related to a loop. In fact, I want to break of of a block that is in a loop without terminating the loop. To do that, I made the block a one-iteration loop:
for b in 1..2 do
puts b
begin
puts 'want this to run'
break
puts 'but not this'
end while false
puts 'also want this to run'
end
Hope this helps the next googler that lands here based on the subject line.
If you want your block to return a useful value (e.g. when using #map, #inject, etc.), next and break also accept an argument.
Consider the following:
def contrived_example(numbers)
numbers.inject(0) do |count, x|
if x % 3 == 0
count + 2
elsif x.odd?
count + 1
else
count
end
end
end
The equivalent using next:
def contrived_example(numbers)
numbers.inject(0) do |count, x|
next count if x.even?
next (count + 2) if x % 3 == 0
count + 1
end
end
Of course, you could always extract the logic needed into a method and call that from inside your block:
def contrived_example(numbers)
numbers.inject(0) { |count, x| count + extracted_logic(x) }
end
def extracted_logic(x)
return 0 if x.even?
return 2 if x % 3 == 0
1
end
use the keyword break instead of return
Perhaps you can use the built-in methods for finding particular items in an Array, instead of each-ing targets and doing everything by hand. A few examples:
class Array
def first_frog
detect {|i| i =~ /frog/ }
end
def last_frog
select {|i| i =~ /frog/ }.last
end
end
p ["dog", "cat", "godzilla", "dogfrog", "woot", "catfrog"].first_frog
# => "dogfrog"
p ["hats", "coats"].first_frog
# => nil
p ["houses", "frogcars", "bottles", "superfrogs"].last_frog
# => "superfrogs"
One example would be doing something like this:
class Bar
def do_things
Foo.some_method(x) do |i|
# only valid `targets` here, yay.
end
end
end
class Foo
def self.failed
#failed ||= []
end
def self.some_method(targets, &block)
targets.reject {|t| t.do_something.bad? }.each(&block)
end
end
next and break seem to do the correct thing in this simplified example!
class Bar
def self.do_things
Foo.some_method(1..10) do |x|
next if x == 2
break if x == 9
print "#{x} "
end
end
end
class Foo
def self.some_method(targets, &block)
targets.each do |target|
begin
r = yield(target)
rescue => x
puts "rescue #{x}"
end
end
end
end
Bar.do_things
output: 1 3 4 5 6 7 8
You have four ways to unwind the stack in 'non-exceptional' ways: next, break, return, and throw.
next will cause the block to return.
break will cause the method that yielded to the block to return.
return will cause the method where the block is defined to return.
throw will traverse up the stack until it finds a catch with a matching symbol, and cause that to return. This is much like a 'lightweight' exception for non-exceptional situations.
All of them can take a return value that will be returned by whatever they caused to return instead of the value their last expression which they would return normally.
Here are some examples:
def doSomething
puts "> doSomething"
yield
puts "< doSomething"
end
def withNext
puts "> withNext"
doSomething do
puts "> block"
puts "* NEXT! causes the block to return to doSomething"
next
puts "< block"
end
puts "< withNext"
end
def withBreak
puts "> withBreak"
doSomething do
puts "> block"
puts "* BREAK! causes doSomething to return to withBreak"
break
puts "< block"
end
puts "< withBreak"
end
def withReturn
puts "> withReturn"
doSomething do
puts "> block"
puts "* RETURN! causes withReturn to return"
return
puts "< block"
end
puts "< withReturn"
end
def withThrow
puts "> withThrow"
catch :label do
puts "> catch :label"
doSomething do
puts "> block 1"
doSomething do
puts "> block 2"
puts "* THROW! causes catch :label to return to withThrow"
throw :label
puts "< block 2"
end
puts "< block 1"
end
puts "< catch :label"
end
puts "< withThrow"
end
withNext
puts "* Done"
puts
withBreak
puts "* Done"
puts
withReturn
puts "* Done"
puts
withThrow
puts "* Done"
output:
> withNext
> doSomething
> block
* NEXT! causes the block to return to doSomething
< doSomething
< withNext
* Done
> withBreak
> doSomething
> block
* BREAK! causes doSomething to return to withBreak
< withBreak
* Done
> withReturn
> doSomething
> block
* RETURN! causes withReturn to return
* Done
> withThrow
> catch :label
> doSomething
> block 1
> doSomething
> block 2
* THROW! causes catch :label to return to withThrow
< withThrow
* Done
To break out from a ruby block simply use return keyword return if value.nil? next.
next terminates lambda, block, or proc it is in.
break terminates the method that yielded to the block or invoked the proc or lambda it is in.
Credit to: Ruby block return, break, next

Syntax for putting a block on a single line

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

Resources