is there a way to shorten this line on Ruby?
if (res = bla_permission_invalid).is_a? String then return res end
on
def something # many things that like this
if (res = bla_permission_invalid).is_a? String then return res end
# do something else
return true
end
when the content of bla_permission_invalid are something like
def bla_permission_invalid
return invalid_address_report_func if invalid_address?
return permission_error_report_func if #user.not_one_of? [ :group1, :group2 ]
return nil
end
invalid_adress_report_func and permission_error_report_func returns string
If possible values are String and NilClass, then the code can be simplified to this:
def something
res = bla_permission_invalid()
return res if res # strings are truthy, so they'll be returned but nil will proceed
# do something else
true
end
def something
bla_permission_invalid || (
# do something else
true)
end
For fun, one could rewrite your something method like this:
def something
true.tap do
bla_permission_invalid.tap { |res| return res if res.is_a? String }
# do something else (thx Sergio)
end
end
But more importantly, Mark Thomas deserves credit for his observation, that the problem at hand should be solved by using custom exceptions.
Error code approach is good in languages that don't have exceptions. Ruby has them.
Mark Thomas already noted in his comment that it looks like you're trying to handle errors on your own using some kind of string identifier. You could use exceptions instead:
class AddressError < StandardError; end
class PermissionError < StandardError; end
def something
bla_permission_invalid
# do something
true
end
def bla_permission_invalid
raise AddressError if invalid_address?
raise PermissionError if #user.not_one_of? [ :group1, :group2 ]
end
In the above code something calls bla_permission_invalid, performs its work and returns true. If an exception is raised in bla_permission_invalid, it automatically propagates up the call stack, you don't have to explicitly return it from something.
To handle the exception:
begin
something
rescue AddressError
# handle address error
rescue PermissionError
# handle permission error
end
Related
I just started learning ruby, but there's something I noticed.
In JavaScript the else block isn't really needed. So you can do something like
if (condition){
// do something
return
}
//do something else
return
instead of
if (condition){
// do something
return
} else {
//do something else
return
}
is this possible in Ruby ?
Yes, it is possible to use if without else in Ruby.
Here are some examples
class Demo
def example1
if condition
do_something
end
continue
end
def example2
do_something if condition # as modifier
continue
end
def example3
do_something unless condition # syntactic sugar for if !(condition)
continue
end
end
For a full tutorial please have a look here.
Please keep in mind, that example1 is (sometimes) concidered a bad coding style by the Ruby Style Guide.
When coding in Ruby, I frequently write something like this
def someopp(param)
r_val = nil
begin
r_val = do_something(param)
rescue Some::Error => e
r_val = {}
end
r_val
end
I don't like the looks of it. Declaring the return variable that doesn't really do anything, then a line again with just the var name in order to return it. It's cumbersome. I feel that, in Ruby, there must be way to make it more clean and pretty. Any suggestion for syntactic sugar over here?
def someopp(param)
do_something(param)
rescue Some::Error
{}
end
or
def someopp(param)
do_something(param) rescue {}
end
But not recomend to use inline-rescue!
If you're really doing this exact thing very frequently (calling external dependencies that can raise exceptions and falling back to some default value), can generalize further:
def rescuing(*exceptions, with:)
yield
rescue *exceptions
with
end
value = rescuing(ZeroDivisionError, with: Float::INFINITY) { 3 / 0 }
# => Infinity
Though it would be better still if you could avoid the exceptions to begin with, if possible.
Can anyone help me to figure out the the use of yield and return in Ruby. I'm a Ruby beginner, so simple examples are highly appreciated.
Thank you in advance!
The return statement works the same way that it works on other similar programming languages, it just returns from the method it is used on.
You can skip the call to return, since all methods in ruby always return the last statement. So you might find method like this:
def method
"hey there"
end
That's actually the same as doing something like:
def method
return "hey there"
end
The yield on the other hand, excecutes the block given as a parameter to the method. So you can have a method like this:
def method
puts "do somthing..."
yield
end
And then use it like this:
method do
puts "doing something"
end
The result of that, would be printing on screen the following 2 lines:
"do somthing..."
"doing something"
Hope that clears it up a bit. For more info on blocks, you can check out this link.
yield is used to call the block associated with the method. You do this by placing the block (basically just code in curly braces) after the method and its parameters, like so:
[1, 2, 3].each {|elem| puts elem}
return exits from the current method, and uses its "argument" as the return value, like so:
def hello
return :hello if some_test
puts "If it some_test returns false, then this message will be printed."
end
But note that you don't have to use the return keyword in any methods; Ruby will return the last statement evaluated if it encounters no returns. Thus these two are equivelent:
def explicit_return
# ...
return true
end
def implicit_return
# ...
true
end
Here's an example for yield:
# A simple iterator that operates on an array
def each_in(ary)
i = 0
until i >= ary.size
# Calls the block associated with this method and sends the arguments as block parameters.
# Automatically raises LocalJumpError if there is no block, so to make it safe, you can use block_given?
yield(ary[i])
i += 1
end
end
# Reverses an array
result = [] # This block is "tied" to the method
# | | |
# v v v
each_in([:duck, :duck, :duck, :GOOSE]) {|elem| result.insert(0, elem)}
result # => [:GOOSE, :duck, :duck, :duck]
And an example for return, which I will use to implement a method to see if a number is happy:
class Numeric
# Not the real meat of the program
def sum_of_squares
(to_s.split("").collect {|s| s.to_i ** 2}).inject(0) {|sum, i| sum + i}
end
def happy?(cache=[])
# If the number reaches 1, then it is happy.
return true if self == 1
# Can't be happy because we're starting to loop
return false if cache.include?(self)
# Ask the next number if it's happy, with self added to the list of seen numbers
# You don't actually need the return (it works without it); I just add it for symmetry
return sum_of_squares.happy?(cache << self)
end
end
24.happy? # => false
19.happy? # => true
2.happy? # => false
1.happy? # => true
# ... and so on ...
Hope this helps! :)
def cool
return yield
end
p cool {"yes!"}
The yield keyword instructs Ruby to execute the code in the block. In this example, the block returns the string "yes!". An explicit return statement was used in the cool() method, but this could have been implicit as well.
I am using StringScanner for lexical analysis like this :
def next
#scanner.skip(/\s+/)
value,kind=nil,nil
TOKEN_DEF.each{|tok,regex| (kind=tok;break) if #scanner.scan(regex)}
return Token.new(kind,value,#line,#scanner.pos)
end
At first approximation, this works well, except that I can't figure out how to now get the #line number.
I have read the doc, where begin_of_line? method seems appropriate, but I cannot figure how to use it.
Keep the text that you are scanning in a variable and use 'count'
I use the following in my code:
def current_line_number; #text[0..#scanner.pos].count("\n") + 1; end
This code doesn't seem ready to go and for sure somewhere else more elegant solution, it just should give you something to think about.
class Retry < StandardError
end
class TextScanner
def initialize(filename)
#lines = IO.readlines(filename)
#fiber = Fiber.new do
#lines.each_with_index do |line, index|
#scanner = StringScanner.new(line)
#scanner.skip(/\s+/)
value, kind = nil, nil
begin
got_token = false
TOKEN_DEF.each do |tok, regex|
if #scanner.scan(regex)
Fiber.yield Token.new(tok, value, index, #scanner.pos)
got_token = true
end
end
raise Retry if got_token
rescue Retry
retry
end
end
"fiber is finished"
end
end
def next
#fiber.resume
end
end
text_scanner = TextScanner('sometextfile')
puts text_scanner.next #=> first token
puts text_scanner.next #=> second token
puts text_scanner.next #=> third token
...
puts text_scanner.next #=> "fiber is finished"
I think I have a simple solution. Here it is :
def next
#line+=1 while #scanner.skip(/\n/)
#line+=1 if #scanner.bol?
#scanner.skip(/\s+/)
#line+=1 if #scanner.bol?
#scanner.skip(/\s+/)
return :eof if #scanner.eos?
TOKEN_DEF.each { |tok,syntax| (kind=tok;break) if #scanner.scan(syntax)}
return Token.new(kind,nil,#line,#scanner.pos)
end
I have the following method and want to make it more readable:
def value_format(value)
if value.respond_to? :to_actor
value.to_actor
elsif value.respond_to? :to_subject
value.to_subject
elsif value.respond_to? :to_json
value.to_json
elsif value.respond_to? :to_hash
value.to_hash
else
value.inspect
end
end
This is my solution. What do you think?
def value_format(value)
methods = [:to_actor, :to_subject, :to_json, :to_hash, :inspect]
value.send(methods.find_all { |m| m if value.respond_to? m }.first)
end
Your solution looks fine, but you might as well use find instead of find_all:
METHODS = [:to_actor, :to_subject, :to_json, :to_hash, :inspect]
def value_format(value)
value.send(METHODS.find { |m| value.respond_to? m })
end
Using a constant has the advantage of not creating a new array every time value_format is ran.
Seems there's a pretty simple optimization to your solution:
def value_format(value)
methods = [:to_actor, :to_subject, :to_json, :to_hash]
value.send(methods.find(:inspect) { |m| value.respond_to? m })
end
The facets gem provides an elegant solution (I think) to this problem. It combines the two steps of checking if an object responds to a method and actually calling that method into a single step.
So your example could be rewritten as this:
require 'facets/kernel/respond'
def value_format(v)
v.respond.to_actor || v.respond.to_subject || v.respond.to_json || v.respond.to_hash || v.respond.inspect
end
Note that this method only works if it is safe to assume that none of these methods are going to return nil or false (because respond returns nil if the object doesn't respond, that is what allows us to chain it together with a bunch of ors).
Since all of the methods you listed should return strings, I believe this approach would work fine in your example.
Documentation:
# Like #respond_to? but returns the result of the call
# if it does indeed respond.
#
# class RespondExample
# def f; "f"; end
# end
#
# x = RespondExample.new
# x.respond(:f) #=> "f"
# x.respond(:g) #=> nil
#
# or
#
# x.respond.f #=> "f"
# x.respond.g #=> nil