I am using Ruby on Rails 3 and I would like to use a case statement that even after matching a when statement can continue to checks other when statement until the last else.
For example
case var
when '1'
if var2 == ...
...
else
...
puts "Don't make nothig but continue to check!"
# Here I would like to continue to check if a 'when' statement will match 'var' until the 'else' case
end
when '2'
...
...
else
put "Yeee!"
end
Is it possible in Ruby? If so, how?
Most of the code I see coming from ruby is done with if elsif else but you can mimic switch logical expressions similar to other languages like this:
case var
when 1
dosomething
when 2..3
doSomethingElse
end
case
when var == 1
doSomething
when var < 12
doSomethingElse
end
This came from this SO Question. Like I said this is usually done with if elsif else in ruby such as:
if my_number == "1"
#do stuff when equals 1
elsif my_number == "e"
#same thing here
else
#default, no case found
end
Ruby doesn't have any sort of fall-through for case.
One alternative be a series of if statements using the === method, which is what case uses internally to compare the items.
has_matched? = false
if '2' === var
has_matched? = true
# stuff
end
if '3' === var
has_matched? = true
# other stuff
end
if something_else === var
has_matched? = true
# presumably some even more exciting stuff
end
if !has_matched?
# more stuff
end
This has two glaring issues.
It isn't very DRY: has_matched? = true is littered all over the place.
You always need to remember to place var on the right-hand-side of ===, because that's what case does behind the scenes.
You could create your own class with a matches? method that encapsulates this functionality. It could have a constructor that takes the value you'll be matching against (in this case, var), and it could have an else_do method that only executes its block if its internal #has_matched? instance variable is still false.
Edit:
The === method can mean anything you want it to mean. Generally, it's a more "forgiving" way to test equivalency between two objects. Here's an example from this this page:
class String
def ===(other_str)
self.strip[0, other_str.length].downcase == other_str.downcase
end
end
class Array
def ===(str)
self.any? {|elem| elem.include?(str)}
end
end
class Fixnum
def ===(str)
self == str.to_i
end
end
Essentially, when Ruby encounters case var, it will call === on the objects against which you are comparing var.
Related
I was trying to DRY up a Rails controller by extracting a method that includes a guard clause to return prematurely from the controller method in the event of an error. I thought this may be possible using a to_proc, like this pure Ruby snippet:
def foo(string)
processed = method(:breaker).to_proc.call(string)
puts "This step should not be executed in the event of an error"
processed
end
def breaker(string)
begin
string.upcase!
rescue
puts "Well you messed that up, didn't you?"
return
end
string
end
My thinking was that having called to_proc on the breaker method, calling the early return statement in the rescue clause should escape the execution of foo. However, it didn't work:
2.4.0 :033 > foo('bar')
This step should not be executed in the event of an error
=> "BAR"
2.4.0 :034 > foo(2)
Well you messed that up, didn't you?
This step should not be executed in the event of an error
=> nil
Can anyone please
Explain why this doesn't work
Suggest a way of achieving this effect?
Thanks in advance.
EDIT: as people are wondering why the hell I would want to do this, the context is that I'm trying to DRY up the create and update methods in a Rails controller. (I'm trying to be agressive about it as both methods are about 60 LoC. Yuck.) Both methods feature a block like this:
some_var = nil
if (some complicated condition)
# do some stuff
some_var = computed_value
elsif (some marginally less complicated condition)
#error_msg = 'This message is the same in both actions.'
render partial: "show_user_the_error" and return
# rest of controller actions ...
Hence, I wanted to extract this as a block, including the premature return from the controller action. I thought this might be achievable using a Proc, and when that didn't work I wanted to understand why (which I now do thanks to Marek Lipa).
What about
def foo(string)
processed = breaker(string)
puts "This step should not be executed in the event of an error"
processed
rescue ArgumentError
end
def breaker(string)
begin
string.upcase!
rescue
puts "Well you messed that up, didn't you?"
raise ArgumentError.new("could not call upcase! on #{string.inspect}")
end
string
end
After all this is arguably a pretty good use case for an exception.
It seems part of the confusion is that a Proc or lambda for that matter are distinctly different than a closure (block).
Even if you could convert Method#to_proc to a standard Proc e.g. Proc.new this would simply result in a LocalJumpError because the return would be invalid in this context.
You can use next to break out of a standard Proc but the result would be identical to the lambda that you have now.
The reason Method#to_proc returns a lambda is because a lambda is far more representative of a method call than a standard Proc
For Example:
def foo(string)
string
end
bar = ->(string) { string } #lambda
baz = Proc.new {|string| string }
foo
#=> ArgumentError: wrong number of arguments (given 0, expected 1)
bar.()
#=> ArgumentError: wrong number of arguments (given 0, expected 1)
baz.()
#=> nil
Since you are converting a method to a proc object I am not sure why you would also want the behavior to change as this could cause ambiguity and confusion. Please note that for this reason you can not go in the other direction either e.g. lambda(&baz) does not result in a lambda either as metioned Here.
Now that we have explained all of this and why it shouldn't really be done, it is time to remember that nothing is impossible in ruby so this would technically work:
def foo(string)
# place assignment in the guard clause
# because the empty return will result in `nil` a falsey value
return unless processed = method(:breaker).to_proc.call(string)
puts "This step should not be executed in the event of an error"
processed
end
def breaker(string)
begin
string.upcase!
rescue
puts "Well you messed that up, didn't you?"
return
end
string
end
Example
Lets say we have two classes:
class Test
end
class Test2
def a
end
end
and say we have a variable var which can point to an object of these class or a Hash, so:
var = Test.new
or
var = Test1.new
or
var = {"a" => 1}
Now we need to ensure that we call 'a' on 'var' only if its allowed (i.e. it should not be called in case it points to Test).
So, if I do:
var.a
it gives an error in case var points to Test object. Is there any way we can prevent the error? (Any other solution apart from doing .is_a? test ?)
You could use respond_to? to check if you can call the method on the object:
var.a if var.respond_to? :a
Strange questions lead to nasty answers. But at least it's working:
def a(test)
(test.respond_to?(:a) ? test.a : nil) ||
(test.respond_to?('has_key?') && test.has_key?('a') ? test['a'] : nil)
end
You can rescue from error, and return a default value, something like this
result = var.a rescue ""
Here blank string is used as default for illustration purposes, you can pick a reasonable default for your case
In my Rails app I have this (rather silly) method:
def my_method(param)
foo = "hey"
bar = "ho"
if param == :foo
return foo
elsif param == :bar
return bar
end
end
I don't like the if/else block, though.
Is there a simpler way to return the value of the local variable foo if :foo is provided as a parameter?
Or will I have to use an array or a hash here?
If you're using the very latest Ruby, you can use binding.local_variable_get(param). A hash seems cleaner to me, but your mileage may vary.
This should look simpler, don't think introducing a new data structure is required:
def my_method(param)
return 'hey' if param == :foo
return 'ho' if param == :bar
end
You can use a Hash:
def my_method(param)
objs = {
foo: "hey",
bar: "ho"
}
objs[param]
end
This is really a good time to use a case statement:
def my_method(param)
case param
when :foo
'hey'
when :bar
'ho'
else
# what do you want to do here?
end
end
Something to consider is, you're using an if/elseif, but what happens if neither of those hit? Do you want to return nil, or trap an error? As you look around in other people's code, you'll sometimes find long chains of if/elseif tests, with no final else, which opens up a potential logic error and can result in a hard-to-find bug.
My goal is to replace methods in the String class with other methods that do additional work (this is for a research project). This works for many methods by writing code in the String class similar to
alias_method :center_OLD, :center
def center(args*)
r = self.send(*([:center_OLD] + args))
#do some work here
#return something
end
For some methods, I need to handle a Proc as well, which is no problem. However, for the scan method, invoking it has the side effect of setting special global variables from the regular expression match. As documented, these variables are local to the thread and the method.
Unfortunately, some Rails code makes calls to scan which makes use of the $& variable. That variable gets set inside my version of the scan method, but because it's local, it doesn't make it back to the original caller which uses the variable.
Does anyone know a way to work around this? Please let me know if the problem needs clarification.
If it helps at all, all the uses I've seen so far of the $& variable are inside a Proc passed to the scan function, so I can get the binding for that Proc. However, the user doesn't seem to be able to change $& at all, so I don't know how that will help much.
Current Code
class String
alias_method :scan_OLD, :scan
def scan(*args, &b)
begin
sargs = [:scan_OLD] + args
if b.class == Proc
r = self.send(*sargs, &b)
else
r = self.send(*sargs)
end
r
rescue => error
puts error.backtrace.join("\n")
end
end
end
Of course I'll do more things before returning r, but this even is problematic -- so for simplicity we'll stick with this. As a test case, consider:
"hello world".scan(/l./) { |x| puts x }
This works fine both with and without my version of scan. With the "vanilla" String class this produces the same thing as
"hello world".scan(/l./) { puts $&; }
Namely, it prints "ll" and "ld" and returns "hello world". With the modified string class it prints two blank lines (since $& was nil) and then returns "hello world". I'll be happy if we can get that working!
You cannot set $&, because it is derived from $~, the last MatchData.
However, $~ can be set and that actually does what you want.
The trick is to set it in the block binding.
The code is inspired by the old Ruby implementation of Pathname.
(The new code is in C and does not need to care about Ruby frame-local variables)
class String
alias_method :scan_OLD, :scan
def scan(*args, &block)
sargs = [:scan_OLD] + args
if block
self.send(*sargs) do |*bargs|
Thread.current[:string_scan_matchdata] = $~
eval("$~ = Thread.current[:string_scan_matchdata]", block.binding)
yield(*bargs)
end
else
self.send(*sargs)
end
end
end
The saving of the thread-local (well, actually fiber-local) variable seems unnecessary since it is only used to pass the value and the thread never reads any other value than the last one set. It probably is there to restore the original value (most likely nil, because the variable did not exist).
One way to avoid thread-locals at all is to create a setter of $~ as a lambda (but it does create a lambda for each call):
self.send(*sargs) do |*bargs|
eval("lambda { |m| $~ = m }", block.binding).call($~)
yield(*bargs)
end
With any of these, your example works!
I wrote simple code simulating the problem:
"hello world".scan(/l./) { |x| puts x }
"hello world".scan(/l./) { puts $&; }
class String
alias_method :origin_scan, :scan
def scan *args, &b
args.unshift :origin_scan
#mutex ||= Mutex.new
begin
self.send *args do |a|
break if !block_given?
#mutex.synchronize do
p $&
case b.arity
when 0
b.call
when 1
b.call a
end
end
end
rescue => error
p error, error.backtrace.join("\n")
end
end
end
"hello world".scan(/l./) { |x| puts x }
"hello world".scan(/l./) { puts $& }
And found the following. The change of containment of the variable $& became inside a :call function, i.e. on 3-rd step before :call $& contains a valid value, but inside the block it becomes the invalid. I guess this become due to the singularity stack and variable restoration during the change process/thread context, because, probably, :call function can't access the :scan local state.
I see two variants: the first is to avoid to use global variables in the specific function redefinitions, and second, may to dig sources of ruby more deeply.
I am developing a Ruby on Rails app. My question is more about Ruby syntax.
I have a model class with a class method self.check:
class Cars < ActiveRecord::Base
...
def self.check(name)
self.all.each do |car|
#if result is true, break out from the each block, and return the car how to...
result = SOME_CONDITION_MEET?(car) #not related with database
end
puts "outside the each block."
end
end
I would like to stop/break out from the each block once the result is true (that's break the each block if car.name is the same as the name parameter once) AND return the car which cause the true result. How to break out in Ruby code?
You can break with the break keyword. For example
[1,2,3].each do |i|
puts i
break
end
will output 1. Or if you want to directly return the value, use return.
Since you updated the question, here the code:
class Car < ActiveRecord::Base
# …
def self.check(name)
self.all.each do |car|
return car if some_condition_met?(car)
end
puts "outside the each block."
end
end
Though you can also use Array#detect or Array#any? for that purpose.
I provide a bad sample code. I am not directly find or check something
from database. I just need a way to break out from the "each" block if
some condition meets once and return that 'car' which cause the true
result.
Then what you need is:
def check(cars, car_name)
cars.detect { |car| car.name == car_name }
end
If you wanted just to know if there was any car with that name then you'd use Enumerable#any?. As a rule of thumb, use Enumerable#each only to do side effects, not perform logic.
you can use include? method.
def self.check(name)
cars.include? name
end
include? returns true if name is present in the cars array else it returns false.
You can use break but what your are trying to do could be done much easier, like this:
def self.check(name)
return false if self.find_by_name(name).nil?
return true
end
This uses the database. You are trying to use Ruby at a place the database can deal with it better.
You can also use break conditional:
break if (car.name == name)
I had to do this exact same thing and I was drawing a blank. So despite this being a very old question, here's my answer:
Note: This answer assumes you don't want to return the item as it exists within the array, but instead do some processing on the item and return the result of that instead. That's how I originally read the question, I realise now that was incorrect - though this approach can be easily modified for that effect (break item insead of break output)
Since returning from blocks is dodgy (nobody likes it, and I think the rules are about to change which makes it even more fraught) this is a much nicer option:
collection.inject(nil) do |_acc, item|
output = expensive_operation(item)
break output if output
end
Note that there are lots of variants; for example, if you don't want an incidental variable, and don't mind starting a second loop in some circumstances, you can invert it like this:
collection.inject(nil) do |acc, item|
break acc if acc
expensive_operation(item)
end